From: Davide Franco Date: Wed, 1 Dec 2010 12:15:04 +0000 (+0100) Subject: bacula-web: Upgraded phplot to version 5.2.0 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=4db6f8ad9d03e152ab344dcaeb3c5e6d333a36df;p=bacula%2Fbacula bacula-web: Upgraded phplot to version 5.2.0 --- diff --git a/gui/bacula-web/external_packages/phplot/COPYING b/gui/bacula-web/external_packages/phplot/COPYING new file mode 100644 index 0000000000..602bfc9463 --- /dev/null +++ b/gui/bacula-web/external_packages/phplot/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/gui/bacula-web/external_packages/phplot/ChangeLog b/gui/bacula-web/external_packages/phplot/ChangeLog index b1c6d91afb..ace1b3bfc7 100644 --- a/gui/bacula-web/external_packages/phplot/ChangeLog +++ b/gui/bacula-web/external_packages/phplot/ChangeLog @@ -1,1610 +1,1245 @@ -2004-10-24 18:40 migueldb +This is the Change Log for PHPlot. +The project home page is http://sourceforge.net/projects/phplot/ +----------------------------------------------------------------------------- + +2010-10-03 (lbayuk) ===== Released as 5.2.0 ===== + * phplot.php: Updated version + * README.txt: Updated for new release + * NEWS.txt: Add text for new release + * Makefile: Removed HorizontalBars.txt from release. This is now all + documented in the reference manual. + +2010-10-01 + * Rewrite color handling. Default and specified colors are now validated, + converted to RGBA arrays, and saved. The colors indexes are not + allocated until graph drawing time. Unneeded colors are not allocated. + + Fix bug 3045131 "SetTransparentColor problems": Transparency setup is + deferred to graph drawing time, after most other colors are allocated. + Fixes SetTransparentColor order dependency, and works with data colors. + + Fix bug 3049726 "Optimize color allocation": Colors allocation is + deferred to graph drawing time, and colors for most elements are only + allocated if needed. In particular, the data colors and dark colors, + which used 32 slots, now only get defined as needed. + + Feature request (partial) 3077554 "Finer control over plot element + colors": The colors of the main, X, and Y titles can now be controlled + separately. + + Change details: + New internal functions: + GetColorIndex(), GetDarkColorIndex() + Allocate pre-parsed colors for elements. + SetColorIndexes() + Does the deferred color allocation and defaults. + NeedDataDarkColors(), NeedErrorBarColors() + Called by graph drawing functions if those colors are needed. + GetDataColor(), GetDataErrorColors(), GetBarColors() + Custom or standard data color assignment for most plot types. + truncate_array() + Shorten an array. Used to limit data color allocation. + + Removed internal functions: + SetIndexColor(), SetIndexDarkColor() + Unneeded. Color handling is now in two separate phases. + + Removed internal member variable: + data_colors_alpha + PHPlot no longer calls SetDataColors before plotting, so this + variable is not needed to preserve the default alpha. + + New internal member variables: + transparent_color + Saves color value from SetTransparentColor. For bug 3045131. + x_title_color, y_title_color, ndx_x_title_color, ndx_y_title_color + For title colors. DrawXTitle and DrawYTitle use them now. + + Changed internal member variables: + i_border, plot_bg_color, bg_color, label_color, text_color, etc. + All variables for colors now always hold a parsed 4-component + (RGBA) color specification as an array, and are no longer + statically initialized to a color name. + + New public functions: + SetXTitleColor(), SetYTitleColor() + These can be used to make the 3 titles have different colors. + +2010-09-26 + * Don't let SetXDataLabelPos() accept 'xaxis' or 'all' as valid. + Don't let SetXTickLabelPos(), SetYTickLabelPos() accept 'all' as valid. + These were never implemented and never documented. + + + * Feature request #3073679 "Stacked bar plots with negative values": + Allow stacked bar plots (vertical and horizontal) to work with negative + values. Changed FindDataLimits() to not take abs() values for + stackedbar when calculating the data range. Changed DrawStackedBars() + and DrawHorizStackedBars() to handle negative values, including proper + label positions. The first non-zero value in a row sets the direction + of the bar. Mixing positive and negative values within a row works but + the results are not useful. + + * New internal DrawBar() moves common code from 4 bars and stackedbars + drawing functions and makes it easier to deal with shading vs bar + direction. + +2010-09-24 + * Fix bug 3074402 "Fix Y axis default for horizontal plots": + Make the default axis positions symmetrical for vertical plots + (X and Y) and horizontal plots (Y and X). This fixes the problem + with horizontal bar/thinbarline plots that have negative data, + where the Y axis stayed on the left and the bars went rightward. + +2010-09-01 + * Fix bug 3056991 "Internal methods should be 'protected'": + Changed CheckDataArray(), number_format(), FindDataLimits(), and + DrawBackground() to be 'protected'. The test suite no longer calls + these directly. For SetIndexColor(), SetRGBColor(), DrawText(), + SizeText(), xtr(), and ytr(), left these as public with a comment + indicating they should be treated as protected. + + * Fix bug 3057000 "Review 'deprecated' methods": + + Changed deprecated method SetNewPlotAreaPixels() to just call + SetPlotAreaPixels(). It was suspicious as coded, and was found + to be equivalent to SetPlotAreaPixels(). + + Removed SetColor(), which didn't do anything. It was calling + SetRGBColor and discarding the result. After reviewing the + history of this in all CVS versions, it does not seem to have + ever been coded correctly, so there is no harm in removing it. + + All other deprecated methods seem OK and are left as is. + +2010-08-30 (lbayuk) ===== Released as 5.1.3 ===== + * phplot.php: Updated version + * README.txt: Updated for new release + * NEWS.txt: Add text for new release + +2010-08-27 + * Fix bug 3051832 "Let PHP/GD find the font file": + Instead of checking for file existence, PHPlot now uses a non-drawing GD + operation to validate a TrueType font in SetFontTTF(). This allows GD to + use its internal search path, making it likely that fonts can be found + without needing a platform-dependent path in your script. Full paths + will still work, so there is no compatibility issue. + + * Fix bug 3051906 "Better choice for default TT font": + Removed "benjamingothic.ttf" as the default TT font setting. This has + not been included in PHPlot since 2004. Changed SetFontTTF() to call + new GetDefaultFontTTF(), which will try a number of sans-serif font + names the first time it needs a default TT font. Considering the above + fix to finding fonts, this has a good chance of finding a working + font. It is known to work on Windows and some Linux distributions. + +2010-08-19 + * Makefile: Removed phplot_data.php from list of files to release. + Reference bug report 3048267. This file has not been maintained or + tested, and has bugs. It will remain in CVS, but not be included + in PHPlot releases. + +2010-08-17 + * Change new DrawLinePoints(). It does not have to check and + handle error bar plots, as DrawDots and DrawLines will do that. + +2010-08-16 + * Rewrote DecodeDataType(). Previous implementation was hard to + extend and inefficient. Now it uses new class variables (not a + returned array) and is only called once, by DrawGraph. Changed all + users of data_type to use the new variables. + + In CheckDataArray(), calculate data_columns properly for + text-data-single data type (pie chart) too. Simplify DrawPie + to use this, and merge 2 of the 3 cases. + + Have a single function handle each plot type, so it can properly + check the data type and report a correct error message showing + all supported types. For example, DrawBars is now the entry point + for both bars and horizontal bars; DrawGraph does not directly + call DrawHorizBars. Similar for DrawStackedBars and + DrawHorizStackedBars. Lines, Points, and Linepoints also now + have a single function each, dispatching to others as needed. + (These changes were split off from an upcoming, larger change.) + +2010-08-14 + * Fixes to SetDataValues: need to clear out the 2 arrays before + setting values in a loop, else if the function is used more than + once then old values will remain. + Move records_per_group calculation outside the loop. + (These were split off from upcoming, larger changes.) + + * Part 4 of horizontal plots: implement horizontal thinbarline plots. + Added new data type 'data-data-yx' (label, Y, X1, X2, ...). + DrawThinBarLines() now draws either vertical or horizontal plots, and + supports data types text-data, data-data, text-data-yx, data-data-yx. + + Fixed DecodeDataType() to handle text-data-single too, for completeness. + + * Fix for over-padding style and color arrays. These were padded to + records_per_group, but that is the maximum data array row size. + The number of actual data sets is less, and depends on the data type. + Calculate the correct number as data_columns at the top of DrawGraph, + and use that to pad the arrays. Also moved early error checking in + DrawGraph() into new CheckDataArray(). + +2010-08-09 + * Code cleanup. This is a large change to unify the comment and + coding stye used in PHPlot. The PEAR style guide is being used + as a reference, but PHPlot will never be 100% compliant. + This patch introduces no functional changes at all. + - Limit line length to 110. + - Remove obsolete, partially implemented Doxygen comments. + - Add descriptive comment above functions where missing. + - Use consistent comment markers and control structure spacing. + - Remove ctags-confusing end of function comments. + - Rewrote a few if/else blocks for cleaner logic. + - Re-order some functions for consistency (X, then Y). + +2010-08-04 + * Implemented horizontal stacked bar plots: + Use the same data type introduced for horizontal bars, 'text-data-yx', + and the same plot type 'stackedbars', to make a horizontal stacked bar + plot. Data value labels, both at the ends of the bars and within the + bars ('plotstack' labels) are available with horizontal stacked bars. + + * Internal function DrawDataLabel() is replaced by DrawDataValueLabel(), + with different usage. The new function can also check to see if a label + fits in the allocated space. + + * Fixed the text size check in stacked bar plots for data value labels + within the bars. The check only worked correctly for 0 degree labels. It + now works with any text angle. It suppresses the label if it is too high + (for vertical stacked bar plots) or too wide (for horizontal stacked bar + plots) to fit in the bar segment to which it belongs. Note that it only + checks in the bar direction. If the text is too wide (for vertical bars), + or too high (for horizontal bars), it will still be plotted, but will + cross the sides of the bar. + +2010-07-28 + * Allow callbacks to return a value (to support new data_color callback). + * Feature request 3034164 "Extended control of data colors": + Define new callback 'data_color' for picking the data color. + Changed internal plot drawing functions DrawDots, DrawLines, DrawSquared, + DrawBars, DrawStackedBars, DrawDotsError, DrawThinBarLines, + DrawLinesError, and DrawHorizBars to use the data_color callback + (if defined) to select the data colors. + * SetRGBArray code cleanup (no functional changes). + +2010-07-27 + * Fixes for error bars: + Code cleanup in DrawYErrorBar(), with no functional change. + Suppress duplicate drawing of error bars with 'linepoints' error plots. + This was already fixed for data labels. Now error bars will only be + drawn by the 'points' part, not the 'lines' part. There should be no + visible changes to plots. (This is needed for a future change). + +2010-07-26 + * Horizontal bar charts (still an experimental feature) can now have data + value labels. + * HorizontalBars.txt: Fix incorrect description of bar direction. Add + text explaining the new data value labels. + +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 FormatLabel() 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. - * phplot.php: +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. - + 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-10-24 17:44 migueldb - - * README.txt, doc/quickstart.html, examples/create_chart.php, - examples/format_chart.php, examples/inline_image.php: - - + Updated to the latest changes in phplot - -2004-10-24 17:40 migueldb - - * doc/index.php: - - + Minimal change - -2004-09-09 20:27 migueldb - - * phplot.php: - - + 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 14:19 migueldb - - * phplot.php: - - + SetXTickLabelPos() and others: more on the bug reported by Jo - Demol. - -2004-06-14 11:35 migueldb - - * phplot.php: - - + Fixed bug reported by Jo Demol. - -2004-05-11 14:14 migueldb - - * phplot.php: - - + SetBgImage(): added. - - + SetPlotAreaBgImage(): added. - - + SetInputFile(): deprecated. - - + DrawBackground(): now accepts images as backgrounds. - - + DrawPlotAreaBackground(): now accepts images as backgrounds. - - + tile_img(): internal method added. - -2004-04-14 13:26 migueldb - - * phplot.php: - - + DrawXAxis(): No more horizontal tick nor label at X-axis' - sides. - -2004-03-21 18:01 migueldb - - * phplot.php: - - + x/y_label_type automaticaally set to 'data' when setting label - precision. - - + minor corrections. - -2004-03-03 08:17 afan - - * phplot.php: PlotAreaWorld - last fix - -2004-03-03 02:40 migueldb - - * phplot.php: - - + SetPlotAreaWorld(): fixed. - -2004-03-01 21:14 afan - - * phplot.php: Needed one more = in ($ymin === NULL) - -2004-02-29 11:21 afan - - * phplot.php: phplot.php SetPlotAreaWorld - changed ($!ymin) to - ($ymin == NULL) for cases where $ymin = 0. (also done for ymax, - xmin, xmax) afan - -2004-02-23 10:34 migueldb - - * phplot.php: - - + SetPlotAreaWorld(): Fixed the calculation of max and min y. - -2004-02-14 12:29 migueldb - - * phplot.php: - - + SetPlotAreaWorld(): Fine grained control over which values are - auto-calculated. Should fix some trouble with y-scaling. - - + DrawGraph(): yet another fix to drawing order... - - + CheckOption(): fixes problems with mixedcase arguments. - - + SetFileFormat(), FormatLabel(): typos fixed. - -2004-01-30 12:56 migueldb - - * examples/data_sample1.php: - - + Added "stacked bars" plot type. - -2004-01-30 12:50 migueldb - - * phplot.php: - - + DrawStackedBars(): added plot type. - - + SetEqualXCoord(): renamed to more intuitive CalcBarWidths(). - Modified for stacked bars. - - + Changed graphing order. The grids are again at the background, - as they should. Added var $grid_at_foreground (bool) to alter - this. - - + text-data-pie renamed to text-data-single. - - + SetPlotAreaWorld(): fixed min_y bug. - -2004-01-29 17:10 migueldb - - * phplot.php: - - + SetPlotAreaWorld(): default behaviour is to adjust Y axis to Y - min/max. - - + SetSkipLeftTick(), SetSkipRightTick(): added. - - + SetFileFormat(): fixed silly bug that prevented selections from - being made. - -2004-01-28 20:00 migueldb - - * phplot.php, doc/quickstart.html: - - + Merged final changes to rel-5-0 into main trunk. - -2004-01-28 19:12 migueldb - - * phplot.php: - - + DrawGraph(): Fixed graph drawing order for default plots - (bars). Removed some redundat lines of code. - -2004-01-28 18:58 migueldb - - * doc/quickstart.html: - - + More stuff... - -2004-01-28 18:49 migueldb - - * phplot.php: - - + DrawPieChart(): fixed use of old variable. - - + First steps for data labels autoadjustement and skipping. - -2004-01-27 12:12 migueldb - - * phplot.php: - - + Added missing SetSkipTopTick() - - + Some comments. - - + Fixed option checking for a couple of functions. Added some - more. - -2004-01-27 00:03 migueldb - - * doc/index.php: [no log message] - -2004-01-25 20:28 migueldb - - * doc/index.php: - - + Fixed typo. - -2004-01-25 19:57 migueldb - - * doc/index.php: - - + Commit for 5.0rc1. - -2004-01-25 19:50 migueldb - - * README.txt, doc/index.php, doc/quickstart.html, doc/schema.html: - - + Commit for 5.0rc1. - -2004-01-25 19:44 migueldb - - * phplot.php: - - + Added missing SetDrawXDataLabelLines() and - SetDrawYDataLabelLines(). - - + Added some parameter checking with CheckOption(). - - + Added some comments. - -2004-01-25 19:32 migueldb - - * examples/: create_chart.php, example3.php, format_chart.php: - - + Commiting for 5.0rc1 - -2004-01-25 18:21 migueldb - - * examples/example8.php: [no log message] - -2004-01-25 18:20 migueldb - - * doc/style.css: - - + Added 'box' style. - -2004-01-25 17:11 migueldb - - * phplot.php: - - + Set*Colors(): should be faster when using default values. - - + array_pad_array() non class-member function added. - - + PadArrays(): now pads arrays with themselves, to mimic previous - behaviour, uses array_pad_array() - - + DrawAxisLegend(): skeleton added. To do. - -2004-01-24 23:18 migueldb - - * phplot_data.php: - - + DoMovingAverage(): some corrections. The legend isn't correctly - updated yet. - -2004-01-24 23:16 migueldb - - * phplot.php: - - + PadArrays(): added. Formerly in SetDataValues(), now called - from DrawGraph(). Fixes SetLineWidths() buggy behaviour. - - + Added MINY and MAXY constants, for DrawXDataLine(). - - + DrawGraph(): changed order when drawing axis, to avoid - overwriting. - -2004-01-21 20:08 migueldb - - * doc/quickstart.html: - - + More corrections - - + Some links - - + $Id$ tag - -2004-01-21 19:55 migueldb - - * doc/quickstart.html: - - + HTML heavily cleaned - - + Some corrections - -2004-01-21 18:47 migueldb - - * doc/index.php: - - + Added quickstart. - - + More things in whishlist. - -2004-01-21 18:46 migueldb - - * doc/quickstart.html: - - + Doc by Afan. With slight corrections. - -2004-01-21 18:44 migueldb - - * examples/rgb.inc.php, doc/index.html: [no log message] - -2004-01-21 18:43 migueldb - - * phplot.php: - - + Small changes to functions' documentation. - -2004-01-21 18:06 migueldb - - * phplot_data.php: - - + renamed tedious data_values to data - - + work still in progress - -2004-01-21 18:03 migueldb - - * phplot.php: - - + Added check for __FUNCTION__ for old PHP versions. - - + DrawPieChart(): 'data-data' support added. - - + DrawGraph(): added check to avoid calling FindDataLimits() - twice. - - + DrawXDataLine(): Added. - - + DrawXDataLabels(): Added support for vertical lines to data - points via DrawXDataLine() - - + FindDataLimits(): Added calculation of per-row min_y and max_y, - for DrawDataLine() - -2003-12-30 13:26 migueldb - - * phplot.php: - - + SetDataValues(). Now reads a reference and copies it to - $this->data, a numeric array. All indices are converted to - numeric. num_recs[] holds the number of records per data row. - - + FindDataLimits(). Works with new $this->data - - + The following functions now work with the new data set and use - faster loops: - - + DrawDots() - - + DrawDotsError() - - + DrawThinBarLines() - - + DrawPieChart(). Minimum changes. - - + DrawLines() - - + DrawArea(). Other optimizations too. - - + DrawBars(). Other things too. - -2003-12-30 00:01 migueldb - - * phplot_data.php: - - + Fixed constructor. Added parameters to fit PHPlot()'s - - + Fixed many "undefined index" errors. - - + DoMovingAverage(): puts data in a new row, and sets colors and - legend. Won't work for the moment. - -2003-12-29 21:58 migueldb - - * phplot_data.php: - - + Formatted after phplot.php style (PEAR) - - + Added Doxygen comments. - - + DoScaleData(): Optimized with for loops. Needs testing. - - + DoMovingAverage(): Optimized. Changed behaviour with first - elements in dataset. Needs testing. - - + DoExponentialMovingAverage(): added. Needs testing. - -2003-12-27 14:53 migueldb - - * benjamingothic.ttf: - - + The TTFont is only needed in 'examples/'. - -2003-12-27 14:52 migueldb - - * phplot.php: - - + Removed some (done) TODO marks. - -2003-12-27 14:06 migueldb - - * examples/test_setup.php: [no log message] - -2003-12-27 14:06 migueldb - - * examples/nav.html: - - + This was unnecessary. - -2003-12-27 14:01 migueldb - - * README.txt, LICENSE.GPL, LICENSE.PHP_3_0: [no log message] - -2003-12-27 13:46 migueldb - - * doc/index.php: - - + Added all the examples. - - + Added 5.0 version number. - - + Added myself as author. - -2003-12-27 13:44 migueldb - - * examples/example9.php: - - + Changed to follow phplot function renaming (again!) - -2003-12-24 13:39 migueldb - - * examples/: create_chart.php, format_chart.php: - - + New data type: randfunction, using data-data-error. - -2003-12-24 13:38 migueldb - - * examples/: data_sample1.php, data_sample2.php, data_sample3.php, - data_sample4.php: - - + Moved data type selection links to format_chart.php - -2003-12-24 13:36 migueldb - - * phplot.php: - - + Fixed spurious ticks bug. - - + Corrected some comments. - -2003-12-24 12:52 migueldb - - * examples/example5.php: - - + Now in format_chart.php - -2003-12-24 12:47 migueldb - - * examples/example4.php: Ooops! I deleted the wrong file! - -2003-12-24 12:39 migueldb - - * examples/data_sample5.php: - - + New 'randfunction' data type in example-o-matic. Taken from - deleted example4.php and test1.php. - -2003-12-24 12:37 migueldb - - * examples/example4.php: - - + Inserted example in example-o-matic as 'randfunction' - -2003-12-24 12:35 migueldb - - * examples/test1.php: inserted example in example-o-matic as - 'randfunction' - -2003-12-24 10:38 migueldb - - * phplot.php: - - + Fixed silly typo. - -2003-12-23 17:40 migueldb - - * phplot.php: - - + All renaming with leading underscores undone for compatibility. - It was a mess. - - + Fixed a couple function call typos. - - + Fixed axis position calculation. - -2003-12-17 16:28 migueldb - - * examples/create_chart.php: - - + Fixed to work with phplot.php v1.69 - -2003-12-17 16:28 migueldb - - * phplot.php: - - + Added skip_left_tick and skip_right tick for x axis. - - + Added y_label_angle (last commit) - - + Fixed automatic axis positioning for plots with negative values - or log scales. - - + Deleted messy SetGridParams(), SetTickParams() and - SetDataLabelParams(). I realized they were a bad idea of mine... - :( - - + Some more code grouping and reorganisation. - -2003-12-15 16:52 migueldb - - * examples/: create_chart.php, format_chart.php: - - + Added x/y tick crossing lenghts. - - + Added x axis and y axis positioning. - - + Corrected some label placement options. - -2003-12-15 16:50 migueldb - - * examples/: example4.php, example5.php, example9.php, test1.php: - - + Updated examples to work with new PHPlot. - - + Fixed some things. - -2003-12-15 16:48 migueldb - - * phplot.php: - - + x_tick_pos: added support for 'xaxis' position. - - + x_tick_label_pos: added support for 'xaxis' position. - - + DrawYTick(): fixed 'yaxis' - - + Set[X|Y]TickCrossing(): added. Draw ticks crossing axis by a - specified length in pixels. - - + DrawText(): improved vertical centering for TTF. - - + SetDrawBrokenLines(): fixed silly thing. - - + _DrawPlotBorder(): new option 'right' and 'sides' - - + DrawLinesError(): removed some 'ifs' for speed. - - + DrawDotsError(): removed some 'ifs' for speed. - - + Minor corrections here and there. - -2003-12-13 04:58 migueldb - - * phplot.php: - - + DrawGraph(): Fixed drawing order error. - -2003-12-13 04:44 migueldb - - * phplot.php: - - + _SetIndexColors(): removed. When sessions were not set, indexes - were being calculated twice - - + Centralized color and style defaults in _SetDefaultStyles() - (former _SetDefaultColors()) - - + SetLineWidth(): superseded by new SetLineWidths(). Now using an - array of values for per-line setting. - - + DrawDashedLine(): added again for backward compatibility. - - + DrawBars(): Faster shading. - - + DrawYTicks(): slightly improved, DrawYTick() modified too. - - + DrawGraph(): plot borders now drawn after plots. - - + Added color and style variable declarations for easier - modification. - - + More formatting. Some renaming undone for backwards - compatibility. - -2003-12-10 04:04 migueldb - - * examples/: data_sample1.php, data_sample2.php: - - + New plot type 'squared' added. - -2003-12-10 04:03 migueldb - - * examples/: create_chart.php, format_chart.php: - - + New option 'Draw broken lines' added. - -2003-12-10 04:00 migueldb - - * phplot.php: - - + Better available graph room usage. - - + More renaming and formatting... - - + Parameter validation with _CheckOption(), but I might drop it - if it slows everything down. - - + DrawBinary() is again DrawSquared()... ooops. :) It is at least - now working. - - + DrawSquared() done, quite silly thing, though. - - + SetBrokenLines() added. Tells whether to draw lines for missing - Y data. - -2003-12-10 01:32 migueldb - - * doc/schema.html: - - + Simple [go to index] link. - - + Typos fixed. - -2003-12-10 00:56 migueldb - - * phplot.php: - - + Reworking PEAR coding standards conformance, I had quite - screwed it up. Vim regexes are proving veeeery useful ;) - - + Important comment about the destructor and class instantiation. - - + Internal methods will now have a prepended underscore. I'm - renaming them little by little. - - + SetDefaultDashedStyle(): now accepts any string as style. - - + DrawSquared() renamed to DrawBinary() - - + _CalcMargins(): corrected [x|y]_tick_label_width calculation. - -2003-12-07 18:58 migueldb - - * examples/: example6.php, example7.php: - - + SetDrawXDataLabels(false); - -2003-12-07 18:30 migueldb - - * phplot.php: - - + DrawDotsError(): doesn't fail anymore if data type isn't - data-data-error (might include text-data-error in the future). - - + More variables changed to bool. - - + SetNewPlotAreaPixels(): removed, SetPlotAreaPixels() now does - the same work. - - + SetMarginsPixels(): now updates margin variables. - - + SetTitle(): fixed wrong height calc for empty title. - - + Removed many internal variable declarations. See the beggining - of the class declaration for comments on this. This might be a - bit stupid... :-? - - + DrawSquared(): plot type on the works, addressing Feature - Request [558302]. Just the skeleton for now. - -2003-12-07 02:12 migueldb - - * phplot.php: - - + SetInputFile(): deallocates previously allocated 'img' - - + Added 'line_spacing' and SetLineSpacing() - - + InitImage(): deleted, moved into the constructor - - + DrawError(): shows plain text message if 'img' unavailable. - - + SetXTitle(): Fixed x_title_height calculation for TTF. - - + More code reorganisation. As I seem to be alone in this, that - should be no problem... - -2003-12-07 01:12 migueldb - - * phplot.php: - - + TTF font provided with package now works (it wasn't being - found). - - + TTF placement a bit adjusted. - - + SetTitle(): Fixed wrong title height calculation for TTFonts. - - + DrawLegend(): TTF support added. - - + DrawDot(): renamed dot type 'crosshair' to 'plus'. Added - 'cross' and 'trianglemid'. - - + FormatTickLabel(): now used for tick and data labels. Renamed - to FormatLabel(). - - + draw_x_data_label: variable removed, now using - [x/y]_data_label_pos for both x and y data labels. - - + draw_x_data_label_lines: parameter added. - - + line_width: minor usage fixes. - - + SetXDataLabelAngle() renamed to SetXLabelAngle(). Old function - kept in 'deprecated' section. - - + Changed some options to bool format. - - + Tried to make better decisions on Set*LabelParams() regarding - placement. - - + Removed superfluous DrawLabels() function. - - + Moved CalcXHeights() and CalcYWidths() into CalcMargins() for - speed (unperceptible I must admit) and clarity. - - + Other changes here and there... - -2003-12-07 01:06 migueldb - - * examples/data_sample2.php: - - + Added missing data. - -2003-12-07 01:05 migueldb - - * examples/: create_chart.php, data_sample1.php: [no log message] - -2003-12-07 01:04 migueldb - - * examples/format_chart.php: - - + Added a check to see if we are being called from the right - place. - - + New data label angle option. - - + New line and error bar line width options. - - + New data label options. - - + New point types. - - + Some corrections. - -2003-12-06 21:12 migueldb - - * examples/nav.html: [no log message] - -2003-12-06 21:09 migueldb - - * examples/test_setup.php: - - + Added nav. bar - -2003-12-06 21:08 migueldb - - * examples/nav.html: - - + Simple navigation bar, not very useful yet. - -2003-12-06 20:43 migueldb - - * doc/style.css: - - + New "nav" class. - -2003-12-06 20:42 migueldb - - * doc/index.php: - - + New index, now calls some php. - -2003-12-06 20:41 migueldb - - * doc/php_test.php: - - + Added the test for php functionality in index.php, so this file - is no longer necessary. - -2003-12-05 19:13 migueldb - - * examples/inline_image.php: - - + Added warning message when called on its own. - -2003-11-28 01:21 migueldb - - * phplot.php: - - + Default title position='none' for better positioning of - elements. - - + xtr(), yrt(): return value round()ed - - + DrawLines(): better (?) management of incomplete data sets. No - begin or end points forced. No more "undefined offset x in ..." - - + DrawArea(): more modifications when working with incomplete - data sets. No extra beginning or ending points. - -2003-11-26 17:52 migueldb - - * phplot.php: [no log message] - -2003-11-26 17:16 migueldb - - * examples/: create_chart.php, format_chart.php: - - + Added shading option - -2003-11-26 16:43 migueldb - - * phplot.php: - - + Added spaces after every comma, around every equal sign for - prettier code. :) - - + DrawXTitle(): Another fix. Removed the check for tick_pos I - just introduced. Works better now. - - + _PHPlot(): destructor added. - - + Comments, indenting, and a couple of minor corrections here and - there. - -2003-11-26 12:19 migueldb - - * phplot.php: l - - + DrawXTitle(): check for tick_label_pos when calculating title - position (before the title would be misplaced under certain - circumstances) - - + Added SetIndexDarkColor() and $ndx_data_dark_color[] calculated - from $data_colors[], for shadows. - - + New data type (one value per data row) 'text-data-once' for pie - charts - - + DrawPieChart(): some optimisations. Shading added. - -2003-11-25 17:30 migueldb - - * examples/create_chart.php: - - + Now accepts point size from format_chart.php. - -2003-11-25 17:29 migueldb - - * examples/format_chart.php: - - + New point type 'crosshair' added to the options. - - + Link to the documentation index at the bottom of the page. - -2003-11-25 17:28 migueldb - - * examples/data_sample1.php: - - + Fixed wrong comment. - -2003-11-25 17:28 migueldb - - * phplot.php: - - + DrawDot(): added new variables to avoid calls to ytr() and - xtr(), replaced imagefilledrectangle() with imageline() in - "halfline" and "line" modes. - - + DrawDot(): added point type 'crosshair' - - + DrawArea(): modified to follow the structure of the other plot - drawing methods. Removed obsolete DrawAreaSeries() - - + DrawLines(): same modifications as with other plot drawing - methods. - - + Removed dummy DrawLineSeries() - - + More use of imagesetthickness() - -2003-11-25 01:34 migueldb - - * phplot.php: Right now working on richer data label placement, but - in the meantime: - - + Removed unnecessary (and wrong) right alignment option in - FormatTickLabel() - - + Removed unused (thought it better :) DrawPlotLabel - - + DrawYErrorBar(): uses imagesetthickness() - - + DrawDots() optimised and cleaned. - - + DrawThinBarLines() optimised and cleaned. - - + DrawDotsError() optimised (I hope!) - - + DrawLinesError() optimised. Fixed a bug with multiple lines - - + Other small things, as usual... - -2003-11-25 01:27 migueldb - - * examples/data_sample2.php: - - + Removed bogus tag. - -2003-11-25 00:49 migueldb - - * examples/data_sample4.php: - - + Added 'thinbarline' data plot option. - -2003-11-25 00:48 migueldb - - * examples/data_sample1.php: - - + Added thinbarline plot mode. - - + Added comment on 'text-data' data type. - -2003-11-24 23:32 migueldb - - * examples/: data_sample1.php, data_sample2.php, data_sample4.php, - format_chart.php: - - + Replaced old data type "linear" with new "data" everywhere. - -2003-11-24 23:32 migueldb - - * examples/create_chart.php: - - + Accepts new $data_row format from data_sample3.php - (data-data-error format) - - + Replaced old data type "linear" with new "data". - -2003-11-24 23:30 migueldb - - * examples/data_sample3.php: - - + Replaced old "linear" with new "data" everywhere. - - + Modified code and $data_row organization to allow for easier - changes. - - + Added another set of values (to have two lines in the example) - -2003-11-24 21:06 migueldb - - * examples/data_sample3.php: - - + Removed bogus tag - - + Added slashes to input tags end - -2003-11-24 17:44 migueldb - - * phplot.php: - - + Fixed a typo. - -2003-11-24 17:03 migueldb - - * doc/: examples.html, function_reference.html, quick_start.php: - - + Inserted into index.html (some time ago) - -2003-11-24 17:00 migueldb - - * doc/style.css: - - + Added new classes for the docs. - -2003-11-24 16:37 migueldb - - * phplot.php: - - + Added SetDefaultDashedStyle() - - + Replaced outdated call to imagesetstyle() with SetDashedStyle() - - + Moved the check for dashed_grid from constructor (where it - could lead to strange behaviour) into DrawXTicks() and - DrawYticks() (BTW, shouldn't these be renamed?) - -2003-11-24 16:12 migueldb - - * examples/: data_sample1.php, data_sample2.php, data_sample3.php, - data_sample4.php: - - + HTML tags to lowercase. - - + Added missing closing tags. - - + Indented code. - -2003-11-24 16:05 migueldb - - * examples/create_chart.php: - - + Modified to work with the new format_chart.php - -2003-11-24 16:04 migueldb - - * examples/format_chart.php: - - + New layout - - + Many new options - - + Some introductory words - - + Removed "you have to reload" warning (the default behaviour for - PHPlot now is to send no-cache header) - - + Small corrections - -2003-11-24 14:46 migueldb - - * phplot.php: - - + Added checks for tick placement in CalcMargins() - - + Uninportant renaming of some internal variables. - -2003-11-24 14:25 migueldb - - * phplot.php: - - + Some more renaming for consistency: vtick, vert_tick, etc are - now y_tick_ - - + Same for htick -> x_tick_ - - + + Draw*Ticks() renamed following the same scheme - - + Grouped tick label parameter setting functions in - SetTickLabelParams(). Left original ones in 'deprecated' - - + Grouped grid parameter setting functions in SetGridParams(). - Left original ones in 'deprecated' - - + Grouped Title setting and positioning in Set*Title(). Left - original ones in 'deprecated' - - + Slight modifications to data label placement - -2003-11-24 11:30 migueldb - - * doc/index.html: - - + More reorganisation - -2003-11-23 23:53 migueldb - - * phplot.php: - - + Corrected a typo. - -2003-11-23 23:39 migueldb - - * phplot.php: - - + Restructured internal font management: font variables are now - hashes with all associated info. DrawText() now only needs that - and chooses whether to draw TrueType or not. Almost every "if - ($this->use_ttf)" check removed as a result. - - + Added SetDefaultTTFont() - - + When drawing pie charts (no axis titles nor labels), maximize - plot area. - -2003-11-23 21:48 migueldb - - * doc/index.html: - - + Included function_reference.html links here. - -2003-11-23 21:42 migueldb - - * doc/: index.html, schema.html: [no log message] - -2003-11-23 21:39 migueldb - - * doc/index.html: - - + New welcome page, some text. - - + Some features listed. - - + Uses stylesheet - -2003-11-23 21:37 migueldb - - * doc/: style.css, php_test.php: - - + First commit - -2003-11-23 21:36 migueldb - - * examples/test_setup.php: - - + Added stylesheet - - + Silly change - -2003-11-23 21:35 migueldb - - * examples/inline_image.php: - - + Silly change - -2003-11-23 21:34 migueldb - - * examples/create_chart.php: - - + Renamed SetYLabel() and SetXLabel() to SetYTitle() and - SetXTitle() - -2003-11-23 21:33 migueldb - - * examples/format_chart.php: - - + Added file format option - - + Added stylesheet - -2003-11-23 20:00 migueldb - - * phplot.php: - - + DrawText(): horizontal (left, center and right) and vertical - (top, center, bottom) alignment finished. - - + Replaced all outdated ocurrences of ImageString() with calls to - DrawText(). - - + Removed redundant text placement calculations. - - + DrawLegend() now draws the legend box more accurately (final - solution to bug #527867) - -2003-11-23 02:02 migueldb - - * phplot.php: - - + tick_length and tick_length2 now are htick_length and - vtick_length. - - + Included SetHTickLength() and SetVTickLength() - - + Fixed silly bug with SetDefaultFonts() - - + SetUseTTF() now resets the fonts. - - + More room for title - -2003-11-22 23:40 migueldb - - * phplot.php: - - + Somewhat clearer naming convention for labels, ticks, titles - and fonts. - - + X tick labels and X Title can be: plotdow, plotup, both, none - - + Y tick labels and Y Title can be: plotleft, plotright, both, - none - - + Renamed some Set*() internal functions to Calc*() (Set - - + is to be left for "public" methods) - - + More doxygen comments - - + Optimised FindDataLimits() - - + A few bugfixes - -2003-11-22 18:04 migueldb - - * examples/test_setup.php: - - + Added wbmp format - - + Now using imagetypes() instead of function_exists() - -2003-11-22 13:57 migueldb - - * examples/test_setup.php: - - + Graphic formats availability checks now use function_exists() - - + HTML code restructured. - -2003-11-22 13:55 migueldb - - * examples/create_chart.php: - - + Using $_GET and $_POST - - + SetImageFormat() option added - -2003-11-22 13:54 migueldb - - * examples/format_chart.php: - - + HTML code rewritten, better layout - - + Image format option - -2003-11-22 03:02 migueldb - - * phplot.php: - - + Improved datalabel_font - - + + Added SetDatalabelFontSize() - - + Added many more doxygen comments. - - + Other micro-changes. - -2003-11-22 02:06 migueldb - - * examples/inline_image.php: - - + register_globals default (PHP4) setting taken into account - -2003-11-22 01:46 migueldb - - * phplot.php: - - + Bug #790745 fixed. Thx. to the poster. - - + DrawError() now accepts position and centers text - - + '[423202] Catch error on empty arrays' solved. No more 'divide - by zero's - - + Some (random) doxygen comments inserted. It might be a good - idea for auto-docs. - -2003-11-21 22:27 migueldb - - * phplot.php: My first commit, here are the changes (that I - remember) - - + Y axis tick and label options now work - - + X axis tick and label options: plotdown,plotup,both,none - - + Implemented tick_length2 for rightmost/upper side - - + More default data colors assigned to data_color and error_color - arrays - - + Deprecated draw_vert_ticks and SetDrawVertTicks() in favor of - 'none' value in vert_tick_position - - + Better? upper,lower, rightmost margin calculation - - + Dashed grid lines - - + Added FormatTickLabel(). Removed redundant code in - DrawHorizontalTicks() - - + Removed all calls left to DrawDataLabel(), moved to - "deprecated"... - - + Moved some code and functions around. - - + Attempted correction for Bug [440065] "Pie labels not centered - correctly" - - + Corrected '$which_pt not an acceptable plot type' typo [682068] - - + Now using SetFileFormat() for PHP4 - - + Bug [484235] might be solved by now (not sure!) - -2003-11-21 20:42 migueldb - - * examples/create_chart.php: Now works with register_globals off - -2002-06-21 01:35 afan - - * examples/test_setup.php: Testing update: (Moved GIF to last - check) - -2002-04-30 15:25 afan - - * phplot.php: Fixed error in pie graphs with data=0. Changed - functions and tabs to PEAR standards - - Afan - -2002-02-22 03:15 afan - - * phplot.php: No changes - just settting up new server - -2001-04-19 03:03 afan - - * phplot.php, examples/inline_image.php: fix error on function - -2001-04-19 00:18 mdj_guardian - - * phplot.php: Fixed problem with example 8. Also modified the - default y_padding value so the examples with zero on the bottom - turn out that way. - -2001-04-18 07:18 mdj_guardian - - * phplot.php: Made the following changes: - - + Fixed the case of built in functions to match PHP documentation - - + Modified SetFileFormat to work in PHP3 & 4 with no changes - - + Improved auto-scaling signifcantly (SetVertPadding, y_padding, - SetPlotAreaWorld) (needs docs and examples) - - + Fixed a problem in DrawDashedLine when the line segments were - zero length - - + Modified DrawLegend to fix positioning of lower right corner of - box. Was cutting through text - - + Removed a duplicated line in FindDataLimits - -2001-04-08 15:58 afan - - * phplot.php: Minor change to formatting - -2001-03-27 07:42 afan - - * phplot.php, phplot_data.php, examples/data.php, - examples/data_sample1.php, examples/data_sample3.php, - examples/example1.php, examples/example2.php, - examples/example3.php, examples/example4.php, - examples/example6.php, examples/example7.php, - examples/example8.php, examples/example9.php, - examples/format_chart.php, examples/inline_image.php, - examples/test_setup.php: Committing 4.4.6 - -2001-03-19 16:08 afan - - * phplot.php, doc/user_functions.html: Cleaned up Shading Function - for Bars Bug Fix in Placement of Datalabels. - -2001-03-17 16:40 afan - - * examples/: data.php, data_date.php, data_date2.php, - data_sample1.php, data_sample2.php, data_sample3.php, - example1.php, example2.php, example3.php, example6.php, - example7.php, example8.php, example9.php, format_chart.php, - inline_image.php, test_setup.php: changed examples from ? to ?php - format. - -2001-03-16 13:59 afan - - * phplot.php: Error in line 361 corrected. - -2001-03-16 03:10 afan - - * phplot.php: Major updates to DrawDots and DrawBars to make it - easy to put labels and dots overwriting bars. Code optimizations - and checks to make sure you don't need to set error_reporting(0). - -2001-03-09 19:58 afan - - * phplot.php: Minor bug fixes - -2001-03-01 17:47 afan - - * README.txt, phplot.php, rgb_small.inc.php, examples/example8.php: - Bug on line 164 corrected Removed rgb_small.inc.php - -2001-02-27 17:49 afan - - * phplot.php: Added ability to use background Images with plots - Changed Image Color Allocation to work even with background - Images Note: this change requires usage of PHPLOT 3.0.2 or later. - -2001-02-23 21:22 afan - - * phplot.php, phplot_data.php, examples/example3.php, - examples/example6.php, examples/example9.php: DrawText, - SetRGBColor, Sessions fixes - -2001-02-16 16:40 afan - - * phplot.php, examples/example1.php: Added output_file and - input_file Added Constructor Added check to see if an image index - has already been added for smaller files - -2001-02-14 21:06 afan - - * phplot.php, examples/example1.php: Added Function SetColorIndex - for operating on image color indexes directly Also checks to see - if the color has already been defined for image size - optimization. - -2001-02-13 20:50 afan - - * phplot.php, examples/example1.php: Minor Bugfix - -2001-02-13 18:16 afan - - * phplot.php, examples/data.php, examples/example2.php, - examples/example3.php, examples/example4.php: Title - modifications, added movable Y axis - -2001-02-12 14:54 afan - - * examples/example8.php: Adding example8: two plots on one image - -2001-02-09 18:56 afan - - * phplot.php: Added SetYAxisPosition and changed DrawYAxisCode - -2001-02-09 10:14 afan - - * phplot.php: Added Title return lines - -2001-02-08 05:35 afan - - * README.txt, phplot.php, doc/user_functions.html, - doc/user_internal_functions.html, examples/data.php, - examples/example1.php, examples/example4.php: Added Error Bar - Widths, Missing Data Handling, Minor bugfix, More docs - -2001-01-29 05:51 afan - - * phplot.php, examples/create_chart.php, examples/example7.php: - Added NumHorizTicks, fixed bug in example 7 - -2001-01-23 05:09 afan - - * doc/user_functions.html: Doc Fix - -2001-01-23 05:04 afan - - * phplot.php: Minor Changes - -2001-01-23 05:03 afan - - * phplot.php, doc/internal_functions.html, doc/user_functions.html: - Updated Documentation - -2001-01-23 04:36 afan - - * phplot.php, examples/example3.php: Minor bug fixes. - -2001-01-23 01:33 afan - - * phplot.php: Minor fix to phplot after major update - -2001-01-23 01:06 afan - - * README.txt, phplot.php, phplot_data.php, - examples/create_chart.php, examples/data.php, - examples/data_date.php, examples/data_date2.php, - examples/example2.php, examples/example4.php, - examples/example6.php, examples/example7.php, examples/test1.php: - Lots changed: easier to make multiple images, logs, etc. - -2001-01-17 04:05 afan - - * phplot.php: [no log message] - -2001-01-17 03:42 afan - - * phplot.php: Implemented Log Plots. Very little error checking - -2001-01-17 03:41 afan - - * phplot.php: Log plots implemented. Very little data error - checking. - -2001-01-14 03:02 extensive - - * phplot.php, phplot_data.php, doc/internal_functions.html, - doc/user_functions.html: Changed ReportError to DrawError - respectively PrintError, added new functionality to PHPlot_data - and added some documentation of PHPlot_data - -2001-01-11 18:43 afan - - * phplot.php, doc/internal_functions.html: Added documentation. - -2001-01-11 17:07 afan - - * create_chart.php, data_sample1.php, data_sample2.php, - data_sample3.php, data_sample4.php, doc.htm, format_chart.php, - phplot.php, doc/examples.html, doc/function_reference.html, - doc/index.html, doc/internal_functions.html, doc/quick_start.php, - doc/user_functions.html, doc/user_internal_functions.html, - examples/benjamingothic.ttf, examples/create_chart.php, - examples/data_sample1.php, examples/data_sample2.php, - examples/data_sample3.php, examples/data_sample4.php, - examples/format_chart.php, examples/inline_image.php, - examples/test_setup.php: Major change to structure of PHPLOT. Doc - directory, moved examples to examples directory. - -2001-01-11 16:25 extensive - - * phplot.php: just added ReportError() and changed all DrawError() - calls to use ReportError(). phplot seems to be broken on my - system but I don't think it is my fault (this was the first time - I was testing a 3.7.x version), let's address that later. Is it - broken on your system, too, Afan? - -2001-01-11 02:14 extensive - - * phplot.php, phplot_data.php: I just corrected some typos (mostly - my name being misspelled ;-) - -2001-01-08 00:53 afan - - * doc.htm, phplot.php, phplot_data.php: Added Plot Border types - -2001-01-07 00:35 afan - - * doc.htm, phplot_data.php: Adding phplot_data for release 3.7.0 - -2001-01-07 00:30 afan - - * examples/: data.php, example1.php, example2.php, example3.php, - example4.php, example5.php, rgb.inc.php, test1.php: Adding - PHPLOT_DATA routines. Defining the first sub-class. Added - examples: Afan - -2001-01-07 00:23 afan - - * README.txt, create_chart.php, data_sample3.php, doc.htm, - phplot.php, stocks.php, stocks1.php, test1.php: 3.6.4 Committed - -2000-12-13 22:02 afan - - * phplot.php: Allowing data in time_format but printed using - strftime - -2000-12-13 04:47 afan - - * doc.htm, phplot.php, stocks.php: ver 3.2.1 and New Docs - -2000-12-11 18:03 afan - - * create_chart.php, data_sample2.php, format_chart.php, phplot.php: - Release version 3.2.0 - -2000-11-29 17:12 afan - - * README.txt, benjamingothic.ttf, create_chart.php, - data_sample1.php, data_sample2.php, data_sample3.php, - data_sample4.php, doc.htm, format_chart.php, phplot.php, - rgb.inc.php, rgb_small.inc.php, stocks.php, stocks1.php, - test1.php: Initial revision - -2000-11-29 17:12 afan - - * README.txt, benjamingothic.ttf, create_chart.php, - data_sample1.php, data_sample2.php, data_sample3.php, - data_sample4.php, doc.htm, format_chart.php, phplot.php, - rgb.inc.php, rgb_small.inc.php, stocks.php, stocks1.php, - test1.php: The first upload! Afan Ottenheimer +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/gui/bacula-web/external_packages/phplot/LICENSE.GPL b/gui/bacula-web/external_packages/phplot/LICENSE.GPL deleted file mode 100644 index 5b6e7c66c2..0000000000 --- a/gui/bacula-web/external_packages/phplot/LICENSE.GPL +++ /dev/null @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, 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 or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -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 Program or any portion -of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -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 Program, 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 Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, 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 executable. However, as a -special exception, the source code 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. - -If distribution of executable or 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 counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program 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. - - 5. 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 Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. 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 Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program 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 Program. - -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. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program 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. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of 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 Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff --git a/gui/bacula-web/external_packages/phplot/LICENSE.PHP_3_0 b/gui/bacula-web/external_packages/phplot/LICENSE.PHP_3_0 deleted file mode 100644 index ffc1ab71f3..0000000000 --- a/gui/bacula-web/external_packages/phplot/LICENSE.PHP_3_0 +++ /dev/null @@ -1,68 +0,0 @@ --------------------------------------------------------------------- - The PHP License, version 3.0 -Copyright (c) 1999 - 2003 The PHP Group. All rights reserved. --------------------------------------------------------------------- - -Redistribution and use in source and binary forms, with or without -modification, is permitted provided that the following conditions -are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - 3. The name "PHP" must not be used to endorse or promote products - derived from this software without prior written permission. For - written permission, please contact group@php.net. - - 4. Products derived from this software may not be called "PHP", nor - may "PHP" appear in their name, without prior written permission - from group@php.net. You may indicate that your software works in - conjunction with PHP by saying "Foo for PHP" instead of calling - it "PHP Foo" or "phpfoo" - - 5. The PHP Group may publish revised and/or new versions of the - license from time to time. Each version will be given a - distinguishing version number. - Once covered code has been published under a particular version - of the license, you may always continue to use it under the terms - of that version. You may also choose to use such covered code - under the terms of any subsequent version of the license - published by the PHP Group. No one other than the PHP Group has - the right to modify the terms applicable to covered code created - under this License. - - 6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes PHP, freely available from - ". - -THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND -ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP -DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED -OF THE POSSIBILITY OF SUCH DAMAGE. - --------------------------------------------------------------------- - -This software consists of voluntary contributions made by many -individuals on behalf of the PHP Group. - -The PHP Group can be contacted via Email at group@php.net. - -For more information on the PHP Group and the PHP project, -please see . - -This product includes the Zend Engine, freely available at -. diff --git a/gui/bacula-web/external_packages/phplot/NEWS.txt b/gui/bacula-web/external_packages/phplot/NEWS.txt new file mode 100644 index 0000000000..0efb1bc5c9 --- /dev/null +++ b/gui/bacula-web/external_packages/phplot/NEWS.txt @@ -0,0 +1,975 @@ +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-10-03 Release 5.2.0 + +Overview: + +This is the current stable release of PHPlot. This release includes some +bug fixes and new features, described below, and a reimplementation of +internal processing of colors. + +The PHPlot reference manual has been updated to match this release. +Horizontal plots are now documented in the manual, and the interim +documentation in the source release (HorizontalBars.txt) has been removed. +The manual is available in HTML format as a separate download from +Sourceforge. The manual is also available for on-line viewing from the +project home page. Starting with this release, the manual is also +available in PDF format from the project home page. + + +Cautions, Important Notes, and Compatibility Issues: + +Due to changes in color allocation (see bug #3049726 below), image files +produced with PHPlot-5.2.0 will differ when compared byte-for-byte with +those created by earlier releases, even when the images are identical (as +they nearly always are). + +If you are creating a horizontal plot with any negative X values, and not +setting the Y axis position, your plot will change (see bug #3074402 below) +because PHPlot no longer leaves the Y axis on the left for horizontal plots. + +Some internal methods that used to have 'public' visibility are now protected. +This will generally prevent you from using them. The list of changed functions +can be found in the release ChangeLog. Avoid using any internal methods. + +If you are making a stackedbar plot with any negative values, PHPlot-5.1.3 +and earlier took the absolute value of each data value (which was not +documented), but PHPlot-5.2.0 does not. See Feature Request #3073679 below. + +If your plot fills the 256 available color map slots in a palette image, +your image may look different with PHPlot-5.2.0 due to changes in color +allocation order. This was seen on two of the tests in the PHPlot test +suite. These tests tiled a JPEG image into the plot area or image +background. Being a truecolor image, the JPEG had a huge number of colors, +which filled all available slots in the color map. Additional colors for +plot elements had to be approximated, and different colors resulted with +PHPlot-5.2.0 versus previous releases. To avoid this problem, either use a +truecolor PHPlot object (PHPlot_truecolor constructor), or reduce the +number of colors in the background image and convert it to PNG or GIF. + +This release contains significant changes to PHPlot internals. In particular, +variables and functions related to element colors and color handling have +changed. Refer to the PHPlot release ChangeLog for more details. Remember, +if you rely on accessing any member variable, or on using any non-public +function or any function not documented in the reference section of the manual, +your code is at risk of breaking with each new release. + + +New features in 5.2.0: + +#3077554 "Finer control over plot element colors" (partial): + The X, Y, and main titles can now have different colors. See the PHPlot + Reference Manual entries for SetXTitleColor and SetYTitleColor. + +#3073679 "Stacked bar plots with negative values": + A stacked bar plot can now include negative values, and stacks of negative + values will be drawn downwards (or leftwards). See the PHPlot Reference + Manual under "Plot Type: stackedbars" for details. + + +Bugs Fixed in 5.2.0: + +#3045131 "SetTransparentColor problems": + Setting a transparent color now works whether before or after setting + the background color (for example), and also now works with a data color. + +#3049726 "Optimize color allocation" + PHPlot now defers allocating colors until drawing time, and tries to allocate + only the colors which are actually needed. For palette images, this results + in use of fewer color slots and slightly smaller image files. + +#3074402 "Fix Y axis default for horizontal plots": + When horizontal plots were introduced, an asymmetry with the X and Y axis + position defaults was known but left. This behavior was later determined + to be unhelpful. So now the Y axis on horizontal plots will default to X=0, + or the X value closest to zero within the plot area range. This is the same + behavior as for the X axis in vertical plots. + +#3056991 "Internal methods should be 'protected'": + More of the internal PHPlot functions were changed to 'protected' visibility, + as the test suite was fixed to not call them directly. + +#3057000 "Review 'deprecated' methods": + One broken deprecated method (SetColor) was removed and one changed. + Note that deprecated methods are not documented and not tested. + +----------------------------------------------------------------------------- + +2010-08-30 Release 5.1.3 + +Overview: + +This is the current stable release of PHPlot. Additional horizontal plot +types and features have been added, however horizontal plots are still +considered 'experimental'. A new callback has been added which allows +greater control over the data colors. An improved method for accessing +TrueType Font (TTF) files means that on many systems TTF text can be used +without specifying font paths. + + +Cautions and Important Notes: + +Since the previous PHPlot release, PHP-5.3.3 and PHP-5.2.14 have been +released, and these include a fix for the TrueType Font (TTF) rendering +problem. Use of these releases is now recommended. + +There has been extensive cleanup of the PHPlot code. If you have a +customized version of PHPlot, you may find it difficult to update. + +The "additional data support" script phplot_data.php has been removed from +this release. The script has not been developed or tested in a long time, +and was found to have numerous problems. The script can still be found in +the CVS repository. + +The changes in this release (horizontal plots, custom data color callback, +and TrueType Font handling) should not result in any compatibility issues. + + +New features in 5.1.3: + +#3049703 "Additional horizontal plots and features": + + Implemented data values labels in horizontal bar charts. + Use: SetXDataLabelPos('plotin'). + + Add horizontal stacked bar charts, with data value labels. + + Add horizontal thinbarline plots. + Horizontal plots are still considered 'experimental', and documentation is + in the HorizontalBars.txt text file rather than the PHPlot Reference Manual. + +#3034164 "Extended control of data colors": + New callback 'data_color' can be used to customize selection of the color + of each bar, line segment, point marker, etc. This is documented in the + PHPlot Reference Manual section "Custom Data Color Selection", with new + examples in the Examples chapter. + + +Bugs Fixed in 5.1.3: + +#3051906 "Better choice for default TT font": + Rather than always using the unlikely 'benjamingothic.ttf' as its default + TrueType font name, PHPlot now has a short list of sans-serif fonts, and + tries to find one that works if a default TT font is needed. On many + systems, this will provide a high-quality default font without help. + +#3051832 "Let PHP/GD find the font file": + Instead of using file existence to validate a TT font file, PHPlot now + just tries to use the font. This allows PHP/GD to use its own rules to + try to find the font, without needing a pathname. This works on Windows + and at least some Linux systems. + +#3048267 "phplot_data add-on is still broken" + Not fixed. phplot_data.php has been removed from the release. + +----------------------------------------------------------------------------- + +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 & linepoints 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/gui/bacula-web/external_packages/phplot/README b/gui/bacula-web/external_packages/phplot/README deleted file mode 100644 index b571f13ee3..0000000000 --- a/gui/bacula-web/external_packages/phplot/README +++ /dev/null @@ -1,45 +0,0 @@ -This is a class for creating scientific and business charts. -To start extract the files with - - tar -zxvf phplot-5.0rc1.tar.gz - -and then point your browser to - - doc/index.php. - -There are some configuration settings that you will need to make -based on your setup. - -1. File Type: Depending on the version of GD you are using, - you may or may not have GIF or PNG file ability. That is - set with the function. - - SetFileFormat("") where is png, gif, jpeg, ... - - or edit the file phplot.php and change the line - - var $file_format = ""; - -2. TTF: If you have TTF installed then use (and read the docs) - - SetUseTTF(TRUE); - - otherwise use - - SetUseTTF(FALSE); - -Everything else should be independent of what version you are using. -This has been tested with PHP3, PHP4, GD1.2 and GD 3.8. - -To start please see doc/index.php. There you'll find examples, tests and -some introductory documents. - --------------------------- - -This is distributed with NO WARRANTY and under the terms of the GNU GPL -and PHP licenses. If you use it - a cookie or some credit would be nice. - -You can get a copy of the GNU GPL at http://www.gnu.org/copyleft/gpl.html -You can get a copy of the PHP License at http://www.php.net/license.html - -See http://www.sourceforge.net/projects/phplot/ for the latest changes. diff --git a/gui/bacula-web/external_packages/phplot/README.txt b/gui/bacula-web/external_packages/phplot/README.txt new file mode 100644 index 0000000000..7485226510 --- /dev/null +++ b/gui/bacula-web/external_packages/phplot/README.txt @@ -0,0 +1,163 @@ +This is the README file for PHPlot +Last updated for PHPlot-5.2.0 on 2010-10-03 +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 + NEWS.txt . . . . . . . . . . . . Highlights changes in releases + README.txt . . . . . . . . . . This file + contrib . . . . . . . . . . . . "Contributed" directory, add-ons + phplot.php . . . . . . . . . . The main PHPlot source file + rgb.inc.php . . . . . . . . . . Optional extended color table + + +REQUIREMENTS: + +You need a recent version of PHP5, and you are advised to use the latest +stable release. This version of PHPlot has been tested with PHP-5.3.3 and +PHP-5.2.14 on Linux, and with PHP-5.3.3 on Windows/XP. + +Use of PHP-5.3.2 or PHP-5.2.13 is not recommended, if you are using +TrueType Font (TTF) text. A bug with TTF rendering in those versions +affects PHPlot images. This was fixed in PHP-5.3.3 and PHP-5.2.14. + +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 two script files somewhere +your PHP application scripts will be able to find them. The scripts are: + phplot.php - The main script file + rgb.inc.php - Optional large color table +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 Issues: + + PHP has many build-time and configuration options, and these can affect +the operation of PHPlot (as well as any other application or library). Here +are some known issues: + + Slackware Linux includes a version of PHP built with --enable-gd-jis-conv +(JIS-mapped Japanese font support). This prevents the usual UTF-8 encoding +of characters from working in TrueType Font (TTF) text strings. + + The Ubuntu Linux PHP GD package (php5-gd) was built to use the external +shared GD library, not the one bundled with PHP. This can result in small +differences in images, and some unsupported features (such as advanced +truecolor image operations). Also, although this Ubuntu GD library was +built with fontconfig support, PHP does not use it, so you still need to +specify TrueType fonts with their actual file names. + + Some PHP installations may have a memory limit set too low to support +large images, especially truecolor images. + + +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/gui/bacula-web/external_packages/phplot/contrib/README.txt b/gui/bacula-web/external_packages/phplot/contrib/README.txt new file mode 100644 index 0000000000..3c63225200 --- /dev/null +++ b/gui/bacula-web/external_packages/phplot/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/gui/bacula-web/external_packages/phplot/contrib/color_range.example.php b/gui/bacula-web/external_packages/phplot/contrib/color_range.example.php new file mode 100644 index 0000000000..d29978b770 --- /dev/null +++ b/gui/bacula-web/external_packages/phplot/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/gui/bacula-web/external_packages/phplot/contrib/color_range.php b/gui/bacula-web/external_packages/phplot/contrib/color_range.php new file mode 100755 index 0000000000..6ff4d6d0ab --- /dev/null +++ b/gui/bacula-web/external_packages/phplot/contrib/color_range.php @@ -0,0 +1,103 @@ + + "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. + + Newer versions of PHPlot use 4 components (r, g, b, a) arrays for color. + This script ignores the alpha component in those arrays. + +*/ +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' || $data_type == 'text-data-yx') + return ($max_row - 1); # Each record is (label Y1 Y2...) + + if ($data_type == 'data-data' || $data_type == 'data-data-yx') + 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/gui/bacula-web/external_packages/phplot/contrib/color_range.test1.php b/gui/bacula-web/external_packages/phplot/contrib/color_range.test1.php new file mode 100644 index 0000000000..661f591ea7 --- /dev/null +++ b/gui/bacula-web/external_packages/phplot/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/gui/bacula-web/external_packages/phplot/contrib/prune_labels.php b/gui/bacula-web/external_packages/phplot/contrib/prune_labels.php new file mode 100644 index 0000000000..5f272e52cd --- /dev/null +++ b/gui/bacula-web/external_packages/phplot/contrib/prune_labels.php @@ -0,0 +1,36 @@ + 0) $data[$i][0] = ''; + if (++$k >= $m) $k = 0; + } +} diff --git a/gui/bacula-web/external_packages/phplot/contrib/prune_labels.test.php b/gui/bacula-web/external_packages/phplot/contrib/prune_labels.test.php new file mode 100644 index 0000000000..6d3bbb2635 --- /dev/null +++ b/gui/bacula-web/external_packages/phplot/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/gui/bacula-web/external_packages/phplot/doc/imgs/graph1.png b/gui/bacula-web/external_packages/phplot/doc/imgs/graph1.png deleted file mode 100644 index 3b5a1c205a..0000000000 Binary files a/gui/bacula-web/external_packages/phplot/doc/imgs/graph1.png and /dev/null differ diff --git a/gui/bacula-web/external_packages/phplot/doc/imgs/graph2.png b/gui/bacula-web/external_packages/phplot/doc/imgs/graph2.png deleted file mode 100644 index ff0e80beb8..0000000000 Binary files a/gui/bacula-web/external_packages/phplot/doc/imgs/graph2.png and /dev/null differ diff --git a/gui/bacula-web/external_packages/phplot/doc/imgs/graph3.png b/gui/bacula-web/external_packages/phplot/doc/imgs/graph3.png deleted file mode 100644 index e437c8789e..0000000000 Binary files a/gui/bacula-web/external_packages/phplot/doc/imgs/graph3.png and /dev/null differ diff --git a/gui/bacula-web/external_packages/phplot/doc/imgs/phplot-dia.png b/gui/bacula-web/external_packages/phplot/doc/imgs/phplot-dia.png deleted file mode 100644 index eac14eb27d..0000000000 Binary files a/gui/bacula-web/external_packages/phplot/doc/imgs/phplot-dia.png and /dev/null differ diff --git a/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig1.png b/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig1.png deleted file mode 100644 index 08fc68bc3b..0000000000 Binary files a/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig1.png and /dev/null differ diff --git a/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig2.png b/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig2.png deleted file mode 100644 index 5deafe4b04..0000000000 Binary files a/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig2.png and /dev/null differ diff --git a/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig3.png b/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig3.png deleted file mode 100644 index c98f692c52..0000000000 Binary files a/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig3.png and /dev/null differ diff --git a/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig4.png b/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig4.png deleted file mode 100644 index f6c6872c0a..0000000000 Binary files a/gui/bacula-web/external_packages/phplot/doc/imgs/qstart_fig4.png and /dev/null differ diff --git a/gui/bacula-web/external_packages/phplot/doc/index.php b/gui/bacula-web/external_packages/phplot/doc/index.php deleted file mode 100644 index a6482ec64a..0000000000 --- a/gui/bacula-web/external_packages/phplot/doc/index.php +++ /dev/null @@ -1,152 +0,0 @@ - - - - - Welcome to PHPlot - - - - -

Welcome to PHPlot 5.0

- - - - - -
-

- PHPlot is a PHP4 class for on the fly graphs - generation. It was started by - Afan Ottenheimer in 2000 as an - opensource project, and is now co-developed with - Miguel de Benito thanks to - sourceforge. It is distributed under - the terms of the - GNU General Public License, - and the PHP license. You can always - obtain the latest source from the - sourceforge project page, please do also check CVS, we try to have it always working - there. -

-

For further information, please check - our website -

-
- - - -
Example line graph with labels, legend - and left and lower axis titles.
-
- - -

Features

- - - - - -
- - - -
Example 3d pie chart.
-
-

- Here goes a (incomplete) list, in no particular order.: -

    -
  • Several different graph types: lines, bars, stacked bars, points, areas, pie, squared.
  • -
  • text-data, data only and data-error data types accepted.
  • -
  • 3D shading for pie and bar graphs.
  • -
  • Different line types: solid and wholly customizable dashed ones.
  • -
  • Can draw error margins along y-axis when supplied in data.
  • -
  • Highly customizable canvas: titles, labels and ticks can be - placed anywhere, with any color and everything gets automagically placed without overlapping.
  • -
  • Vertical and horizontal grids.
  • -
  • Legend. Different types on the works.
  • -
  • TrueType font support.
  • -
  • Linear and logaritmic scales.
  • -
  • Several output formats: jpeg, png, gif, wbmp (those supported by your GD)
  • -
- And here a short to-do/whishlist: -
    -
  • Horizontal bars.
  • -
  • Simple isometric 3D plots.
  • -
  • Automatic placement of several plots in one image.
  • -
  • Better or automatic management of many drawing options (ticks, labels, etc.)
  • -
  • Subclassing for optimisation: move features into subclasses for optional use - and leave a fast core.
  • -
-

-
- - -

Requirements

-

-We are not sure about exact requirements, but at least PHP 4.2.0 and -GD Lib 2 are necessary. Feedback is welcome. -

- - -

Quick start

-

You can rush for a quick start here.

- - -

Tests and examples

-

-These examples make use of many, but not all, of the features present in PHPlot. -The best one is the example-o-matic, where you can alter many parameters. Please proceed -to any of them: -

-

- - -

Internals

-

-Description of the use and inner workings of PHPlot: -

-

- -

The Authors

-

-

-

- -

$Id$

- - diff --git a/gui/bacula-web/external_packages/phplot/doc/internal_functions.html b/gui/bacula-web/external_packages/phplot/doc/internal_functions.html deleted file mode 100644 index 90895c0d7c..0000000000 --- a/gui/bacula-web/external_packages/phplot/doc/internal_functions.html +++ /dev/null @@ -1,269 +0,0 @@ - - -PHPlot Internal Functions - - - -

PHPlot Internal Functions

- -

DrawArea() -
Internal Function: Draw an Area Chart. $data[] must already be defined as data-data -using $data[] = array("title",x,y1,y2,...) Points are placed at (x,y1), (x,y2), ... -one x-point per $data[] element.
Colors and border colors for each of the y1, y2, ... is set -by SetDataColors($which_data,$which_border) -This function is called if -you use SetPlotType("area") and SetDataType("data-data") - - -

DrawAreaSeries() -
Internal Function: Draw an Area Chart. $data[] must already be defined as $data[] = -array("title",y1,y2,y3,...) The x axis is divided evenly, one x-point per -$data[] element.
Colors and border colors for each of the y1, y2, ... is set -by SetDataColors($which_data,$which_border) -This function is called if -you use SetPlotType("area") and SetDataType("text-data") - -

DrawBackground() -
Internal Function: Draw the full background of the image on image pointer. Should be used -BEFORE other Draw functions unless you really want to overwrite what you've done -before. See SetBackgroundColor and SetImageArea for setting -parameters. - -

DrawBars() -
Internal Function: Draw vertical bars. $data[] must already be defined as $data[] = -array("title",y1,y2,y3,...) The x axis is divided evenly, one x-point per -$data[] element.
Colors and border colors for each of the y1, y2, ... is set -by SetDataColors($which_data,$which_border) -This function is called if -you use SetPlotType("bars") and SetDataType("text-data") - -

DrawDashedLine($x1,$y1,$x2,$y2,$dashed, $space, $color) -
Internal Function: Called instead of ImageLine when line_style = 'dashed' -line_style is set by SetLineStyles All variables passed to DrawDashedLine -are in pixel coordinates. - -

DrawDots() -
Internal Function: Draw Dots as defined by SetPointType and data in x,y format. $data[] -must already be defined as $data[] = array("title",x,y1,y2,...) Where the point -occurs depends on both the X and Y values.
Colors and border colors for each -y1, y2, , ... is set by SetDataColors($which_data,$which_border) -This function is called if -you use SetPlotType("dots") and SetDataType("data-data") - -

DrawDotSeries() -
Internal Function: Draw Dots as defined by SetPointType and data in text-data format. -$data[] must already be defined as $data[] = array("title",y1,y2,...) -The x axis is divided evenly, one x-point per $data[] element.
-Colors and border colors -for each y1, y2, , ... is set by SetDataColors($which_data,$which_border) -This function is called if -you use SetPlotType("dots") and SetDataType("text-data") - -

DrawDotsError() -
Internal Function: Draw Dots and Error Bars as defined by SetPointType and -data in data-data-error format. $data[] must already be defined -as $data[] = array("label",x,y,error+,error-) -Colors and border colors -for each y1, y2, , ... is set by SetDataColors($which_data,$which_border) -The shape of the error bars is defined by SetErrorBarShape($which_ebs) -and SetErrorBarSize($which_ebs)
-This function is called if -you use SetPlotType("dots") and SetDataType("data-data-error") - -

DrawError($error_message) -
Internal Function: Draw an internal error message printed on the Image directly. -This way - if there is an error and the output is an image you -don't get the PHP "Header already sent" messages. - -

DrawGraph() -
Internal Function: This is th last function called after you have set up -all the parameters of the graph see Set.... functions below. - -

DrawHorizontalTicks() -
Internal Function: Draw the ticks on the X axis. -The distance between ticks can be defined in a number of ways. Note: -that for text-data data its best to let the program handle the -distance between ticks - the default is 1. (Text-data data is data where the data is evenly spaced over -the x axis, no x-value is entered) - -

DrawImageBorder() -
Internal Function: Draw the border around the entire image. Currently this -generates the raised border look around the image. - -

DrawLabels() -
Internal Function: Draw the Title, X-axis label, and the Y-axis label. This -really just calls DrawTitle, DrawXLabel, and DrawYLabel. - -

DrawLegend(x,y,type) -
Internal Function: Draw the Legend. See SetLegendPixels() - -

DrawLines() -
Internal Function: Draw lines in data-data format. -$data[] must already be defined -as $data[] = array("label",x,y1,y2,....) -Colors and border colors -for each y1, y2, , ... is set by SetDataColors($which_data,$which_border) -This function is called if -you use SetPlotType("lines") and SetDataType("data-data") -
- -

DrawLineSeries() -
Internal Function: Draw lines in text-data format. -$data[] must already be defined -as $data[] = array("label",y1,y2,y3,....) -The x axis is divided evenly, one x-point per -$data[] element.
Colors and border colors for each of the y1, y2, ... is set -by SetDataColors($which_data,$which_border) -This function is called if -you use SetPlotType("lines") and SetDataType("text-data") - -

DrawLinesError() -
Internal Function: Lines and Error Bars as defined by SetPointType and -data in data-data-error format. $data[] must already be defined -as $data[] = array("label",x,y,error+,error-) -Colors and border colors -for each y1, y2, , ... is set by SetDataColors($which_data,$which_border) -The shape of the error bars is defined by SetErrorBarShape($which_ebs) -and SetErrorBarSize($which_ebs) -This function is called if -you use SetPlotType("lines") and SetDataType("data-data-error") - -

DrawPieChart() -
Internal Function: Draw pie chart. Data is in text-data format. $data[] must -already been defined as $data[] = array("label",y1,y2,y3,...)
-Colors and border colors for each of the y1, y2, ... is set -by SetDataColors($which_data,$which_border) -This function is called if -you use SetPlotType("pie") and SetDataType("text-data") - -

DrawPlotAreaBackground() -
Internal Function: Draw the Background - in the color as defined by X -and in an area as defined by X - -

DrawPlotBorder() -
Internal Function: Draw the border around the -plot area - (Note: this is NOT the full image). This draws -the rectangle as well as calling the functions -DrawVerticalTicks() and DrawXAxis() - -

DrawTitle() -
Internal Function: This will draw the title as set by -SetTitle and -SetTitleColor. This is also affected by SetUseTTF -to show how the title appears. - - -

DrawVerticalTicks() -
Internal Function: Called by DrawPlotBorder. The spacing between the ticks -is automatically set unless you have previously set the increment -by - SetNumVertTicks($which_nt) -or - SetVertTickIncrements but not both. - -

DrawXAxis() -
Internal Function: Draw the horizontal X axis at the world position X=0. - -

DrawXLabel() -
Internal Function: Called from DrawLabels. Draws the label of the X-Axis. - -

DrawYErrorBar($x_world,$y_world,$error_height,$error_bar_type,$color) -
Internal Function: This is similar to DrawDot, but it draws error bars. -Draw one set of error bars at $x_world and $y_world where the -data is defined in world coordinates (not pixel coordinates). - -

DrawYLabel() -
Internal Function: Called from DrawLabels. Draws the label of the Y-Axis. Its -position is defined by the size of the font. Typically there is a -two character height width around the plot area for drawing the -labels. - -

FindDataLimits() -
Internal Function: This must be called AFTER SetDataType. It finds -the maxima and minima for setting the scaling to be -able to convert from world to pixel coordinates. - -

InitImage() -
Internal Function: An internal function called to set the -image pointer for GD. - -

PrintError($error_message) -
Internal Function: Prints the given error message to stdout. The function -is used for fatal errors that do not allow for creating an image. - -

PrintImage() -
Internal Function: An internal function which prints the image using the PHP -functions Header("Content-type:...") and ImageGIF(), ImageJPEG(), -or ImagePNG() depending on what the setting is of $this->file_format. -This is set by SetFileFormat() - -

SetColor($color_asked) -
Internal Function: Translate from $color_asked to the index -name color used in GD. -$color_asked can be either a name like "black" or an rgb color array -array(int,int,int).
- -

SetDefaultColors() -
Internal Function: Sets the default colors when first defining an image. -Overridden by functions like SetPlotBgColor, - SetBackgroundColor, - SetTextColor, - SetGridColor, - SetLightGridColor, - SetTickColor, and - SetTitleColor - -

SetDrawYGrid($which_dyg) -
Internal Function: 1 = Draw the Y grid. Anything else, don't draw the Y grid. - -

SetEqualXCoord() -
Internal Function: For text-data graphs set the spacing between -data points on the x-axis. - -

SetMargins() -
Internal Function: When the image is first created - set the margins - as the full area of the view surface (or panel), - less a margin of 4 character heights all round for labelling. - It thus depends on the current character size, set by SetCharacterHeight(). - -

SetTranslation() -
Internal Function: Calculate the scale and origin for translating from -world to pixel coordinates. This is an internal function, -not one that you'd use normally, but if you are going to -use it make sure you do it after SetDataType. - -

SetXLabelHeight() -
Internal Function: This is an internal function that sets the -height of the X labels based on the maximum string -length of the X data. It is used for positioning -where the labels go relative to the x-axis. - - -

SetYLabelWidth() -
Internal Function: Set the -width of the Y Label based on the maximum string -length of the Y data. It is used for positioning -where the labels go relative to the y-axis. - - -

TTFBBoxSize($size, $angle, $font, $string) -
Internal Function: Calculate the size of the box which encloses the -text string $string using font $font, -angle $angle and size $size. This is -an internal function which is not called unless use_ttf is -set to 1. - -

xtr($x_world) -
Internal Function: Translate into x-pixels coordinates from x world coordinates. - -

ytr($y_world) -
Internal Function: Translate into y-pixels coordinates from y world coordinates. - - - - - - - - - diff --git a/gui/bacula-web/external_packages/phplot/doc/quickstart.html b/gui/bacula-web/external_packages/phplot/doc/quickstart.html deleted file mode 100644 index 6f738c6c9c..0000000000 --- a/gui/bacula-web/external_packages/phplot/doc/quickstart.html +++ /dev/null @@ -1,455 +0,0 @@ - - - - - PHPLOT Quick Start and Examples - - - - - - - - - - - - - - -

PHPlot Quick Start and Examples

- -

Afan Ottenheimer, January 7, 2001

-

Miguel de Benito, January 21, 2004

-

Contents

- - -

Introduction

- -

Many web sites need to create real-time or dynamic charts and graphs. -from live data sets. Many users have found PHP a great way for this dynamic -creation of images using the GD library and there have been several -good articles describing this [] [] []. The advantage of using the server -to create an image (server side scripting) is that one does not have to -worry about browser compatibility or client operating system compatibility issues. -The PHP image generating library uses the GD library to create elementary -shapes (elipse, line, rectangle, ...). -PHPlot is a graphics library which provides a means by which you can have your -(PHP enabled) web server create and manipulate graphs as objects and -display the completed graph as an image. Data sets passed to PHPlot use a very -convenient way for database driven sites, in rows with y coordinate data. -

- -

First, lets discuss how PHPlot works in general with some -terminology. A PHPlot image can consist of several graphs , -each graph consisting of several elements. -You define an object (e.g. a variable like $graph), -select the properties of the element that compose the graph and "Draw" -what you want into the object. Tipically by selecting the plot type with -SetPlotType and at the end calling DrawGraph. You -can also directly invoke PrintImage, which either inserts the image -into the data streaming to the client or writes it to disk. -

-

In PHPlot there are World coordinates, which are the XY coordinates -relative to the axis origin, in the units of the data set; and -device (pixel) coordinates which in GD are relative to the -origin at the upper left side of the image. -

-

You can think of the "Draw" functions as shaping the -image object and the "Print" function as the method of -finally creating the digital image. PHPlot is smart enough that if -you only have one graph on an image, then the "Print" is -done for you automatically. If you do have multiple graphs per image -then you'll need to use both the "Draw" and "Print" -functions. We'll talk about that a bit later. -

-

Since PHP is a server scripted language you have several options -for how you can "Print" the image. You can:

-
    -
  1. Write the image as a file on the server. (You specify a file - name and can specify caching as well)
  2. -
  3. Have the raw data stream out within an HTML file, as <IMG - SRC="my_PHPlot_code.php">
  4. -
  5. Precede the raw data with html image headers and call the - script directly (showing up as an image) e.g. - http://somewhere/my_PHPlot_code2.php .
  6. -
-

This document explains how to create plots using PHPlot from a -PHP script. Information on PHP can be found at www.php.net. -Information about the GD library which PHP uses to create images can -be found at www.boutell.com. -More information about PHPlot can be found at www.PHPlot.com.

- -

Creating the Object

- -

You create a PHPlot object by first including the code to be used -and then defining the variable:

-
-
<?php
-include('./phplot.php');  // here we include the PHPlot code 
-$graph =& new PHPlot();   // here we define the variable graph
-
-//Rest of code goes below
-?>
-
- -

The above code assigns the PHPlot object to the variable $graph. Please, -use that '&' to create a reference, it is needed by phplot's approximation of a destructor, -a facility unavailable in PHP4.

- -

Back to top

- - -

Real World Examples

-

Case 1: A simple graph

- -

We will start with a simple line graph.

-
-
<?php
-//Include the code
-include('./PHPlot.php');
-
-//Define the object
-$graph =& new PHPlot();
-
-//Define some data
-$example_data = array(
-     array('a',3),
-     array('b',5),
-     array('c',7),
-     array('d',8),
-     array('e',2),
-     array('f',6),
-     array('g',7)
-);
-$graph->SetDataValues($example_data);
-
-//Draw it
-$graph->DrawGraph(); // remember, since in this example we have one graph, PHPlot
-                        // does the PrintImage part for you
-?>
-
- -

And that's it! What we get is the following graph:

- -
- figure 1 -
Figure 1 -
- -

That's a great start, but now we'd like to specify the width and height -of the image.

- - -

Case 1a: Different Size Images and Titles

-

-Lets say we want it to have a width of 300 and a height of 250 pixels. -So instead of having the line

- $graph =& new PHPlot();

-we replace it with

- $graph =& new PHPlot(300,250);

-and you have specified the size in pixels of the image to be created. -A couple of things to note: -

-
    -
  • The default is not to use TTF fonts.
  • -
  • Since there was only one graph on the image we didn't have to - specify PrintImage, DrawGraph took care of it for us. -
  • -
  • - We did not specify the data type. If you do not specify the data - type PHPlot assumes text-data. -
  • -
  • - We did not specify the file type (gif, png, jpg, ...) . - PHPlot 5.0 assumes PNG image formats. -
  • -
  • - The data is passed in as an array of arrays. This may seem awkward - now, but as we add functionality this will be beneficial. -
  • -
-

Ok, now we're ready to add some customization to the plot. Let's change -the size, the title and the x/y axis labels. All we need to do is modify -the variable $graph before printing the image. We achieve this with: -

-
-
<?php
-include ('./phplot.php');
-
-//create an graph object 300x250 pixels
-$graph =& new PHPlot(300,250);
-//Set titles
-$graph->SetTitle("Title\n\rSubtitle");
-$graph->SetXTitle('X data');
-$graph->SetYTitle('Y data');
-
-//...rest of the code
-
-
-?>
-
-
-
- Figure 2 -
- -

Note that in order for the "\n" and "\r " to be interpreted as -new line/new return characters for SetTitle you have to -enclose the string in double quotes.

- -

Case 2: Multiple Lines per Graph

-

Lets say we want to plot not just one -dataset but several y values for each x position. With PHPlot it is -easy to specify the multiple data lines by just passing in all the Y -values for a given X value at once. So instead of array('label', y) -we specify array('label', y1, y2, y3, -...) This is very convenient when working with rows of data from databases. -
-Now our data will have three Y values for each position on the X axis -

-
-
<?php
-//Include the code
-include('./phplot.php');
-
-//Define the object
-$graph =& new PHPlot(300,250);
-
-//Set titles
-$graph->SetTitle("Title\n\rSubtitle");
-$graph->SetXTitle('X data');
-$graph->SetYTitle('Y data');
-
-
-//Define some data
-$example_data = array(
-     array('a',3,4,2),
-     array('b',5,'',1),  // here we have a missing data point, that's ok
-     array('c',7,2,6),
-     array('d',8,1,4),
-     array('e',2,4,6),
-     array('f',6,4,5),
-     array('g',7,2,3)
-);
-$graph->SetDataValues($example_data);
-
-//Draw it
-$graph->DrawGraph();
-?>
-
-

Which gives us:

-
- -
Figure 3 -
- -

Notice that each set of Y data gets a different color. -Also the missing data point is skipped, this behaviour can be adjusted with -SetDrawBrokenLines(TRUE); -

-

-This gives you the basics of how to create a graph in PHPlot. -A nice start, but now we'd like to add some customization, namely different -fonts, margins and types of graphs. -

-

Back to top

- - -

Customization

-

Valid types of plots (as of PHPlot 5.0): -

    -
  • bars (with optional shadows)
  • -
  • lines
  • -
  • linepoints (a faster way of plotting when - you want both points and lines)
  • -
  • area
  • -
  • points (lots of point types here)
  • -
  • pie (2D or 3D)
  • -
  • thinbarline (sometimes also called impulse)
  • -
  • error bar (which can also be used for stock market data graphs)
  • -
  • squared (for binary data)
  • -
-

-

You specify which type with the SetPlotType function. -We'll look at that function with bars and lines in the next example when we look at -multiple graphs per image. -

-

As we discussed before, there are several ways we can manipulate -the look/feel of the graph object. Almost every parameter of ticks, grids and data labels -can be adjusted via (among many others): -

    -
  • SetXTickPos()
  • -
  • SetYTickPos()
  • -
  • SetXTickLength()
  • -
  • SetYTickLength()
  • -
  • SetXTickCrossing()
  • -
  • SetYTickCrossing()
  • -
  • SetXTickIncrement()
  • -
  • SetYTickIncrement()
  • -
  • SetNumXTicks()
  • -
  • SetNumYticks()
  • -
  • SetSkipTopTick()
  • -
  • SetSkipBottomTick()
  • - -
  • SetDrawXGrid()
  • -
  • SetDrawYGrid()
  • -
  • SetDrawDashedGrid()
  • -
  • SetDrawXDataLabelLines()
  • -
  • SetDrawYDataLabelLines() (not yet implemented)
  • -
  • SetXDataLabelPos()
  • -
  • SetYDataLabelPos()
  • -
  • SetXLabelAngle()
  • -
  • SetYLabelAngle()
  • -
  • SetXLabelType()
  • -
  • SetYLabelType()
  • -
-As we go further we will introduce some of these features of PHPlot. -For more specialized examples, please go back to the index and -look in the examples section. -

- -

Back to top

- -

Case 3: Multiple Graphs per Image

- -

To create an image with several separate graphs -on it is a straightforward process. As in the previous examples we -first have to create an object (e.g. variable) but now we tell it to -not print the image at the same time as the draw command. Now -we want it to wait for the explicit PrintImage function call. -To tell PHPlot this is the way we want to work, we use the -SetPrintImage function. -SetPrintImage(TRUE) is the default, and tells to draw the image -when DrawGraph is called. To turn this -off we use SetPrintImage(FALSE).

-

Now we will draw several images entirely within one object. That -means that if we set a value for one graph, there will be a couple of -other commands we will need. -

-

To specify in pixels the placement of each graph we use -SetNewPlotAreaPixels. The format is -SetNewPlotAreaPixels(upper_left_x, upper_left_y, lower_right_x, -lower_right_y) . Again we are using the GD coordinates where 0,0 -is the upper left corner of the image. -

-

In more detail:

-
-
<?php
-include('./PHPlot.php');  // here we include the PHPlot code
-$graph =& new PHPlot(400,250);   // here we define the variable $graph
-$graph->SetPrintImage(0); //Don't draw the image yet
-
-//....Data and Values for first graph here .....
-
-$graph->SetNewPlotAreaPixels(70,10,375,100);  // where to place it
-
-$graph->DrawGraph();   //Draw the first graph to the image.
-
-//....Data and Values for second graph here .....
-
-$graph->SetNewPlotAreaPixels(70,120,375,220); //where to place graph 2
-$graph->DrawGraph();  //Draw the second graph to the image
-
-//Print the image with both graphs
-$graph->PrintImage();
-?>
-
- -

Lets now create an image with 2 graphs on it with some example data.

- -
-
<?php
-//Include the code
-include('./phplot.php');
-
-//Define the object
-$graph =& new PHPlot(400,250);
-
-$graph->SetPrintImage(0); //Don't draw the image until specified explicitly
-
-$example_data = array(
-     array('a',3),
-     array('b',5),
-     array('c',7),
-     array('d',8),
-     array('e',2),
-     array('f',6),
-     array('g',7)
-);
-
-$graph->SetDataType("text-data");  //Must be called before SetDataValues
-
-$graph->SetDataValues($example_data);
-$graph->SetYTickIncrement(2);  //a smaller graph now - so we set a new tick increment
-
-$graph->SetXLabelAngle(90);
-$graph->SetXTitle("");
-$graph->SetYTitle("Price");
-$graph->SetPlotType("lines");
-$graph->SetLineWidth(1);
-
-$graph->SetNewPlotAreaPixels(70,10,375,100);  // where do we want the graph to go
-$graph->DrawGraph(); // remember, since we said not to draw yet, PHPlot
-                     // still needs a PrintImage command to write an image.
-
-
-//Now do the second chart on the same image
-unset($example_data);  //we are re-using $example_data (to save memory), but you don't have to
-$example_data = array(
-     array('a',30,40,20),
-     array('b',50,'',10),  // here we have a missing data point, that's ok
-     array('c',70,20,60),
-     array('d',80,10,40),
-     array('e',20,40,60),
-     array('f',60,40,50),
-     array('g',70,20,30)
-);
-
-$graph->SetDataType("text-data");  //Must be called before SetDataValues
-
-$graph->SetDataValues($example_data);
-
-$graph->SetXTitle("");
-$graph->SetYTitle("Verbal Cues");
-$graph->SetYTickIncrement(10);
-$graph->SetPlotType("bars");
-$graph->SetXLabelAngle(0);  //have to re-set as defined above
-
-$graph->SetNewPlotAreaPixels(70,120,375,220);
-$graph->SetPlotAreaWorld(0,0,7,80);
-$graph->DrawGraph();
-
-//Print the image
-$graph->PrintImage();
-?>
-
- -



Which gives us:

- -
-
- Figure 4
-
- -

-You must remember that world Coordinates are the XY coordinates relative to the -axis origin that can be drawn. Not the device (pixel) coordinates -which in GD are relative to the origin at the upper left -side of the image. -

- - -
$Id$
- - - diff --git a/gui/bacula-web/external_packages/phplot/doc/schema.html b/gui/bacula-web/external_packages/phplot/doc/schema.html deleted file mode 100644 index 458ede2772..0000000000 --- a/gui/bacula-web/external_packages/phplot/doc/schema.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - PHPlot schema - - - - - - -

The Drawing

-

-The drawing you can see here is an attempt to illustrate the positioning -of elements on the canvas of PHPlot graphs. I'm not specially good at it -so please don't complain. I (Miguel) -hope it will help somebody as it did with me when I first began browsing -through the 3000 lines of code Afan had written. -

-

If you are curious, for the drawing I used -Dia a gtk+ diagram creation -sofware which I find easy and suited for my purposes. -

schema
-

- -

The words

-

Creating a graph with PHPlot is easy, you just create an instance of -the class, set up a few options, push in the data and smile at your -new cute graphic. However here we'll proceed a little more step by step so -to show you how to get the most of PHPlot. Please go to -quickstart. -

- -

$Id$

- - diff --git a/gui/bacula-web/external_packages/phplot/doc/style.css b/gui/bacula-web/external_packages/phplot/doc/style.css deleted file mode 100644 index ff700e0f5e..0000000000 --- a/gui/bacula-web/external_packages/phplot/doc/style.css +++ /dev/null @@ -1,42 +0,0 @@ -/* $Id$ - * Style sheet for PHPlot - */ - - -.foot { - text-align:right; - font-style:italic; - font-size:smaller; - width:100%; - background:#CCCCCC; - color:#000000; - } - - -.nav { - text-align:center; - color:#DDDDDD; - background:#FFFFFF; - font-size:7px; - } - -.imgfoot { text-align:center; font-size:small; font-style:italic; } -.hdr { text-align:center; background: #DDDDDD; color:#000000; } -.bighdr { text-align:center; font-weight: bold; } - -h2 { border-bottom: 1px dashed; } -h3 { border-bottom: 1px dotted; margin-left:1em;} - -img { padding: 0.5em; } - -.box { - background-color: #cccccc; - color: #000000; - border:1px solid; - width:75%; - margin:2em; - font-size:smaller; - } - -pre { font-family: monospace, "white-space" } - diff --git a/gui/bacula-web/external_packages/phplot/doc/user_functions.html b/gui/bacula-web/external_packages/phplot/doc/user_functions.html deleted file mode 100644 index 9b529019f3..0000000000 --- a/gui/bacula-web/external_packages/phplot/doc/user_functions.html +++ /dev/null @@ -1,316 +0,0 @@ - - -PHPlot User Functions - - - -

PHPlot User Functions

- - - - -
- -

Important functions

- -

SetDataType($which_dt) -
User Function: PHPLOT can accept data in a number of different -formats. One main difference between PHPLOT and PGPLOT -is that the x-y or x-y-error data should be passed in -with the data grouped together in an array. -

-Asside: Why the author chose this data format: In PGPLOT -you would have the X-values in one array and the Y-values -in a second. This would sometimes lead to really strange -graphs if one array was accidentally one data element -short. Plus most of the data the author was using came in -from a database using a _get_row or _get_array (in php) -and one can just pass it straight in to PHPLOT.

- -Colors and border colors are set by SetDataColors. Why -not have colors as part of the same array? Because most -data applications are used to putting out raw data and not -raw data + color information.

- -So in PHPLOT the datalabel,x-value,y-value and error are -grouped together as a value array. Then the -entire set of points to be plotted is passed in as a data array -E.g. Data_array = array(value_array_1,value_array_2,....)

-See below for examples of the various data types:
- -

  • text-data: Data is displayed at equal spacing along the x-axis. Data -is passed in as a data array of value arrays. Each element of the data -array is a position on the x-axis. The first element of the value array is -the label for that x-axis position, every subsequent element of the value -array is a y-value. -Example data
    -
    -$data = array(
    -	array("label 1",1.1,2,3,4),
    -	array("label 2",2,3,4,5),
    -	array("label 3",5,6,7,8),
    -	array("label 4",10,12,13,14)
    -);
    -
    -Which will display data points at -(1,1.1), (1,2), (1,3), (1,4), (2,2), (2,3)....
    - -
  • -
  • data-data: - X-Y data is passed in like text-data except -that the second element of the value array is the position along the -x axis of the data point(s). One data element would be $data[] = (title,x,y1,y2,y3,y4,...) -or -
    -$data = array(
    -	array("label 1",1.1,2,3,4),
    -	array("label 2",2,3,4,5),
    -	array("label 3",5,6,7,8),
    -	array("label 4",10,12,13,14)
    -);
    -
    -Notice that this is the same as in the previous example except that -the x,y data points are at (1.1,2), (1.1,3), (1.1,4), (2,3)... -
  • -
  • data-data-error: -Again X-Y data is passed in as an array with each value -array being (data_label,x_position,y_position,error_plus,error_minus) -
  • - -

    SetDataValues($which_dv) -
    User Function: Passes the raw data values into the class variable -$this->data_values. This function needs to be called before any -image can be drawn. - -

    SetPlotType($which_pt) -
    User Function: Can be: bars, lines, linepoints, area, points, and pie - -

    SetErrorBarLineWidth($width) -
    Width of the Error Bars in Pixels. If not set then -uses "line_width" to set the width of the error_bar lines. - -

    SetFileFormat($which_file_format) -
    User Function: Set the format of the output graph. Supported formats -are GIF, JPEG, and PNG. You can only use those formats -that are supported by your version of GD. For example, -if you use GD-1.8.3 you can not use GIF images. If you -use GD-1.2 you can not use PNG or JPEG images. - -

    SetUseTTF($which_ttf) -
    User Function: Call this as SetUseTTF(1) when you have TTF compiled into PHP -otherwise call this as SetUseTTF(0) - - -
    - -

    Appearance functions

    - - -

    SetErrorBarShape($which_ebs) -
    User Function: Can be "tee" or "line." If it is tee, then -the half-width of the tee is set by SetErrorBarSize - -

    SetErrorBarSize($which_ebs) -
    User Function: Size in pixels of the Tee shape of the error bar. - -

    SetHorizTickIncrement($which_ti) -
    User Function: Set where to place the X-tick marks. - -

    SetNumHorizTicks($which_nt) -
    User Function: Use this or SetHorizTickIncrements but not both. - -

    SetNumVertTicks($which_nt) -
    User Function: Use this or SetVertTickIncrements but not both. - -

    SetPlotAreaPixels($x1,$y1,$x2,$y2) -
    User Function: You can use this to set the actual size in -pixels of the plot area on the image. - -

    SetPointShape($which_pt) -
    User Function: Can be: rect,circle,diamond,triangle,dot,line,halfline - -

    SetPointSize($which_ps) -
    User Function: Set the width of the displayed data point for -plot_type's that have data points. E.g. dots, linepoints -This is an integer. For diamonds, values that are even -make the diamond look better. - -

    SetPrecisionX($which_prec) -
    User Function: Set the precision of the data written to the X axis. -$which_prec is an integer which represents the number of digits -displayed to the right of the decimal. This uses the number_format -command in PHP. - -

    SetPrecisionY($which_prec) -
    User Function: Set the precision of the data written to the Y axis. -$which_prec is an integer which represents the number of digits -displayed to the right of the decimal. This uses the number_format -command in PHP. - -

    SetShading($which_s) -
    User Function: Set the length of the shadows for shading bar charts -The color used is the LightGridColor See SetLightGridColor.
    - -

    SetTickLength($which_tl) -
    User Function: Set the length of the tick in pixels for the x axis and y axis. -$which_color can be either a name like "black" or an rgb color array -array(int,int,int).
    - - -

    SetTitle($title) -
    User Function: Set the title of the graph. Printed at the top -middle of the graph. - -

    SetHorizTickIncrement($which_ti) -
    User Function: Set the distance between tick marks on the X axis. $which_ti -is in world coordinates. - -

    SetVertTickIncrement($which_ti) -
    User Function: Set the distance between tick marks on the Y axis. $which_ti -is in world coordinates. - -

    SetXDatalabelMaxlength($which_xdlm) -
    User Function: Sets the maximum length of the datalabels on the x-axis. -This adjusts the margins if the angle of the labels is not 0. - -

    - -

    SetXGridLabelType($which_xtf) -
    User Function: Can be "time", "title", "none", "default" or "data".
    -time - label is set by the the php command strftime()
    -title - label is treated as text, the first element of the data array.
    -data - label is formateed using php command number_format
    -none - no labelss are printed.
    -default - prints as it is entered.
    -

    - - -

    SetXScaleType($which_xct) -
    Can be "log" or "linear". - -

    SetXTimeFormat($which_xtf) -
    User Function: Used for x_grid_label_type="time". -For the format see the php manual for strftime(). It assumes the x-data values are unix timestamps, and displays them according to the passed format string. -

    Example: If x values are seconds offset from the beginning of the day. This coresponds to unix timestamps on January 1, 1970, so all I had to do was $graph->SetXGridLabelType("%H:%M") to display the time properly. -

    -The php strtotime() function also comes in handy turning dates into timestamps, especially for parameters to SetPlotAreaWorld(). -

    -Example:
    -$graph->SetPlotAreaWorld(strtotime("October 1"), 0,strtotime("December 15"),10);
    -$graph->SetXGridLabelType("time") ;
    -$graph->SetXTimeFormat("%b %d") ;
    - -

    SetXLabel($xlbl) -
    User Function: Set the label for the X axis. - -

    SetYLabel($ylbl) -
    User Function: Set the label for the Y axis. - -

    SetYScaleType($which_xct) -
    Can be "log" or "linear". - - -
    - -

    Color functions

    - -

    SetBackgroundColor($which_color) -
    User Function: Set the color of the background of the entire image. -$which_color can be either a name like "black" or an rgb color array -array(int,int,int).
    It defaults to array(222,222,222) if -not defined.
    - -

    SetGridColor ($which_color) -
    User Function: Set the color of the grid. Defaults to "black" -$which_color can be either a name like "black" or an rgb color array -array(int,int,int).
    - -

    SetLegend($which_legend) -
    $which_legend is an array of text for display in a small box -on the image. If you do not set the position with SetLegendPixels() -then it puts it in the upper right hand side. - -

    SetLegendPixels($which_x,$which_y,$which_type) -
    Pick the upper left corner of the legend box with -$which_x and $which_y in pixels. $which_type is reserved -for future use. - -

    SetLegendWorld($which_x,$which_y,$which_type) -
    Untested and documentation not written. Have Fun! - -

    SetLightGridColor ($which_color) -
    User Function: There are two grid colors, this sets the -light grid color. -$which_color can be either a name like "black" or an rgb color array -array(int,int,int).
    It defaults to array(222,222,222) if -not defined.
    - -

    SetLineWidth($which_lt) -
    User Function: Set the width of lines in pixels for graphs that use -lines (linepoints and lines). Affects the size of the -error bar lines as well. - -

    SetLineStyles($which_sls) -
    User Function: Set style of the line to be printed. -This is an array. Currently only 'dashed' and 'solid' are -supported. - -

    SetPlotBgColor($which_color) -
    User Function: Set the Background color of the area on which -the plot is defined. Called from PlotAreaBackground
    -$which_color can be either a name like "black" or an rgb color array -array(int,int,int).
    It defaults to array(222,222,222) if -not defined. - -

    SetTextColor ($which_color) -
    User Function: Set the color of text written. It defaults to black if not called. -You can call this function multiple times - each time it changes the -color of text written. -$which_color can be either a name like "black" or an rgb color array -array(int,int,int).
    - -

    SetTickColor ($which_color) -
    User Function: Set the color of the ticks on the axes -$which_color can be either a name like "black" or an rgb color array -array(int,int,int).
    - -

    SetTitleColor($which_color) -
    User Function: Set the color of the title. -$which_color can be either a name like "black" or an rgb color array -array(int,int,int).
    - -
    - -

    Data manipulation functions

    - -

    These functions only are availabe in the class PHPlot_data that extends PHPlot. -Since all functions of that section do calculations on the data, it is neccessary -that data is supplied to the class by the SetDataValues() function before calling -any of the functions.

    - -

    DoScaleData($even, $show_in_legend) -
    User Function: Scales the data so that graphs with widely different amplitudes -can be plotted into one image. If $show_in_legend is true, the amplification factor -that is applied to each for the datasets is appended to the legend of the graph. If -$even is true, the function multiplies only by 10^x,2*10^x or 5*10^x.
    - -

    DoMovingAverage($datarow, $interval, $show_in_legend) -
    User Function: Computes a moving average over an amount of $interval units -on the data row that is indexed by $datarow. If $show_in_legend is true, a notice -that a data row is averaged and the length of the interval are appended to the legend. -
    - -
    - -

    not implemented

    - -

    SetCharacterHeight() -
    User Function: Not yet implemented - - - diff --git a/gui/bacula-web/external_packages/phplot/doc/user_internal_functions.html b/gui/bacula-web/external_packages/phplot/doc/user_internal_functions.html deleted file mode 100644 index bb2346322d..0000000000 --- a/gui/bacula-web/external_packages/phplot/doc/user_internal_functions.html +++ /dev/null @@ -1,62 +0,0 @@ - - -PHPlot User / Internal Functions - - - -

    PHPlot User / Internal Functions

    - - -

    DrawDataLabel($lab,$x_world,$y_world) -
    User Function and Internal Function: Draw a label at the world coordinates of x_world and y_world. - - -

    DrawDot($x_world,$y_world,$dot_type,$color) -
    User Function and Internal Function: Draw one dot at $x_world and $y_world where the world -coordinates (not pixel coordinates). - - -

    DrawXDataLabel($xlab,$xpos) -
    User Function and Internal Function: Draw a data label on the x axis at world -position $xpos. The position below -the x axis is set based on the size of the font, and (if -using TTF) the maximum string length of the labels, and -the angle at which the labels are printed. - - -

    SetDataColors($which_data,$which_border) -
    If called with $which_data="" it defaults to - $which_data = array("blue","bisque",array(0,176,0)); - $which_border = array("black"); - - -

    SetErrorBarColors($which_data) -
    If not called then the colors are the same as the -colors of the data as set by SetDataColors() -otherwise it sets the colors of the error bars. - -

    SetLabels($xlbl,$ylbl,$title) -
    Internal Function and User Function: Set the text of the X, Y and Title labels. -If you want to just set the X-axis label you can use SetXLabel. - - -

    SetNewPlotAreaPixels($x1,$y1,$x2,$y2) -
    Reserved - not used yet. - - - -

    SetPlotAreaWorld($xmin,$ymin,$xmax,$ymax) -
    User Function and Internal Function: You do not have to call this function as the -program will set the plot area automatically based on -max values of X and Y.
    You can call this function -as SetPlotAreaWorld("","","","") and it will set the -plot area based on max/min values of data. - - - - - - - - - diff --git a/gui/bacula-web/external_packages/phplot/examples/benjamingothic.ttf b/gui/bacula-web/external_packages/phplot/examples/benjamingothic.ttf deleted file mode 100644 index 61058a6e21..0000000000 Binary files a/gui/bacula-web/external_packages/phplot/examples/benjamingothic.ttf and /dev/null differ diff --git a/gui/bacula-web/external_packages/phplot/examples/create_chart.php b/gui/bacula-web/external_packages/phplot/examples/create_chart.php deleted file mode 100644 index 37e43cb07c..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/create_chart.php +++ /dev/null @@ -1,167 +0,0 @@ -This file is intended to be called from format_chart.php, the example-o-matic.". - "Please click here to try it.

    "; - exit; -} -*/ - -extract ($_GET, EXTR_OVERWRITE); -extract ($_POST, EXTR_OVERWRITE); - - -//Sample functions - -// data-data as a function -if ($which_data_type == 'function') { - //Put function here - $dx = ".3"; - $max = 6.4; - $maxi = $max/$dx; - for ($i=0; $i<$maxi; $i++) { - $a = 4; - $x = $dx*$i; - $data[$i] = array("", $x, $a*sin($x),$a*cos($x),$a*cos($x+1)); - } - $which_data_type = "data-data"; -} -// data-data-error as a random function -else if ($which_data_type == 'randfunction') { - srand ((double) microtime() * 1000000); - $a = 9.62; - $label[0] = "October"; $label[5] = "Day 5"; $label[10] = "Day 10"; - $label[15] = "Day 15"; $label[20] = "Day 20"; $label[25] = "Day 25"; - - for ($i = 0; $i <= 30; $i++) { - $a += rand(-1, 2); - $b = rand(0,1); - $c = rand(0,1); - $data[] = @ array($label[$i],$i+1,$a,$b,$c); - } - $which_data_type = 'data-data-error'; -} -// MBD, this is for data_sample3.php, $num_data_rows is set there -else if ($which_data_type == 'data-data-error') { - for ($i = 0; $i < $num_data_rows; $i++) { - eval ("\$data[\$i] = \$data_row$i; "); - } -} else { - foreach($data_row0 as $key=>$val) { - $data[$key] = array($data_row0[$key],$data_row1[$key],$data_row2[$key],$data_row3[$key],$data_row4[$key]); - } -} - - -//////////////////////////////////////////////// - -//Required Settings - include("../phplot.php"); - $graph = new PHPlot($xsize_in, $ysize_in); - $graph->SetDataType($which_data_type); // Must be first thing - - $graph->SetDataValues($data); - -//Optional Settings (Don't need them) - -// $graph->SetTitle("This is a\n\rmultiple line title\n\rspanning three lines."); - $graph->SetTitle($title); - $graph->SetXTitle($xlbl, $which_xtitle_pos); - $graph->SetYTitle($ylbl, $which_ytitle_pos); - $graph->SetLegend(array("A","Bee","Cee","Dee")); - - $graph->SetFileFormat($which_fileformat); - $graph->SetPlotType($which_plot_type); - - $graph->SetUseTTF($which_use_ttf); - - $graph->SetYTickIncrement($which_yti); - $graph->SetXTickIncrement($which_xti); - $graph->SetXTickLength($which_xtl); - $graph->SetYTickLength($which_ytl); - $graph->SetXTickCrossing($which_xtc); - $graph->SetYTickCrossing($which_ytc); - $graph->SetXTickPos($which_xtick_pos); - $graph->SetYTickPos($which_ytick_pos); - - - $graph->SetShading($which_shading); - $graph->SetLineWidth($which_line_width); - $graph->SetErrorBarLineWidth($which_errorbar_line_width); - - $graph->SetDrawDashedGrid($which_dashed_grid); - switch($which_draw_grid) { - case 'x': - $graph->SetDrawXGrid(TRUE); - $graph->SetDrawYGrid(FALSE); - break; - case 'y': - $graph->SetDrawXGrid(FALSE); - $graph->SetDrawYGrid(TRUE); - break; - case 'both': - $graph->SetDrawXGrid(TRUE); - $graph->SetDrawYGrid(TRUE); - break; - case 'none': - $graph->SetDrawXGrid(FALSE); - $graph->SetDrawYGrid(FALSE); - } - - $graph->SetXTickLabelPos($which_xtick_label_pos); - $graph->SetYTickLabelPos($which_ytick_label_pos); - $graph->SetXDataLabelPos($which_xdata_label_pos); - $graph->SetYDataLabelPos($which_ydata_label_pos); - - // Please remember that angles other than 90 are taken as 0 when working fith fixed fonts. - $graph->SetXLabelAngle($which_xlabel_angle); - $graph->SetYLabelAngle($which_ylabel_angle); - - //$graph->SetLineStyles(array("dashed","dashed","solid","solid")); - $graph->SetPointShape($which_point); - $graph->SetPointSize($which_point_size); - $graph->SetDrawBrokenLines($which_broken); - - // Some forms in format_chart.php don't set this variable, suppress errors. - @ $graph->SetErrorBarShape($which_error_type); - - $graph->SetXAxisPosition($which_xap); - $graph->SetYAxisPosition($which_yap); - $graph->SetPlotBorderType($which_btype); - - if ($maxy_in) { - if ($which_data_type = "text-data") { - $graph->SetPlotAreaWorld(0,$miny_in,count($data),$maxy_in); - } - } - -/* -//Even more settings - - $graph->SetPlotAreaWorld(0,100,5.5,1000); - $graph->SetPlotAreaWorld(0,-10,6,35); - $graph->SetPlotAreaPixels(150,50,600,400); - - $graph->SetDataColors( - array("blue","green","yellow","red"), //Data Colors - array("black") //Border Colors - ); - - $graph->SetPlotBgColor(array(222,222,222)); - $graph->SetBackgroundColor(array(200,222,222)); //can use rgb values or "name" values - $graph->SetTextColor("black"); - $graph->SetGridColor("black"); - $graph->SetLightGridColor(array(175,175,175)); - $graph->SetTickColor("black"); - $graph->SetTitleColor(array(0,0,0)); // Can be array or name -*/ - -// $graph->SetPrintImage(false); - $graph->DrawGraph(); -// xdebug_dump_function_profile(XDEBUG_PROFILER_FS_SUM); -?> diff --git a/gui/bacula-web/external_packages/phplot/examples/data.php b/gui/bacula-web/external_packages/phplot/examples/data.php deleted file mode 100644 index b1d1314db9..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/data.php +++ /dev/null @@ -1,27 +0,0 @@ - diff --git a/gui/bacula-web/external_packages/phplot/examples/data_date.php b/gui/bacula-web/external_packages/phplot/examples/data_date.php deleted file mode 100644 index 4a14d7b5cf..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/data_date.php +++ /dev/null @@ -1,267 +0,0 @@ - diff --git a/gui/bacula-web/external_packages/phplot/examples/data_date2.php b/gui/bacula-web/external_packages/phplot/examples/data_date2.php deleted file mode 100644 index 37152c7c6f..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/data_date2.php +++ /dev/null @@ -1,267 +0,0 @@ - diff --git a/gui/bacula-web/external_packages/phplot/examples/data_sample1.php b/gui/bacula-web/external_packages/phplot/examples/data_sample1.php deleted file mode 100644 index 11b8117c1e..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/data_sample1.php +++ /dev/null @@ -1,58 +0,0 @@ -

    -Data type: (Text-data)
    -

    - - - - - - - - - - - - - -
    Title (x axis)Y data 1Y data 2Y data 3 Y data 4
    - - - - - - - - - -
    - -

    -Graph type: - -

    -
    -Please note when writing your application that the graph
    -types marked with an asterisk only support the data
    -type for this form, "text-data". -
    diff --git a/gui/bacula-web/external_packages/phplot/examples/data_sample2.php b/gui/bacula-web/external_packages/phplot/examples/data_sample2.php deleted file mode 100644 index e754a039d3..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/data_sample2.php +++ /dev/null @@ -1,52 +0,0 @@ - -

    -Data type: (data-data)
    - - - - - - - - - - - - - - -
    Title (data label)X dataY data 1Y data 2Y data 3
    - - - - - - - - - -
    - -

    -Graph type: - -

    diff --git a/gui/bacula-web/external_packages/phplot/examples/data_sample3.php b/gui/bacula-web/external_packages/phplot/examples/data_sample3.php deleted file mode 100644 index 46f774b56d..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/data_sample3.php +++ /dev/null @@ -1,58 +0,0 @@ -

    -Data set as X, Y, E+, E-, Y2, E2+, E2-,...
    - -Data type: (data-data-error) -

    - - - - - - - - -
    Title (data label)X dataY data 1Error +Error -Y data 2Error +Error -
    - - "; - - for ($i = 0; $i < $num_rows; $i++) { - // The label input element must be bigger. - $lines[0] = "\n"; - - // Show s for the rest of the columns - for ($j=1; $j<8; $j++) - $lines[$j] = "\n"; - $groups[$i] = join('', $lines); - } - echo join("
    \n", $groups); - ?> - -
    - -

    -Graph type: - -     -Error bar type: - -

    diff --git a/gui/bacula-web/external_packages/phplot/examples/data_sample4.php b/gui/bacula-web/external_packages/phplot/examples/data_sample4.php deleted file mode 100644 index 5c4d14c16f..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/data_sample4.php +++ /dev/null @@ -1,24 +0,0 @@ -

    -Data type: data-data calculated from a function. -

    -
    -	$dx = ".3";
    -	$max = 6.4;
    -	$maxi = $max/$dx;
    -	for ($i=0; $i<$maxi; $i++) {
    -		$a = 4;
    -		$x = $dx*$i;
    -		$data[$i] = array("", $x, $a*sin($x), 
    -                              $a*cos($x), $a*cos($x+1)); 	
    -	}
    -
    -

    -Chart type: - - -

    diff --git a/gui/bacula-web/external_packages/phplot/examples/data_sample5.php b/gui/bacula-web/external_packages/phplot/examples/data_sample5.php deleted file mode 100644 index b8859b6345..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/data_sample5.php +++ /dev/null @@ -1,31 +0,0 @@ -

    -Data type: data-data-error calculated from a function. -

    -
    -    srand ((double) microtime() * 1000000);
    -    $a = 9.62;
    -    $label[0] = "October"; $label[5] = "Day 5"; $label[10] = "Day 10";
    -    $label[15] = "Day 15"; $label[20] = "Day 20"; $label[25] = "Day 25";
    -
    -    for ($i = 0; $i <= 30; $i++) {
    -        $a += rand(-1, 2);
    -        $b = rand(0,1);
    -        $c = rand(0,1);
    -        $data[] = @ array($label[$i],$i+1,$a,$b,$c);
    -    }
    -
    -

    -Chart type: - -     -Error bar type: - - -

    diff --git a/gui/bacula-web/external_packages/phplot/examples/example1.php b/gui/bacula-web/external_packages/phplot/examples/example1.php deleted file mode 100644 index a134097785..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/example1.php +++ /dev/null @@ -1,16 +0,0 @@ -SetDataValues($example_data); - - -//Draw it -$graph->DrawGraph(); - -?> diff --git a/gui/bacula-web/external_packages/phplot/examples/example2.php b/gui/bacula-web/external_packages/phplot/examples/example2.php deleted file mode 100644 index 8f06a20dba..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/example2.php +++ /dev/null @@ -1,22 +0,0 @@ -SetDataType("linear-linear"); - -//Remove the X data labels -//$graph->SetXGridLabelType("none"); - -//Load the data into data array -$graph->SetDataValues($example_data); - -//Draw the graph -$graph->DrawGraph(); -?> diff --git a/gui/bacula-web/external_packages/phplot/examples/example3.php b/gui/bacula-web/external_packages/phplot/examples/example3.php deleted file mode 100644 index a518e1315e..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/example3.php +++ /dev/null @@ -1,32 +0,0 @@ -SetDataType("data-data"); - -//Load the data into the data array -$graph->SetDataValues($example_data); -$graph->DoMovingAverage(4,2,TRUE); - -//Call Scaling Function (in phplot_data.php) -//$graph->DoScaleData(1,1); - -//Draw a Legend at pixel location 100,100 -$graph->SetLegendPixels(100,100,""); - -//have no labels on Y axis -//$graph->SetYGridLabelType("none"); - -//Print that puppy! -$graph->DrawGraph(); -?> diff --git a/gui/bacula-web/external_packages/phplot/examples/example4.php b/gui/bacula-web/external_packages/phplot/examples/example4.php deleted file mode 100644 index 360d2df8aa..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/example4.php +++ /dev/null @@ -1,80 +0,0 @@ -SetDataType("linear-linear-error"); //Must be first thing - -//Set the Graph particulars - $graph->SetPrecisionX(0); - $graph->SetPrecisionY(0); - $graph->SetUseTTF("0"); - $graph->SetDrawYGrid("1"); // 1 = true - $graph->SetDataValues($data); - $graph->SetImageArea(600, 400); - $graph->SetVertTickIncrement(""); - $graph->SetHorizTickIncrement(1); - $graph->SetErrorBarLineWidth(1); - $graph->SetYScaleType("log"); - $graph->SetPointShape("halfline"); - $graph->SetErrorBarShape("line"); - $graph->SetPlotType("points"); - $graph->SetXGridLabelType("title"); - $graph->SetXLabel("Day"); - $graph->SetYLabel("Stock value"); - $graph->SetTitle("Logarithmic scale\nexample 4"); - //$graph->SetErrorBarColors(array("blue","red","green","black")); - $graph->SetDataColors( - array("blue","green","yellow","red"), //Data Colors - array("black") //Border Colors - ); - - $graph->SetPlotAreaWorld(0,0,32,50); - //$graph->SetPlotAreaPixels(150,50,600,400); - -/* -//Other settings - $graph->SetPlotBgColor(array(222,222,222)); - $graph->SetBackgroundColor(array(200,222,222)); //can use rgb values or "name" values - $graph->SetTextColor("black"); - $graph->SetGridColor("black"); - $graph->SetLightGridColor(array(175,175,175)); - $graph->SetTickColor("black"); - $graph->SetTitleColor(array(0,0,0)); // Can be array or name - -*/ - - -//Draw the graph - $graph->DrawGraph(); -?> diff --git a/gui/bacula-web/external_packages/phplot/examples/example6.php b/gui/bacula-web/external_packages/phplot/examples/example6.php deleted file mode 100644 index 405943b297..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/example6.php +++ /dev/null @@ -1,19 +0,0 @@ -SetDataType("data-data"); //Must be called before SetDataValues - -$graph->SetXGridLabelType("time"); -$graph->SetXDataLabelAngle(90); -$graph->SetXLabel(""); -$graph->SetYLabel("Volume"); -$graph->SetVertTickIncrement(30); -$graph->SetXTimeFormat("%b %y"); -$graph->SetDataValues($example_data); -$graph->SetHorizTickIncrement(2679000); -$graph->SetPlotType("thinbarline"); -$graph->SetDrawXDataLabels(false); -$graph->SetPlotAreaWorld(883634400,0,915095000,90); -$graph->DrawGraph(); -?> diff --git a/gui/bacula-web/external_packages/phplot/examples/example7.php b/gui/bacula-web/external_packages/phplot/examples/example7.php deleted file mode 100644 index 4b93ee8df2..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/example7.php +++ /dev/null @@ -1,33 +0,0 @@ -SetDataType("data-data-error"); //Must be called before SetDataValues - -$graph->SetImageArea(600,400); -$graph->SetPrecisionY(0); -$graph->SetXLabel(""); -$graph->SetYLabel("Volume"); -$graph->SetVertTickIncrement(20); -$graph->SetXAxisPosition(1); -//$graph->SetSkipBottomTick(1); - -//Set Unixtime Increment and X Axis Settings -$graph->SetHorizTickIncrement(2679000); -$graph->SetXGridLabelType("time"); -$graph->SetXTimeFormat("%b %y"); -$graph->SetXDataLabelAngle(90); - -$graph->SetDataValues($example_data); -$graph->SetPlotType("lines"); -$graph->SetErrorBarShape("line"); -$graph->SetPointShape("halfline"); -$graph->SetYScaleType("log"); -$graph->SetLineWidth(1); -$graph->SetDrawXDataLabels(false); - -//Since X axis is in Unixtime format we set the limits accordingly -$graph->SetPlotAreaWorld(883634400,1,915095000,140); - -$graph->DrawGraph(); -?> diff --git a/gui/bacula-web/external_packages/phplot/examples/example8.php b/gui/bacula-web/external_packages/phplot/examples/example8.php deleted file mode 100644 index d60c053957..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/example8.php +++ /dev/null @@ -1,61 +0,0 @@ -SetPrintImage(0); //Don't draw the image yet - -$graph->SetDataType("data-data-error"); //Must be called before SetDataValues - -$graph->SetNewPlotAreaPixels(90,40,540,190); -$graph->SetDataValues($example_data); - -$graph->SetXLabelType("time"); -$graph->SetXLabelAngle(90); -$graph->SetXTitle(""); -$graph->SetYTitle("Price"); -$graph->SetYTickIncrement(20); -$graph->SetXTickIncrement(2679000); -$graph->SetXTimeFormat("%b %y"); -$graph->SetPlotType("lines"); -$graph->SetErrorBarShape("line"); -$graph->SetPointShape("halfline"); -$graph->SetYScaleType("log"); -$graph->SetLineWidths(array(1)); -$graph->SetPlotAreaWorld(883634400,1,915095000,140); -$graph->SetXDataLabelPos('none'); -$graph->DrawGraph(); - -//Now do the second chart on the image - -unset($example_data); - -$graph->SetYScaleType("linear"); -include("./data_date.php"); - -$graph->SetDataType("data-data"); //Must be called before SetDataValues - -$graph->SetDataValues($example_data); -$graph->SetNewPlotAreaPixels(90,260,540,350); -$graph->SetDataValues($example_data); - -$graph->SetXLabelType("time"); -$graph->SetXLabelAngle(90); -$graph->SetXTitle(""); -$graph->SetYTitle("Volume"); -$graph->SetYTickIncrement(30); -$graph->SetPlotType("thinbarline"); - -//Set how to display the x-axis ticks -$graph->SetXTimeFormat("%b %y"); -$graph->SetXTickIncrement(2679000); -$graph->SetXAxisPosition(0); //Have to reset it after log plots - -//Set Plot to go from x = Jan 1 1998, to x = Dec 31 1998 -// and from y = 0 to y = 90 -$graph->SetPlotAreaWorld(883634400,0,915095000,90); - -$graph->DrawGraph(); - -//Print the image -$graph->PrintImage(); -?> diff --git a/gui/bacula-web/external_packages/phplot/examples/example9.php b/gui/bacula-web/external_packages/phplot/examples/example9.php deleted file mode 100644 index 32865c1526..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/example9.php +++ /dev/null @@ -1,35 +0,0 @@ -SetDataValues($example_data); - -//Don't print until we say so -$graph->SetPrintImage(0); - -//Draw it -$graph->DrawGraph(); - -//Define some colors -$ndx_color = $graph->SetIndexColor("blue"); -$ndx_color1 = $graph->SetIndexColor("orange"); - -//The image data colors are now ndx_data_color[] -$graph->DrawDashedLine(250,$graph->plot_area[1],250,250,4,0,$graph->ndx_data_colors[0]); - -$graph->DrawDashedLine($graph->xtr(5),$graph->ytr(12), - $graph->xtr(20),$graph->ytr(42),5,3,$ndx_color); - -$graph->DrawDashedLine($graph->plot_area[0],250,$graph->plot_area[2],250,2,0,$ndx_color1); -$graph->DrawDashedLine($graph->plot_area[0],251,$graph->plot_area[2],251,2,0,$ndx_color1); - - -//Now print the image -$graph->PrintImage(); - -?> diff --git a/gui/bacula-web/external_packages/phplot/examples/format_chart.php b/gui/bacula-web/external_packages/phplot/examples/format_chart.php deleted file mode 100644 index 5e2f4b899c..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/format_chart.php +++ /dev/null @@ -1,345 +0,0 @@ - - - - Example-o-matic - - - - - - -

    PHPlot test graph form

    - -

    Use this form to test many different options of PHPlot. You can test -every graph type supported for any of four different data types. You can -tweak as you like or you can leave everything as is and press "Submit" for -automatic values. -

    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Data Settings
    -

    - Data type: [ - text-data | - data-data | - function | - data-data-error | - randfunction ] -

    - -
    -
    -

    Optional values

    -
    -
    Sizes
    Width of graph in pixels:
    Height of graph in pixels:
    Maximum height of graph in y axis units:
    Minimum height of graph in y axis units:
    Titles and data labels
    Title:
    Y axis title:
    Y axis title position: - -
    Y axis data labels position: - -
    Y axis labels angle:
    X axis title:
    X axis title position: - -
    X axis data labels position: - -
    X axis labels angle:
    Grid and ticks
    Grid drawn: - -
    Dashed grid? - -
    X axis ticks length:
    X axis ticks crossing:
    X axis ticks position: - -
    X axis tick labels position: - -
    Y axis ticks length:
    Y axis ticks crossing:
    Y axis ticks position: - -
    Y axis tick labels position: - -
    X tick increment:
    Y tick increment:
    Other
    X axis position:
    Y axis position:
    Plot Border: - -
    Shade height (0 for none):
    Plot line width:
    Error bar line width:
    Point Type: - -
    Point Size:
    Draw broken lines with missing Y data: - -
    Use TrueType font: - -
    File format: - -
    -
    -
    - -

    -Please visit PHPlot's site, the -sourceforge project page, -or see more php code and examples -by Afan Ottenheimer of jeonet. -

    - -

    $Id$

    - - diff --git a/gui/bacula-web/external_packages/phplot/examples/inline_image.php b/gui/bacula-web/external_packages/phplot/examples/inline_image.php deleted file mode 100644 index 16a5a352c5..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/inline_image.php +++ /dev/null @@ -1,28 +0,0 @@ - - ************************************************* - * This file is meant to be called only from the * - * test page * - * It will fail if called by itself. * - ************************************************* - -EOF -; -exit; -} - -// From PHP 4.?, register_globals is off, take it into account (MBD) - -include('../phplot.php'); -$graph = new PHPlot; -include('./data.php'); -$graph->SetTitle("$_GET[which_title]"); -$graph->SetDataValues($example_data); -$graph->SetIsInline('1'); -$graph->SetFileFormat("$_GET[which_format]"); -$graph->DrawGraph(); -?> diff --git a/gui/bacula-web/external_packages/phplot/examples/test_setup.php b/gui/bacula-web/external_packages/phplot/examples/test_setup.php deleted file mode 100644 index b8860ca978..0000000000 --- a/gui/bacula-web/external_packages/phplot/examples/test_setup.php +++ /dev/null @@ -1,67 +0,0 @@ - - - - - PHPlot graphic formats test - - - - - - -

    PHPlot test graph form

    - -

    Use this form to test many different options of PHPlot. You can test -every graph type supported for any of four different data types. You can -tweak as you like or you can leave everything as is and press "Submit" for -

    PHPlot graphic formats test

    - -

    -This page will test which graphic formats are supported by the version of GD -linked into PHP. You should see at least one of the four images below. -

    - -
    - - - - - - - - - -
    PNG graphics
    -"; - -?> -
    JPEG graphics
    -"; -?> -
    GIF graphics
    -"; -?> -
    BMP graphics
    -"; -?> -
    -
    - - - diff --git a/gui/bacula-web/external_packages/phplot/phplot.php b/gui/bacula-web/external_packages/phplot/phplot.php index 5ded19e5c3..c7f323c38d 100644 --- a/gui/bacula-web/external_packages/phplot/phplot.php +++ b/gui/bacula-web/external_packages/phplot/phplot.php @@ -1,462 +1,471 @@ + * 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. * - * Requires PHP 4.2.0 or later (CHECK THIS) + * 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 + * --------------------------------------------------------------------- + * + * Co-author and maintainer (2003-2005) + * Miguel de Benito Delgado + * + * Maintainer (2006-present) + * + * + * Requires PHP 5.2.x or later. (PHP 4 is unsupported as of Jan 2008) */ -if (! defined(__FUNCTION__)) - define(__FUNCTION__, '__FUNCTION__ Requires at least PHP 4.3.0.'); - -define ('MINY', -1); // Indexes in $data (for DrawXDataLine()) -define ('MAXY', -2); -define ('TOTY', -3); - -error_reporting(E_ALL); - -class PHPlot { - - /* I have removed internal variable declarations, some isset() checking was required, - * but now the variables left are those which can be tweaked by the user. This is intended to - * be the first step towards moving most of the Set...() methods into a subclass which will be - * used only when strictly necessary. Many users will be able to put default values here in the - * class and thus avoid memory overhead and reduce parsing times. +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. */ - //////////////// CONFIG PARAMETERS ////////////////////// - var $is_inline = FALSE; // FALSE = Sends headers, TRUE = sends just raw image data - var $browser_cache = FALSE; // FALSE = Sends headers for browser to not cache the image, - // (only if is_inline = FALSE also) + 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 - var $safe_margin = 5; // Extra margin used in several places. In pixels + public $safe_margin = 5; // Extra margin used in several places, in pixels - var $x_axis_position = ''; // Where to draw both axis (world coordinates), - var $y_axis_position = ''; // leave blank for X axis at 0 and Y axis at left of plot. + public $x_axis_position = ''; // X axis position in Y world coordinates, blank for default. + public $y_axis_position = ''; // Y axis position in X world coordinates, blank for default. - var $xscale_type = 'linear'; // linear, log - var $yscale_type = 'linear'; + public $xscale_type = 'linear'; // linear, log + public $yscale_type = 'linear'; //Fonts - var $use_ttf = FALSE; // Use True Type Fonts? - var $ttf_path = '.'; // Default path to look in for TT Fonts. - var $default_ttfont = 'benjamingothic.ttf'; - var $line_spacing = 4; // Pixels between lines. - - // Font angles: 0 or 90 degrees for fixed fonts, any for TTF - var $x_label_angle = 0; // For labels on X axis (tick and data) - var $y_label_angle = 0; // For labels on Y axis (tick and data) - var $x_title_angle = 0; // Don't change this if you don't want to screw things up! - var $y_title_angle = 90; // Nor this. - var $title_angle = 0; // Or this. + public $use_ttf = FALSE; // Use True Type Fonts by default? + public $ttf_path = '.'; // Default path to look in for TT Fonts. + // public $default_ttfont; // Initialized in GetDefaultTTFont + 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 - var $file_format = 'png'; - var $output_file = ''; // For output to a file instead of stdout + public $file_format = 'png'; + public $output_file = ''; // For output to a file instead of stdout //Data - var $data_type = 'text-data'; // text-data, data-data-error, data-data, text-data-single - var $plot_type= 'linepoints'; // bars, lines, linepoints, area, points, pie, thinbarline, squared - - var $label_scale_position = 0.5; // Shifts data labes in pie charts. 1 = top, 0 = bottom - var $group_frac_width = 0.7; // value from 0 to 1 = width of bar groups - var $bar_width_adjust = 1; // 1 = bars of normal width, must be > 0 + public $data_type = 'text-data'; // Structure of the data array + public $plot_type= 'linepoints'; // bars, lines, linepoints, area, points, pie, thinbarline, squared - var $y_precision = 1; - var $x_precision = 1; - - var $data_units_text = ''; // Units text for 'data' labels (i.e: '¤', '$', etc.) + 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 - var $title_txt = ''; - - var $x_title_txt = ''; - var $x_title_pos = 'plotdown'; // plotdown, plotup, both, none + public $title_txt = ''; - var $y_title_txt = ''; - var $y_title_pos = 'plotleft'; // plotleft, plotright, both, none + 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. (DONE) - // 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) (TODO) - // 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: Follow the grid, next to ticks in axis. + // Are drawn at grid drawing time, by DrawXTicks() and DrawYTicks() + // Data labels: Follow the data points, and can be placed on the axis or the plot (x/y) + // Are drawn at graph plotting time, by Draw*DataLabel(), called by DrawLines(), etc. + // DrawXDataLabel() also draws vertical lines to data points, depending on + // draw_x_data_label_lines. // Tick Labels - var $x_tick_label_pos = 'plotdown'; // plotdown, plotup, both, xaxis, none - var $y_tick_label_pos = 'plotleft'; // plotleft, plotright, both, yaxis, none - - // Data Labels: - var $x_data_label_pos = 'plotdown'; // plotdown, plotup, both, plot, all, none - var $y_data_label_pos = 'plotleft'; // plotleft, plotright, both, plot, all, none - - var $draw_x_data_label_lines = FALSE; // Draw a line from the data point to the axis? - var $draw_y_data_label_lines = FALSE; // TODO - - // Label types: (for tick, data and plot labels) - var $x_label_type = ''; // data, time. Leave blank for no formatting. - var $y_label_type = ''; // data, time. Leave blank for no formatting. - var $x_time_format = '%H:%m:%s'; // See http://www.php.net/manual/html/function.strftime.html - var $y_time_format = '%H:%m:%s'; // SetYTimeFormat() too... - - // Skipping labels - var $x_label_inc = 1; // Draw a label every this many (1 = all) (TODO) - var $y_label_inc = 1; - var $_x_label_cnt = 0; // internal count FIXME: work in progress - - // Legend - var $legend = ''; // An array with legend titles - var $legend_x_pos = ''; - var $legend_y_pos = ''; - + // 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 - var $x_tick_length = 5; // tick length in pixels for upper/lower axis - var $y_tick_length = 5; // tick length in pixels for left/right axis + 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 - var $x_tick_cross = 3; // ticks cross x axis this many pixels - var $y_tick_cross = 3; // ticks cross y axis this many pixels + public $x_tick_cross = 3; // ticks cross x axis this many pixels + public $y_tick_cross = 3; // ticks cross y axis this many pixels - var $x_tick_pos = 'plotdown'; // plotdown, plotup, both, xaxis, none - var $y_tick_pos = 'plotleft'; // plotright, plotleft, both, yaxis, none + public $x_tick_pos = 'plotdown'; // plotdown, plotup, both, xaxis, none + public $y_tick_pos = 'plotleft'; // plotright, plotleft, both, yaxis, none - var $num_x_ticks = ''; - var $num_y_ticks = ''; + public $num_x_ticks = ''; + public $num_y_ticks = ''; - var $x_tick_inc = ''; // Set num_x_ticks or x_tick_inc, not both. - var $y_tick_inc = ''; // Set num_y_ticks or y_tick_inc, not both. + 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. - var $skip_top_tick = FALSE; - var $skip_bottom_tick = FALSE; - var $skip_left_tick = FALSE; - var $skip_right_tick = FALSE; + public $skip_top_tick = FALSE; + public $skip_bottom_tick = FALSE; + public $skip_left_tick = FALSE; + public $skip_right_tick = FALSE; //Grid Formatting - var $draw_x_grid = FALSE; - var $draw_y_grid = TRUE; + // 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 - var $dashed_grid = TRUE; - var $grid_at_foreground = FALSE; // Chooses whether to draw the grid below or above the graph + 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) - var $color_array = 'small'; // 'small', 'large' or array (define your own colors) + public $color_array = 'small'; // 'small', 'large' or array (define your own colors) // See rgb.inc.php and SetRGBArray() - var $i_border = array(194, 194, 194); - var $plot_bg_color = 'white'; - var $bg_color = 'white'; - var $label_color = 'black'; - var $text_color = 'black'; - var $grid_color = 'black'; - var $light_grid_color = 'gray'; - var $tick_color = 'black'; - var $title_color = 'black'; - var $data_colors = array('SkyBlue', 'green', 'orange', 'blue', 'orange', 'red', 'violet', 'azure1'); - var $error_bar_colors = array('SkyBlue', 'green', 'orange', 'blue', 'orange', 'red', 'violet', 'azure1'); - var $data_border_colors = array('black'); - - var $line_widths = array(1); // single value or array - var $line_styles = array('solid', 'solid', 'dashed'); // single value or array - var $dashed_style = '2-4'; // colored dots-transparent dots - - var $point_sizes = array(5,5,3); // single value or array - var $point_shapes = array('diamond'); // rect, circle, diamond, triangle, dot, line, halfline, cross - - var $error_bar_size = 5; // right and left size of tee - var $error_bar_shape = 'tee'; // 'tee' or 'line' - var $error_bar_line_width = 1; // single value (or array TODO) - - var $plot_border_type = 'sides'; // left, sides, none, full - var $image_border_type = 'none'; // 'raised', 'plain', 'none' - - var $shading = 5; // 0 for no shading, > 0 is size of shadows in pixels - - var $draw_plot_area_background = FALSE; - var $draw_broken_lines = FALSE; // Tells not to draw lines for missing Y data. - + 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'); + + // See SetDefaultStyles() for default colors for PHPlot elements. + + 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. + +//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, + 'data_color' => 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_fule string Path to a file to be used as background. + * $which_width : Image width in pixels. + * $which_height : Image height in pixels. + * $which_output_file : Filename for output. + * $which_input_file : 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) { - /* - * Please see http://www.php.net/register_shutdown_function - * PLEASE NOTE: register_shutdown_function() will take a copy of the object rather than a reference - * so we put an ampersand. However, the function registered will work on the object as it - * was upon registration. To solve this, one of two methods can be used: - * $obj = new object(); - * register_shutdown_function(array(&$obj,'shutdown')); - * OR - * $obj = &new object(); - * HOWEVER, as the second statement assigns $obj a reference to the current object, it might be that - * several instances mess things up... (CHECK THIS) - * - * AND - * as $this->img is set upon construction of the object, problems will not arise for us (for the - * moment maybe, so I put all this here just in case) - */ - register_shutdown_function(array(&$this, '_PHPlot')); - $this->SetRGBArray($this->color_array); - $this->background_done = FALSE; // Set to TRUE after background image is drawn once - if ($which_output_file) $this->SetOutputFile($which_output_file); - if ($which_input_file) + if ($which_input_file) { $this->SetInputFile($which_input_file); - else { + } else { $this->image_width = $which_width; $this->image_height = $which_height; $this->img = ImageCreate($this->image_width, $this->image_height); if (! $this->img) - $this->PrintError('PHPlot(): Could not create image resource.'); - + return $this->PrintError('PHPlot(): Could not create image resource.'); } $this->SetDefaultStyles(); $this->SetDefaultFonts(); - - $this->SetTitle(''); - $this->SetXTitle(''); - $this->SetYTitle(''); - - $this->print_image = TRUE; // Use for multiple plots per image (TODO: automatic) } - /*! - * Destructor. Image resources not deallocated can be memory hogs, I think - * it is safer to automatically call imagedestroy upon script termination than - * do it ourselves. - * See notes in the constructor code. + /* + * 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(). */ - function _PHPlot () + protected function GetImage($image_filename, &$width, &$height) { - ImageDestroy($this->img); - return; + $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 an index to a color passed in as anything (string, hex, rgb) - * - * \param which_color * Color (can be '#AABBCC', 'Colorname', or array(r,g,b)) + /* + * Allocate a GD color index for a color specified by a 4 component array. + * When a color is requested, it is parsed and checked by SetRGBColor, and then saved as an array + * of (R,G,B,A) components. At graph drawing time, this function is used to allocate the color. + * $color : The color specification as a 4 component array: R, G, B, A. + * Returns: A GD color index that can be used when drawing. */ - function SetIndexColor($which_color) + protected function GetColorIndex($color) { - list ($r, $g, $b) = $this->SetRGBColor($which_color); //Translate to RGB - $index = ImageColorExact($this->img, $r, $g, $b); - if ($index == -1) { - return ImageColorResolve($this->img, $r, $g, $b); - } else { - return $index; - } + list($r, $g, $b, $a) = $color; + return imagecolorresolvealpha($this->img, $r, $g, $b, $a); } - - /*! - * Returns an index to a slightly darker color than the one requested. + /* + * Allocate a GD color index for a darker shade of a color specified by a 4 component array. + * See notes for GetColorIndex() above. + * $color : The color specification as a 4 component array: R, G, B, A. + * Returns: A GD color index that can be used when drawing. */ - function SetIndexDarkColor($which_color) + protected function GetDarkColorIndex($color) { - list ($r, $g, $b) = $this->SetRGBColor($which_color); - - $r -= 0x30; $r = ($r < 0) ? 0 : $r; - $g -= 0x30; $g = ($g < 0) ? 0 : $g; - $b -= 0x30; $b = ($b < 0) ? 0 : $b; - - $index = ImageColorExact($this->img, $r, $g, $b); - if ($index == -1) { - return ImageColorResolve($this->img, $r, $g, $b); - } else { - return $index; - } + list ($r, $g, $b, $a) = $color; + $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. If session is set, then only updates indices, - * as they are lost with every script execution, else, sets the default colors by name or value and - * then updates indices too. - * - * FIXME Isn't this too slow? - * - */ - function SetDefaultStyles() - { - /* Some of the Set*() functions use default values when they get no parameters. */ - - if (! isset($this->session_set)) { - // If sessions are enabled, this variable will be preserved, so upon future executions, we - // will have it set, as well as color names (though not color indices, that's why we - // need to rebuild them) - $this->session_set = TRUE; - - // These only need to be set once - $this->SetLineWidths(); - $this->SetLineStyles(); - $this->SetDefaultDashedStyle($this->dashed_style); - $this->SetPointSizes($this->point_sizes); - } - - $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); + /* + * Sets/reverts all colors and styles to their defaults. + */ + protected function SetDefaultStyles() + { + $this->SetDefaultDashedStyle($this->dashed_style); + $this->SetImageBorderColor(array(194, 194, 194)); + $this->SetPlotBgColor('white'); + $this->SetBackgroundColor('white'); + $this->SetLabelColor('black'); + $this->SetTextColor('black'); + $this->SetGridColor('black'); + $this->SetLightGridColor('gray'); + $this->SetTickColor('black'); + $this->SetTitleColor('black'); + // These functions set up the default colors when called without parameters $this->SetDataColors(); $this->SetErrorBarColors(); $this->SetDataBorderColors(); + return TRUE; } - /* - * + * Set the image background color to $which_color. */ function SetBackgroundColor($which_color) { - $this->bg_color= $which_color; - $this->ndx_bg_color= $this->SetIndexColor($this->bg_color); - return TRUE; + return (bool)($this->bg_color = $this->SetRGBColor($which_color)); } /* - * + * Set the plot area background color (if enabled) to $which_color. */ function SetPlotBgColor($which_color) { - $this->plot_bg_color= $which_color; - $this->ndx_plot_bg_color= $this->SetIndexColor($this->plot_bg_color); - return TRUE; + return (bool)($this->plot_bg_color = $this->SetRGBColor($which_color)); } - /* - * - */ - function SetTitleColor($which_color) + /* + * Set the color of the titles (main, X, and Y) to $which_color. + * See also SetXTitleColor and SetYTitleColor. + */ + function SetTitleColor($which_color) { - $this->title_color= $which_color; - $this->ndx_title_color= $this->SetIndexColor($this->title_color); - return TRUE; + return (bool)($this->title_color = $this->SetRGBColor($which_color)); } /* - * + * Set the color of the X title to $which_color. + * This overrides the color set with SetTitleColor. */ - function SetTickColor ($which_color) + function SetXTitleColor($which_color) { - $this->tick_color= $which_color; - $this->ndx_tick_color= $this->SetIndexColor($this->tick_color); - return TRUE; + return (bool)($this->x_title_color = $this->SetRGBColor($which_color)); } - /* - * + * Set the color of the Y title to $which_color. + * This overrides the color set with SetTitleColor. */ - function SetLabelColor ($which_color) + function SetYTitleColor($which_color) { - $this->label_color = $which_color; - $this->ndx_title_color= $this->SetIndexColor($this->label_color); - return TRUE; + return (bool)($this->y_title_color = $this->SetRGBColor($which_color)); } - /* - * + * Set the color of the axis tick marks to $which_color. */ - function SetTextColor ($which_color) + function SetTickColor($which_color) { - $this->text_color= $which_color; - $this->ndx_text_color= $this->SetIndexColor($this->text_color); - return TRUE; + return (bool)($this->tick_color = $this->SetRGBColor($which_color)); } + /* + * Do not use. Use SetTitleColor instead. + */ + function SetLabelColor($which_color) + { + return $this->SetTitleColor($which_color); + } /* - * + * Set the general text color (tick and data labels, legend, etc) to $which_color. */ - function SetLightGridColor ($which_color) + function SetTextColor($which_color) { - $this->light_grid_color= $which_color; - $this->ndx_light_grid_color= $this->SetIndexColor($this->light_grid_color); - return TRUE; + return (bool)($this->text_color = $this->SetRGBColor($which_color)); } - /* - * + * Set the X and Y grid colors to $which_color. Also sets the data label line color. */ - function SetGridColor ($which_color) + function SetLightGridColor($which_color) { - $this->grid_color = $which_color; - $this->ndx_grid_color= $this->SetIndexColor($this->grid_color); - return TRUE; + return (bool)($this->light_grid_color = $this->SetRGBColor($which_color)); } + /* + * Set the color used for the X and Y axis, plot border, legend border to $which_color. + * Note: This has nothing to do with the grid, and we don't recall where this name came from. + */ + function SetGridColor($which_color) + { + return (bool)($this->grid_color = $this->SetRGBColor($which_color)); + } /* - * + * Set the color used for the image border to $which_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 TRUE; + return (bool)($this->i_border = $this->SetRGBColor($which_color)); } - /* - * - */ + * Designate color $which_color to be transparent, if supported by the image format. + */ function SetTransparentColor($which_color) - { - ImageColorTransparent($this->img, $this->SetIndexColor($which_color)); - return TRUE; + { + return (bool)($this->transparent_color = $this->SetRGBColor($which_color)); } - - /*! - * Sets the array of colors to be used. It can be user defined, a small predefined one + /* + * 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'. + * $which_color_array : A color array, or 'small' or 'large'. + * Color arrays map color names into arrays of R, G, B and optionally A values. */ - function SetRGBArray ($which_color_array) - { - if ( is_array($which_color_array) ) { // User defined array + 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), @@ -496,10 +505,12 @@ class PHPlot { 'azure1' => array(240, 255, 255), 'aquamarine1' => array(127, 255, 212) ); - return TRUE; - } elseif ($which_color_array === 'large') { // Large color array - include("./rgb.inc.php"); - $this->rgb_array = $RGBArray; + } 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)); } @@ -507,559 +518,953 @@ class PHPlot { return TRUE; } - /*! - * Returns an array in R, G, B format 0-255 + /* + * 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. * - * \param color_asked array(R,G,B) or string (named color or '#AABBCC') + * 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). + * Note: This function should be considered 'protected', and is not documented for public use. */ - function SetRGBColor($color_asked) + function SetRGBColor($color_asked, $alpha = 0) { - if ($color_asked == '') { $color_asked = array(0, 0, 0); }; + if (empty($color_asked)) { + $ret_val = array(0, 0, 0); - if ( count($color_asked) == 3 ) { // already array of 3 rgb - $ret_val = $color_asked; - } else { // asking for a color by string - if(substr($color_asked, 0, 1) == '#') { // asking in #FFFFFF format. - $ret_val = array(hexdec(substr($color_asked, 1, 2)), hexdec(substr($color_asked, 3, 2)), - hexdec(substr($color_asked, 5, 2))); - } else { // asking by color name - $ret_val = $this->rgb_array[$color_asked]; - } + } 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. - */ - function SetDataColors($which_data = NULL, $which_border = NULL) - { - if (is_null($which_data) && is_array($this->data_colors)) { - // use already set data_colors - } else if (! is_array($which_data)) { - $this->data_colors = ($which_data) ? array($which_data) : array('blue', 'red', 'green', 'orange'); + /* + * 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). + * $which_border is passed to SetDataBorderColors, for backward compatibility. + * $alpha is a default Alpha to apply to all data colors that do not have alpha. + * The default for this is NULL, not 0, so we can tell if it was defaulted. But the effective + * default value is 0 (opaque). + */ + function SetDataColors($which_data = NULL, $which_border = NULL, $alpha = NULL) + { + if (is_array($which_data)) { + $colors = $which_data; // Use supplied array + } elseif (!empty($which_data)) { + $colors = array($which_data); // Use supplied single color + } elseif (empty($this->data_colors) || !is_null($which_data)) { + $colors = $this->default_colors; // Use default color array } else { - $this->data_colors = $which_data; + // which_data is NULL or missing and a color array is already set. + // The existing color array is left alone, except that if $alpha is + // given this will replace the alpha value of each existing color. + // This makes SetDataColors(NULL, NULL, $alpha) work. + if (isset($alpha)) { + $n_colors = count($this->data_colors); + for ($i = 0; $i < $n_colors; $i++) { + $this->data_colors[$i][3] = $alpha; // Component 3 = alpha value + } + } + // No need to reparse the colors or anything else. + return TRUE; } - $i = 0; - foreach ($this->data_colors as $col) { - $this->ndx_data_colors[$i] = $this->SetIndexColor($col); - $this->ndx_data_dark_colors[$i] = $this->SetIndexDarkColor($col); - $i++; + if (!isset($alpha)) + $alpha = 0; // Actual default is opaque colors. + + // Check each color and convert to array (r,g,b,a) form. + // Use the $alpha argument as a default for the alpha value of each color. + $this->data_colors = array(); + foreach ($colors as $color) { + $color_array = $this->SetRGBColor($color, $alpha); + if (!$color_array) return FALSE; // SetRGBColor already did an error message. + $this->data_colors[] = $color_array; } // For past compatibility: - $this->SetDataBorderColors($which_border); - } // function SetDataColors() - + return $this->SetDataBorderColors($which_border); + } - /*! - * + /* + * 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_null($which_br) && is_array($this->data_border_colors)) { - // use already set data_border_colors - } else if (! is_array($which_br)) { - // Create new array with specified color - $this->data_border_colors = ($which_br) ? array($which_br) : array('black'); + if (is_array($which_br)) { + $colors = $which_br; // Use supplied array + } elseif (!empty($which_br)) { + $colors = array($which_br); // Use supplied single color + } elseif (empty($this->data_border_colors) || !is_null($which_br)) { + $colors = array('black'); // Use default } else { - $this->data_border_colors = $which_br; + return TRUE; // Do nothing: which_br is NULL or missing and a color array is already set. } - $i = 0; - foreach($this->data_border_colors as $col) { - $this->ndx_data_border_colors[$i] = $this->SetIndexColor($col); - $i++; + // Check each color and convert to array (r,g,b,a) form. + $this->data_border_colors = array(); + foreach ($colors as $color) { + $color_array = $this->SetRGBColor($color); + if (!$color_array) return FALSE; // SetRGBColor already did an error message. + $this->data_border_colors[] = $color_array; } - } // function SetDataBorderColors() - + return TRUE; + } - /*! + /* * Sets the colors for the data error bars. + * Argument usage is the same as SetDataColors(). */ function SetErrorBarColors($which_err = NULL) { - if (is_null($which_err) && is_array($this->error_bar_colors)) { - // use already set error_bar_colors - } else if (! is_array($which_err)) { - $this->error_bar_colors = ($which_err) ? array($which_err) : array('black'); + if (is_array($which_err)) { + $colors = $which_err; // Use supplied array + } elseif (!empty($which_err)) { + $colors = array($which_err); // Use supplied single color + } elseif (empty($this->error_bar_colors) || !is_null($which_err)) { + $colors = $this->default_colors; // Use default color array } else { - $this->error_bar_colors = $which_err; + return TRUE; // Do nothing: which_err is NULL or missing and a color array is already set. } - $i = 0; - foreach($this->error_bar_colors as $col) { - $this->ndx_error_bar_colors[$i] = $this->SetIndexColor($col); - $i++; - } + // Check each color and convert to array (r,g,b,a) form. + $this->error_bar_colors = array(); + foreach ($colors as $color) { + $color_array = $this->SetRGBColor($color); + if (!$color_array) return FALSE; // SetRGBColor already did an error message. + $this->error_bar_colors[] = $color_array; + } 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. + /* + * Sets the default dashed line style. + * $which_style : A string specifying the dashed line style, as alternating numbers + * of the length (in pixels) of lines and spaces, separated by dashes. + * For example: '2-3-1-2' means 2 dots of color, 3 transparent, 1 color, then 2 transparent. + * This builds a string which will evaluate to an array of integers. Each colored dot + * is '$which_ndxcol' and each transparent dot is 'IMG_COLOR_TRANSPARENT'. When SetDashedStyle() + * eval's this with $which_ndxcol set, the result is a GD line style array. */ - function SetDefaultDashedStyle($which_style) + function SetDefaultDashedStyle($which_style) { - // String: "numcol-numtrans-numcol-numtrans..." + // Explode "numcol-numtrans-numcol-numtrans..." into segment counts: $asked = explode('-', $which_style); if (count($asked) < 2) { - $this->DrawError("SetDefaultDashedStyle(): Wrong parameter '$which_style'."); - return FALSE; + 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++; + // Build the string to be evaluated later by SetDashedStyle() with $which_ndxcolor set. + $result = ''; + $vals = array('$which_ndxcol,', 'IMG_COLOR_TRANSPARENT,'); + $index = 0; + foreach ($asked as $n) { + $result .= str_repeat($vals[$index], $n); + $index = 1 - $index; } - // Remove trailing comma and add closing parenthesis - $this->default_dashed_style = substr($this->default_dashed_style, 0, -1); - $this->default_dashed_style .= ')'; + $this->default_dashed_style = "array($result)"; return TRUE; } - - /*! + /* * Sets the style before drawing a dashed line. Defaults to $this->default_dashed_style - * \param which_ndxcol Color index to be used. + * $which_ndxcol : Color index to be used. */ - function SetDashedStyle($which_ndxcol) + 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. + /* + * Set line widths for each data set. + * $which_lw : Array of line widths in pixels, or a single value to use for all data sets. */ 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); + if (is_array($which_lw)) { + $this->line_widths = $which_lw; // Use provided array + } elseif (!is_null($which_lw)) { + $this->line_widths = array($which_lw); // Convert value to array } return TRUE; } - /*! - * + /* + * Set line style ('solid' or 'dashed') for each data set. + * $which_ls : Array of keywords, or a single keyword to use for all data sets. */ 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 { + if (is_array($which_ls)) { + $this->line_styles = $which_ls; // Use provided array + } elseif (!is_null($which_ls)) { $this->line_styles = ($which_ls) ? array($which_ls) : array('solid'); } return TRUE; } - ///////////////////////////////////////////// -////////////// FONTS +////////////// TEXT and FONTS ///////////////////////////////////////////// - - /*! - * Sets number of pixels between lines of the same text. + /* + * Controls the line spacing of multi-line labels. + * $which_spc : Line spacing factor for text + * 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; } - - /*! - * Enables use of TrueType fonts in the graph. Font initialisation methods - * depend on this setting, so when called, SetUseTTF() resets the font - * settings + /* + * 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) + function SetUseTTF($which_ttf) { $this->use_ttf = $which_ttf; - if ($which_ttf) - $this->SetDefaultFonts(); - return TRUE; + 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; - } else { - $this->PrintError("SetTTFPath(): $which_path is not a valid path."); - return FALSE; + if (!is_dir($which_path) || !is_readable($which_path)) { + return $this->PrintError("SetTTFPath(): $which_path is not a valid path."); } + $this->ttf_path = $which_path; + return TRUE; } - /*! + /* * 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) { - if (is_file($which_font) && is_readable($which_font)) { - $this->default_ttfont = $which_font; - return $this->SetDefaultFonts(); - } else { - $this->PrintError("SetDefaultTTFont(): $which_font is not a valid font file."); - return FALSE; + $this->default_ttfont = $which_font; + return $this->SetUseTTF(TRUE); + } + + /* + * Return the default TrueType font name. If no default has been set, + * this tries some likely candidates for a font which can be loaded. + * If it finds one that works, that becomes the default TT font. + * If there is no default and it cannot find a working font, it falls + * back to the original PHPlot default (which will not likely work either). + */ + protected function GetDefaultTTFont() + { + if (!isset($this->default_ttfont)) { + // No default font yet. Try some common sans-serif fonts. + $fonts = array('LiberationSans-Regular.ttf', // For Linux with a correct GD font search path + 'Verdana.ttf', 'Arial.ttf', 'Helvetica.ttf', // For Windows, maybe others + 'ttf-liberation/LiberationSans-Regular.ttf', // For Debian, Ubuntu, and friends + 'benjamingothic.ttf', // Original PHPlot default + ); + foreach ($fonts as $font) { + // First try the font name alone, to see if GD can find and load it. + if (@imagettfbbox(10, 0, $font, "1") !== False) + break; + // If the font wasn't found, try it with the default TTF path in front. + $font_with_path = $this->ttf_path . DIRECTORY_SEPARATOR . $font; + if (@imagettfbbox(10, 0, $font_with_path, "1") !== False) { + $font = $font_with_path; + break; + } + } + // We either have a working font, or are using the last one regardless. + $this->default_ttfont = $font; } + return $this->default_ttfont; } - /*! + /* * Sets fonts to their defaults */ - function SetDefaultFonts() + protected function SetDefaultFonts() { // TTF: if ($this->use_ttf) { - //$this->SetTTFPath(dirname($_SERVER['PHP_SELF'])); - $this->SetTTFPath(getcwd()); - $this->SetFont('generic', $this->default_ttfont, 8); - $this->SetFont('title', $this->default_ttfont, 14); - $this->SetFont('legend', $this->default_ttfont, 8); - $this->SetFont('x_label', $this->default_ttfont, 6); - $this->SetFont('y_label', $this->default_ttfont, 6); - $this->SetFont('x_title', $this->default_ttfont, 10); - $this->SetFont('y_title', $this->default_ttfont, 10); - } - // Fixed: - else { - $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); + 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); } - - return TRUE; + // 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); } - /*! - * Sets Fixed/Truetype font parameters. - * \param $which_elem Is the element whose font is to be changed. - * It can be one of 'title', 'legend', 'generic', - * 'x_label', 'y_label', x_title' or 'y_title' - * \param $which_font Can be a number (for fixed font sizes) or - * a string with the filename when using TTFonts. - * \param $which_size Point size (TTF only) - * Calculates and updates internal height and width variables. + /* + * 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 SetFont($which_elem, $which_font, $which_size = 12) + function SetFontGD($which_elem, $which_font, $which_spacing = NULL) { - // TTF: - if ($this->use_ttf) { - $path = $this->ttf_path.'/'.$which_font; - - if (! is_file($path) || ! is_readable($path) ) { - $this->DrawError("SetFont(): True Type font $path doesn't exist"); - return FALSE; - } - - switch ($which_elem) { - case 'generic': - $this->generic_font['font'] = $path; - $this->generic_font['size'] = $which_size; - break; - case 'title': - $this->title_font['font'] = $path; - $this->title_font['size'] = $which_size; - break; - case 'legend': - $this->legend_font['font'] = $path; - $this->legend_font['size'] = $which_size; - break; - case 'x_label': - $this->x_label_font['font'] = $path; - $this->x_label_font['size'] = $which_size; - break; - case 'y_label': - $this->y_label_font['font'] = $path; - $this->y_label_font['size'] = $which_size; - break; - case 'x_title': - $this->x_title_font['font'] = $path; - $this->x_title_font['size'] = $which_size; - break; - case 'y_title': - $this->y_title_font['font'] = $path; - $this->y_title_font['size'] = $which_size; - break; - default: - $this->DrawError("SetFont(): Unknown element '$which_elem' specified."); - return FALSE; - } - return TRUE; + 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; + } - // Fixed fonts: - if ($which_font > 5 || $which_font < 0) { - $this->DrawError('SetFont(): Non-TTF font size must be 1, 2, 3, 4 or 5'); + /* + * 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; } - switch ($which_elem) { - case 'generic': - $this->generic_font['font'] = $which_font; - $this->generic_font['height'] = ImageFontHeight($which_font); - $this->generic_font['width'] = ImageFontWidth($which_font); - break; - case 'title': - $this->title_font['font'] = $which_font; - $this->title_font['height'] = ImageFontHeight($which_font); - $this->title_font['width'] = ImageFontWidth($which_font); - break; - case 'legend': - $this->legend_font['font'] = $which_font; - $this->legend_font['height'] = ImageFontHeight($which_font); - $this->legend_font['width'] = ImageFontWidth($which_font); - break; - case 'x_label': - $this->x_label_font['font'] = $which_font; - $this->x_label_font['height'] = ImageFontHeight($which_font); - $this->x_label_font['width'] = ImageFontWidth($which_font); - break; - case 'y_label': - $this->y_label_font['font'] = $which_font; - $this->y_label_font['height'] = ImageFontHeight($which_font); - $this->y_label_font['width'] = ImageFontWidth($which_font); - break; - case 'x_title': - $this->x_title_font['font'] = $which_font; - $this->x_title_font['height'] = ImageFontHeight($which_font); - $this->x_title_font['width'] = ImageFontWidth($which_font); - break; - case 'y_title': - $this->y_title_font['font'] = $which_font; - $this->y_title_font['height'] = ImageFontHeight($which_font); - $this->y_title_font['width'] = ImageFontWidth($which_font); - break; - default: - $this->DrawError("SetFont(): Unknown element '$which_elem' specified."); - return FALSE; + // Empty font name means use the default font. + if (empty($which_font)) + $which_font = $this->GetDefaultTTFont(); + $path = $which_font; + + // First try the font name directly, if not then try with path. + // Use GD imagettfbbox() to determine if this is a valid font. + // The return $bbox is used below, if valid. + if (($bbox = @imagettfbbox($which_size, 0, $path, "E")) === False) { + $path = $this->ttf_path . DIRECTORY_SEPARATOR . $which_font; + if (($bbox = @imagettfbbox($which_size, 0, $path, "E")) === False) { + 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. + $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; } - - /*! - * Returns an array with the size of the bounding box of an - * arbitrarily placed (rotated) TrueType text string. + /* + * 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 TTFBBoxSize($size, $angle, $font, $string) + function SetFont($which_elem, $which_font, $which_size = 12, $line_spacing = NULL) { - // First, assume angle < 90 - $arr = ImageTTFBBox($size, 0, $font, $string); - $flat_width = $arr[2] - $arr[0]; - $flat_height = abs($arr[3] - $arr[5]); + if ($this->use_ttf) + return $this->SetFontTTF($which_elem, $which_font, $which_size, $line_spacing); + return $this->SetFontGD($which_elem, $which_font, $line_spacing); + } - // Now the bounding box - $angle = deg2rad($angle); - $width = ceil(abs($flat_width*cos($angle) + $flat_height*sin($angle))); //Must be integer - $height = ceil(abs($flat_width*sin($angle) + $flat_height*cos($angle))); //Must be integer + /* + * 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; - return array($width, $height); + // 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; } - - /*! - * Draws a string of text. Horizontal and vertical alignment are relative to - * to the drawing. That is: vertical text (90 deg) gets centered along y-axis - * with v_align = 'center', and adjusted to the left of x-axis with h_align = 'right', + /* + * 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 * - * \note Original multiple lines code submitted by Remi Ricard. - * \note Original vertical code submitted by Marlin Viss. */ - function DrawText($which_font, $which_angle, $which_xpos, $which_ypos, $which_color, $which_text, - $which_halign = 'left', $which_valign = 'bottom') - { - // TTF: - if ($this->use_ttf) { - $size = $this->TTFBBoxSize($which_font['size'], $which_angle, $which_font['font'], $which_text); - $rads = deg2rad($which_angle); - if ($which_valign == 'center') - $which_ypos += $size[1]/2; + /* + * 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 ($which_valign == 'bottom') - $which_ypos += $size[1]; + 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; - if ($which_halign == 'center') - $which_xpos -= ($size[0]/2) * cos($rads); + $draw_func = 'ImageStringUp'; - if ($which_halign == 'left') - $which_xpos += $size[0] * sin($rads); + // Rotation matrix "R" for 90 degrees (with Y pointing down): + $r00 = 0; $r01 = 1; + $r10 = -1; $r11 = 0; - if ($which_halign == 'right') - $which_xpos -= $size[0] * cos($rads); + } else { + // Horizontal text (0 degrees): + $draw_func = 'ImageString'; - ImageTTFText($this->img, $which_font['size'], $which_angle, - $which_xpos, $which_ypos, $which_color, $which_font['font'], $which_text); + // Rotation matrix "R" for 0 degrees: + $r00 = 1; $r01 = 0; + $r10 = 0; $r11 = 1; } - // Fixed fonts: - else { - // Split the text by its lines, and count them - $which_text = ereg_replace("\r", "", $which_text); - $str = split("\n", $which_text); - $nlines = count($str); - $spacing = $this->line_spacing * ($nlines - 1); - // Vertical text: - // (Remember the alignment convention with vertical text) - if ($which_angle == 90) { - // The text goes around $which_xpos. - if ($which_halign == 'center') - $which_xpos -= ($nlines * ($which_font['height'] + $spacing))/2; - - // Left alignment requires no modification to $xpos... - // Right-align it. $which_xpos designated the rightmost x coordinate. - else if ($which_halign == 'right') - $which_xpos += ($nlines * ($which_font['height'] + $spacing)); - - $ypos = $which_ypos; - for($i = 0; $i < $nlines; $i++) { - // Center the text vertically around $which_ypos (each line) - if ($which_valign == 'center') - $ypos = $which_ypos + (strlen($str[$i]) * $which_font['width']) / 2; - // Make the text finish (vertically) at $which_ypos - if ($which_valign == 'bottom') - $ypos = $which_ypos + strlen($str[$i]) * $which_font['width']; - - ImageStringUp($this->img, $which_font['font'], - $i * ($which_font['height'] + $spacing) + $which_xpos, - $ypos, $str[$i], $which_color); - } - } - // Horizontal text: - else { - // The text goes above $which_ypos - if ($which_valign == 'top') - $which_ypos -= $nlines * ($which_font['height'] + $spacing); - // The text is centered around $which_ypos - if ($which_valign == 'center') - $which_ypos -= ($nlines * ($which_font['height'] + $spacing))/2; - // valign = 'bottom' requires no modification - - $xpos = $which_xpos; - for($i = 0; $i < $nlines; $i++) { - // center the text around $which_xpos - if ($which_halign == 'center') - $xpos = $which_xpos - (strlen($str[$i]) * $which_font['width'])/2; - // make the text finish at $which_xpos - if ($which_halign == 'right') - $xpos = $which_xpos - strlen($str[$i]) * $which_font['width']; - - ImageString($this->img, $which_font['font'], $xpos, - $i * ($which_font['height'] + $spacing) + $which_ypos, - $str[$i], $which_color); - } + // 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; } - } - return TRUE; - } // function DrawText() + $this->DoCallback('debug_textbox', $px, $py, $bbox_width, $bbox_height); + } + for ($i = 0; $i < $n_lines; $i++) { -///////////////////////////////////////////// -/////////// INPUT / OUTPUT CONTROL -///////////////////////////////////////////// + // 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; - /*! - * Sets output file format. + // 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. + * Note: This function should be considered 'protected', and is not documented for public use. + */ + 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. + * Note: This function should be considered 'protected', and is not documented for public use. + */ + 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 to $format (jpg, png, ...) */ function SetFileFormat($format) { $asked = $this->CheckOption($format, 'jpg, png, gif, wbmp', __FUNCTION__); - + if (!$asked) return FALSE; switch ($asked) { case 'jpg': - if (imagetypes() & IMG_JPG) - $this->file_format = 'jpg'; - return TRUE; + $format_test = IMG_JPG; break; case 'png': - if (imagetypes() & IMG_PNG) - $this->file_format = 'png'; - return TRUE; + $format_test = IMG_PNG; break; case 'gif': - if (imagetypes() & IMG_GIF) - $this->file_format = 'gif'; - return TRUE; + $format_test = IMG_GIF; break; case 'wbmp': - if (imagetypes() & IMG_WBMP) - $this->file_format = 'wbmp'; - return TRUE; + $format_test = IMG_WBMP; break; - default: - $this->PrintError("SetFileFormat():File format '$format' not supported"); - return FALSE; } + 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) + * $input_file : Path to the file to be used (jpeg, png and gif accepted) + * $mode : 'centeredtile', 'tile', or '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) + * $input_file : Path to the file to be used (jpeg, png and gif accepted) + * $mode : 'centeredtile', 'tile', or '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) @@ -1068,7 +1473,7 @@ class PHPlot { 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. */ @@ -1078,10 +1483,8 @@ class PHPlot { return TRUE; } - - /*! - * Performs the actual outputting of the generated graph, and - * destroys the image resource. + /* + * Performs the actual outputting of the generated graph. */ function PrintImage() { @@ -1093,224 +1496,476 @@ class PHPlot { header('Pragma: no-cache'); } - switch($this->file_format) { + 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); - } + $mime_type = 'image/png'; + $output_f = 'imagepng'; 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); - } + $mime_type = 'image/jpeg'; + $output_f = 'imagejpeg'; 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); - } - + $mime_type = 'image/gif'; + $output_f = 'imagegif'; 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); - } - + case 'wbmp': + $mime_type = 'image/wbmp'; + $output_f = 'imagewbmp'; break; default: - $this->PrintError('PrintImage(): Please select an image type!'); - break; + return $this->PrintError('PrintImage(): Please select an image type!'); + } + if (!$this->is_inline) { + Header("Content-type: $mime_type"); + } + if ($this->is_inline && $this->output_file != '') { + $output_f($this->img, $this->output_file); + } else { + $output_f($this->img); } return TRUE; } - /*! - * Prints an error message to stdout and dies + /* + * 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. */ - function PrintError($error_message) + protected function PrintError($error_message) { - echo "

    Fatal error: $error_message

    "; - die; - } + // Be sure not to loop recursively, e.g. PrintError - PrintImage - PrintError. + if (isset($this->in_error)) return FALSE; + $this->in_error = TRUE; - /*! - * Prints an error message inline into the generated image and draws it centered - * around the given coordinates (defaults to center of the image) - * \param error_message Message to be drawn - * \param where_x X coordinate - * \param where_y Y coordinate - */ - function DrawError($error_message, $where_x = NULL, $where_y = NULL) - { - if (! $this->img) - $this->PrintError('_DrawError(): Warning, no image resource allocated. '. - 'The message to be written was: '.$error_message); + // 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); - $ypos = (! $where_y) ? $this->image_height/2 : $where_y; - $xpos = (! $where_x) ? $this->image_width/2 : $where_x; - ImageRectangle($this->img, 0, 0, $this->image_width, $this->image_height, - ImageColorAllocate($this->img, 255, 255, 255)); + // Switch to built-in fonts, in case of error with TrueType fonts: + $this->SetUseTTF(FALSE); - $this->DrawText($this->generic_font, 0, $xpos, $ypos, ImageColorAllocate($this->img, 0, 0, 0), - $error_message, 'center', 'center'); + $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(). + } - $this->PrintImage(); - exit; -// return TRUE; + /* + * 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. + /* + * Sets position for X data labels. For most plot types, these are + * labels along the X axis (but different from X tick labels). + * Accepted positions are: plotdown, plotup, both, none. + * For horizontal bar charts, these are the labels right (or left) of the bars. + * Accepted positions are: plotin, plotstack, none. */ function SetXDataLabelPos($which_xdlp) { - $this->x_data_label_pos = $this->CheckOption($which_xdlp, 'plotdown, plotup, both, xaxis, all, none', - __FUNCTION__); - if ($which_xdlp != 'none') - $this->x_tick_label_pos = 'none'; + $which_xdlp = $this->CheckOption($which_xdlp, 'plotdown, plotup, both, none, plotin, plotstack', + __FUNCTION__); + if (!$which_xdlp) return FALSE; + $this->x_data_label_pos = $which_xdlp; return TRUE; } - /*! - * Sets position for Y labels following data points. + /* + * 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 bar charts, these are the labels along the Y axis. + * Accepted positions are: plotleft, plotright, both, none. */ - function SetYDataLabelPos($which_ydlp) + function SetYDataLabelPos($which_ydlp) { - $this->y_data_label_pos = $this->CheckOption($which_ydlp, 'plotleft, plotright, both, yaxis, all, none', - __FUNCTION__); - if ($which_ydlp != 'none') - $this->y_tick_label_pos = 'none'; + $which_ydlp = $this->CheckOption($which_ydlp, 'plotleft, plotright, both, none, plotin, plotstack', + __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) + /* + * Set position for X tick labels. */ - function SetXTickLabelPos($which_xtlp) + function SetXTickLabelPos($which_xtlp) { - $this->x_tick_label_pos = $this->CheckOption($which_xtlp, 'plotdown, plotup, both, xaxis, all, none', - __FUNCTION__); - if ($which_xtlp != 'none') - $this->x_data_label_pos = 'none'; + $which_xtlp = $this->CheckOption($which_xtlp, 'plotdown, plotup, both, xaxis, 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) + /* + * Set position for Y tick labels. */ - function SetYTickLabelPos($which_ytlp) + function SetYTickLabelPos($which_ytlp) { - $this->y_tick_label_pos = $this->CheckOption($which_ytlp, 'plotleft, plotright, both, yaxis, all, none', - __FUNCTION__); - if ($which_ytlp != 'none') - $this->y_data_label_pos = 'none'; + $which_ytlp = $this->CheckOption($which_ytlp, 'plotleft, plotright, both, yaxis, none', + __FUNCTION__); + if (!$which_ytlp) return FALSE; + $this->y_tick_label_pos = $which_ytlp; return TRUE; } - /*! - * Sets type for tick and data labels on X axis. - * \note 'title' type left for backwards compatibility. + /* + * Set formatting type for tick and data labels on X or Y axis. + * This implements the 4 functions Set[XY]LabelType() and Set[XY]DataLabelType(). + * $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($which_xlt) + function SetXLabelType() // Variable arguments: $type, ... { - $this->x_label_type = $this->CheckOption($which_xlt, 'data, time, title', __FUNCTION__); - return TRUE; + $args = func_get_args(); + return $this->SetLabelType('x', $args); } - /*! - * Sets type for tick and data labels on Y axis. + /* + * 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); + } + + /* + * Set the date/time format code for X labels. + * Note: Use of SetXLabelType('time', $which_xtf) is preferred, because + * SetXTimeFormat does not also enable date/time formatting. */ - function SetYLabelType($which_ylt) + function SetXTimeFormat($which_xtf) { - $this->y_label_type = $this->CheckOption($which_ylt, 'data, time', __FUNCTION__); + $this->label_format['x']['time_format'] = $which_xtf; return TRUE; } - function SetXTimeFormat($which_xtf) + /* + * Set the date/time format code for Y labels. + * Note: Use of SetYLabelType('time', $which_ytf) is preferred, because + * SetYTimeFormat does not also enable date/time formatting. + */ + function SetYTimeFormat($which_ytf) { - $this->x_time_format = $which_xtf; + $this->label_format['y']['time_format'] = $which_ytf; return TRUE; } - function SetYTimeFormat($which_ytf) + + /* + * Set number format parameters (decimal point and thousands separator) for + * 'data' mode label formatting, overriding the locale-defaults. + */ + function SetNumberFormat($decimal_point, $thousands_sep) { - $this->y_time_format = $which_ytf; + $this->decimal_point = $decimal_point; + $this->thousands_sep = $thousands_sep; return TRUE; } - function SetXLabelAngle($which_xla) + /* + * Set the text angle for X labels to $which_xla degrees. + */ + function SetXLabelAngle($which_xla) { $this->x_label_angle = $which_xla; return TRUE; } + /* + * Set the text angle for Y labels to $which_xla degrees. + */ function SetYLabelAngle($which_yla) { $this->y_label_angle = $which_yla; return TRUE; } + /* + * Set the angle for X Data Labels to $which_xdla degrees. + * If not used, this defaults to the value set with SetXLabelAngle. + */ + function SetXDataLabelAngle($which_xdla) + { + $this->x_data_label_angle = $which_xdla; + return TRUE; + } + + /* + * Set the angle for Y Data Labels to $which_ydla degrees. + * If not used, this defaults to zero (unlike X data labels). + */ + function SetYDataLabelAngle($which_ydla) + { + $this->y_data_label_angle = $which_ydla; + return TRUE; + } + ///////////////////////////////////////////// /////////// MISC ///////////////////////////////////////////// - /*! - * Checks the valididy of an option. - * \param which_opt String to check. - * \param which_acc String of accepted choices. - * \param which_func Name of the calling function, for error messages. - * \note If checking everywhere for correctness slows things down, we could provide a - * child class overriding every Set...() method which uses CheckOption(). Those new - * methods could proceed in the unsafe but faster way. + /* + * 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. */ - function CheckOption($which_opt, $which_acc, $which_func) + protected function CheckOption($which_opt, $which_acc, $which_func) { - $asked = trim($which_opt); - - // FIXME: this for backward compatibility, as eregi() fails with empty strings. - if ($asked == '') - return ''; + $asked = strtolower(trim($which_opt)); - $asked = strtolower($asked); - if (@ eregi($asked, $which_acc)) { + // Look for the supplied value in a comma/space separated list. + if (strpos(", $which_acc,", ", $asked,") !== FALSE) return $asked; - } else { - $this->DrawError("$which_func(): '$which_opt' not in available choices: '$which_acc'."); - return NULL; + + $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. + * The goal is minimize which functions understand the actual data type values. + * This sets the datatype_* variables for use by other member functions. + * datatype_implied : Implicit independent variable (e.g. text-data vs data-data) + * datatype_swapped_xy : Swapped X/Y (horizontal plot) + * datatype_error_bars : Data array has error bar data + * datatype_pie_single : Data array is for a pie chart with one row per slice + */ + protected function DecodeDataType() + { + $dt = $this->data_type; + + $this->datatype_implied = ($dt == 'text-data' || $dt == 'text-data-single' + || $dt == 'text-data-yx'); + $this->datatype_swapped_xy = ($dt == 'text-data-yx' || $dt == 'data-data-yx'); + $this->datatype_error_bars = ($dt == 'data-data-error'); + $this->datatype_pie_single = ($dt == 'text-data-single'); } + /* + * Make sure the data array is populated, and calculate the number of columns. + * This is called from DrawGraph. Calculates data_columns, which is the + * maximum number of dependent variable values (usually Y) in the data array rows. + * (For pie charts, this is the number of slices.) + * This depends on the data_type, unlike records_per_group (which was + * previously used to pad style arrays, but is not accurate). + * Returns True if the data array is OK, else reports an error (and may return False). + * Note error messages refer to the caller, the public DrawGraph(). + */ + protected function CheckDataArray() + { + // Test for missing image, which really should never happen. + if (!$this->img) { + return $this->PrintError('DrawGraph(): No image resource allocated'); + } + + // Test for missing or empty data array: + 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'); + } + + // Decode the data type into functional flags. + $this->DecodeDataType(); - /*! - * \note Submitted by Thiemo Nagel + // Calculate the maximum number of dependent values per independent value + // (e.g. Y for each X), or the number of pie slices. + if ($this->datatype_pie_single) { + $this->data_columns = $this->num_data_rows; // Special case for 1 type of pie chart. + } else { + $skip = $this->datatype_implied ? 1 : 2; // Skip data label and independent variable if used + $this->data_columns = $this->records_per_group - $skip; + if ($this->datatype_error_bars) // Each Y has +err and -err along with it + $this->data_columns = (int)($this->data_columns / 3); + } + return TRUE; + } + + /* + * Control headers for browser-side image caching. + * $which_browser_cache : True to allow browsers to cache the image. */ function SetBrowserCache($which_browser_cache) { @@ -1318,8 +1973,9 @@ class PHPlot { return TRUE; } - /*! - * Whether to show the final image or not + /* + * Set whether DrawGraph automatically outputs the image too. + * $which_pi : True to have DrawGraph call PrintImage at the end. */ function SetPrintImage($which_pi) { @@ -1327,110 +1983,140 @@ class PHPlot { return TRUE; } - /*! - * Sets the graph's legend. If argument is not an array, appends it to the legend. + /* + * Set text to display in the graph's legend. + * $which_leg : Array of strings for the complete legend, or a single string + * to be appended to the legend. */ function SetLegend($which_leg) { if (is_array($which_leg)) { // use array $this->legend = $which_leg; - return TRUE; - } else if (! is_null($which_leg)) { // append string + } elseif (! is_null($which_leg)) { // append string $this->legend[] = $which_leg; - return TRUE; } else { - $this->DrawError("SetLegend(): argument must not be null."); - return FALSE; + return $this->PrintError("SetLegend(): argument must not be null."); } + return TRUE; } - /*! - * Specifies the absolute (relative to image's up/left corner) position - * of the legend's upper/leftmost corner. - * $which_type not yet used (TODO) + /* + * Specifies the position of the legend's upper/leftmost corner, + * in pixel (device) coordinates. */ - function SetLegendPixels($which_x, $which_y, $which_type=NULL) - { + 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 relative (to graph's origin) position of the legend's - * upper/leftmost corner. MUST be called after scales are set up. - * $which_type not yet used (TODO) + /* + * Specifies the position of the legend's upper/leftmost corner, + * in world (data space) coordinates. */ - function SetLegendWorld($which_x, $which_y, $which_type=NULL) - { - if (! isset($this->scale_is_set)) - $this->CalcTranslation(); - - $this->legend_x_pos = $this->xtr($which_x); - $this->legend_y_pos = $this->ytr($which_y); + function SetLegendWorld($which_x, $which_y) + { + // Since conversion from world to pixel coordinates is not yet available, just + // remember the coordinates and set a flag to indicate conversion is needed. + $this->legend_x_pos = $which_x; + $this->legend_y_pos = $which_y; + $this->legend_xy_world = TRUE; return TRUE; } - /*! - * Accepted values are: left, sides, none, full + /* + * Set legend text alignment, color box alignment, and style options. + * $text_align : Alignment of the text, 'left' or 'right'. + * $colorbox_align : Alignment of the color boxes, 'left', 'right', 'none', or missing/empty. + * If missing or empty, the same alignment as $text_align is used. Color box is positioned first. + * $style : 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->CheckOption($pbt, 'left, sides, none, full', __FUNCTION__); + $this->plot_border_type = $this->CheckOptionArray($pbt, 'left, right, top, bottom, sides, none, full', + __FUNCTION__); + return !empty($this->plot_border_type); } - /*! - * Accepted values are: raised, plain + /* + * 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) + function SetImageBorderType($sibt) { - $this->image_border_type = $this->CheckOption($sibt, 'raised, plain', __FUNCTION__); + $this->image_border_type = $this->CheckOption($sibt, 'raised, plain, solid, none', __FUNCTION__); + return (boolean)$this->image_border_type; } + /* + * Set border width for the image to $width in pixels. + */ + function SetImageBorderWidth($width) + { + $this->image_border_width = $width; + return TRUE; + } - /*! - * \param dpab bool + /* + * Enable or disable drawing of the plot area background color. */ function SetDrawPlotAreaBackground($dpab) { $this->draw_plot_area_background = (bool)$dpab; + return TRUE; } - - /*! - * \param dyg bool + /* + * Enable or disable drawing of the X grid lines. */ - function SetDrawYGrid($dyg) + function SetDrawXGrid($dxg) { - $this->draw_y_grid = (bool)$dyg; + $this->draw_x_grid = (bool)$dxg; return TRUE; } - - /*! - * \param dxg bool + /* + * Enable or disable drawing of the Y grid lines. */ - function SetDrawXGrid($dxg) + function SetDrawYGrid($dyg) { - $this->draw_x_grid = (bool)$dxg; + $this->draw_y_grid = (bool)$dyg; return TRUE; } - - /*! - * \param ddg bool + /* + * Select dashed or solid grid lines. + * $ddg : True for dashed grid lines, false for solid grid lines. */ - function SetDrawDashedGrid($ddg) + function SetDrawDashedGrid($ddg) { $this->dashed_grid = (bool)$ddg; return TRUE; } - - /*! - * \param dxdl bool + /* + * Enable or disable drawing of X Data Label Lines. */ function SetDrawXDataLabelLines($dxdl) { @@ -1438,748 +2124,1265 @@ class PHPlot { return TRUE; } - - /*! - * TODO: draw_y_data_label_lines not implemented. - * \param dydl bool - */ - function SetDrawYDataLabelLines($dydl) - { - $this->draw_y_data_label_lines = $dydl; - return TRUE; - } - - /*! - * Sets the graph's title. - * TODO: add parameter to choose title placement: left, right, centered= + /* + * Set the main title text for the plot. */ - function SetTitle($which_title) + function SetTitle($which_title) { $this->title_txt = $which_title; - - if ($which_title == '') { - $this->title_height = 0; - return TRUE; - } - - $str = split("\n", $which_title); - $lines = count($str); - $spacing = $this->line_spacing * ($lines - 1); - - if ($this->use_ttf) { - $size = $this->TTFBBoxSize($this->title_font['size'], 0, $this->title_font['font'], $which_title); - $this->title_height = $size[1] * $lines; - } else { - $this->title_height = ($this->title_font['height'] + $spacing) * $lines; - } return TRUE; } - /*! - * Sets the X axis title and position. + /* + * Set the X axis title and position. */ - function SetXTitle($which_xtitle, $which_xpos = 'plotdown') + 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; + } - $str = split("\n", $which_xtitle); - $lines = count($str); - $spacing = $this->line_spacing * ($lines - 1); - - if ($this->use_ttf) { - $size = $this->TTFBBoxSize($this->x_title_font['size'], 0, $this->x_title_font['font'], $which_xtitle); - $this->x_title_height = $size[1] * $lines; - } else { - $this->x_title_height = ($this->y_title_font['height'] + $spacing) * $lines; - } - - return TRUE; - } - - - /*! - * Sets the Y axis title and position. + /* + * Set the Y axis title and position. */ - function SetYTitle($which_ytitle, $which_ypos = 'plotleft') + 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; - - $str = split("\n", $which_ytitle); - $lines = count($str); - $spacing = $this->line_spacing * ($lines - 1); - - if ($this->use_ttf) { - $size = $this->TTFBBoxSize($this->y_title_font['size'], 90, $this->y_title_font['font'], - $which_ytitle); - $this->y_title_width = $size[0] * $lines; - } else { - $this->y_title_width = ($this->y_title_font['height'] + $spacing) * $lines; - } - return TRUE; } - /*! - * Sets the size of the drop shadow for bar and pie charts. - * \param which_s int Size in pixels. + /* + * Set the size of the drop shadow for bar and pie charts. + * $which_s : Size of the drop shadow in pixels. */ - function SetShading($which_s) - { + function SetShading($which_s) + { $this->shading = (int)$which_s; return TRUE; } - function SetPlotType($which_pt) + /* + * Set the plot type (bars, points, ...) + */ + function SetPlotType($which_pt) { - $this->plot_type = $this->CheckOption($which_pt, - 'bars, stackedbars, lines, linepoints, area, points, pie, thinbarline, squared', + $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. + /* + * Set the position of the X axis. + * $pos : Axis position in world coordinates (as an integer). */ - function SetYAxisPosition($pos) + function SetXAxisPosition($pos) { - $this->y_axis_position = (int)$pos; - if (isset($this->scale_is_set)) { - $this->CalcTranslation(); - } + $this->x_axis_position = (int)$pos; return TRUE; } - - /*! - * Sets the position of X axis. - * \param pos int Position in world coordinates. + + /* + * Set the position of the Y axis. + * $pos : Axis position in world coordinates (as an integer). */ - function SetXAxisPosition($pos) + function SetYAxisPosition($pos) { - $this->x_axis_position = (int)$pos; - if (isset($this->scale_is_set)) { - $this->CalcTranslation(); - } + $this->y_axis_position = (int)$pos; return TRUE; } - + /* + * Select linear or log scale for the X axis. + */ function SetXScaleType($which_xst) { $this->xscale_type = $this->CheckOption($which_xst, 'linear, log', __FUNCTION__); - return TRUE; + return (boolean)$this->xscale_type; } + /* + * Select linear or log scale for the Y axis. + */ function SetYScaleType($which_yst) { $this->yscale_type = $this->CheckOption($which_yst, 'linear, log', __FUNCTION__); - return TRUE; + return (boolean)$this->yscale_type; } + /* + * Set the precision for numerically formatted X labels. + * $which_prec : Number of digits to display. + * Note: This is equivalent to: SetXLabelType('data', $which_prec) + */ function SetPrecisionX($which_prec) { - $this->x_precision = $which_prec; - $this->SetXLabelType('data'); - return TRUE; + return $this->SetXLabelType('data', $which_prec); } + /* + * Set the precision for numerically formatted Y labels. + * $which_prec : Number of digits to display. + * Note: This is equivalent to: SetYLabelType('data', $which_prec) + */ function SetPrecisionY($which_prec) { - $this->y_precision = $which_prec; - $this->SetYLabelType('data'); - return TRUE; + return $this->SetYLabelType('data', $which_prec); } + /* + * Set the line width (in pixels) for error bars. + */ function SetErrorBarLineWidth($which_seblw) { $this->error_bar_line_width = $which_seblw; return TRUE; } + /* + * Set the position for pie chart percentage labels. + * $which_blb : Real number between 0 and 1. + * Smaller values move the labels in towards the center. + */ function SetLabelScalePosition($which_blp) { - //0 to 1 $this->label_scale_position = $which_blp; return TRUE; } + /* + * Set the size (in pixels) of the "T" in error bars. + */ function SetErrorBarSize($which_ebs) { - //in pixels $this->error_bar_size = $which_ebs; return TRUE; } - /*! - * Can be one of: 'tee', 'line' + /* + * Set the shape of the in error bars. + * $which_ebs : Error bar shape, 'tee' or 'line'. */ function SetErrorBarShape($which_ebs) { $this->error_bar_shape = $this->CheckOption($which_ebs, 'tee, line', __FUNCTION__); + return (boolean)$this->error_bar_shape; } - /*! - * Sets point shape for each data set via an array. - * Shape can be one of: 'halfline', 'line', 'plus', 'cross', 'rect', 'circle', 'dot', - * 'diamond', 'triangle', 'trianglemid' + /* + * 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; + } elseif ($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. + } + + /* + * Set the point shape for each data set. + * $which_pt : Array (or single value) of valid point shapes. + * The point shape and point sizes arrays are synchronized before drawing a graph + * that uses points. See CheckPointParams() */ function SetPointShapes($which_pt) { - if (is_null($which_pt)) { - // Do nothing, use default value. - } else if (is_array($which_pt)) { - // Did we get an array with point shapes? + if (is_array($which_pt)) { + // Use provided array: $this->point_shapes = $which_pt; - } else { - // Single value into array + } 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) { - // TODO, better check, per element rectification - $this->CheckOption($shape, - 'halfline, line, plus, cross, rect, circle, dot, diamond, triangle, trianglemid', - __FUNCTION__); - } - - // Make both point_shapes and point_sizes same size. - $ps = count($this->point_sizes); - $pt = count($this->point_shapes); - - if ($ps < $pt) { - array_pad_array($this->point_sizes, $pt); - } else if ($pt > $ps) { - array_pad_array($this->point_shapes, $ps); + 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. - * \param ps int Size in pixels. - * \note Test this more extensively + /* + * Set the point size for point plots. + * $which_ps : Array (or single value) of point sizes in pixels. + * The point shape and point sizes arrays are synchronized before drawing a graph + * that uses points. See CheckPointParams() */ function SetPointSizes($which_ps) { - if (is_null($which_ps)) { - // Do nothing, use default value. - } else if (is_array($which_ps)) { - // Did we get an array with point sizes? + if (is_array($which_ps)) { + // Use provided array: $this->point_sizes = $which_ps; - } else { - // Single value into array + } elseif (!is_null($which_ps)) { + // Make the single value into an array: $this->point_sizes = array($which_ps); } - - // Make both point_shapes and point_sizes same size. - $ps = count($this->point_sizes); - $pt = count($this->point_shapes); - - if ($ps < $pt) { - array_pad_array($this->point_sizes, $pt); - } else if ($pt > $ps) { - array_pad_array($this->point_shapes, $ps); - } - - // Fix odd point sizes for point shapes which need it - for ($i = 0; $i < $pt; $i++) { - if ($this->point_shapes[$i] == 'diamond' or $this->point_shapes[$i] == 'triangle') { - if ($this->point_sizes[$i] % 2 != 0) { - $this->point_sizes[$i]++; - } - } - } return TRUE; } - - /*! - * Tells not to draw lines for missing Y data. Only works with 'lines' and 'squared' plots. - * \param bl bool + /* + * Sets whether lines should be broken at missing data. + * $bl : True to break the lines, false to connect around missing data. + * This only works with 'lines' and 'squared' plots. */ 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-, ...) + * data-data-yx: ('label', y, x1, x2, x3, ..) + * 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'; }; - if ($which_dt == 'linear-linear') { $which_dt = 'data-data'; }; - if ($which_dt == 'linear-linear-error') { $which_dt = 'data-data-error'; }; - if ($which_dt == 'text-data-pie') { $which_dt = 'text-data-single'; } - + 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', __FUNCTION__); - return TRUE; + 'data-data, data-data-error, '. + 'data-data-yx, 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) + function SetDataValues($which_dv) { - unset ($this->data_limits_done); // Reset this for every new data_set $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->total_records = 0; + $this->data = array(); + $this->num_recs = array(); + for ($i = 0; $i < $this->num_data_rows; $i++) { $this->data[$i] = array_values($which_dv[$i]); // convert to numerical indices. - // Compute some values - $recs = count($this->data[$i]); + // Count size of each row, and total for the array. + $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; } + // This is the size of the widest row in the data array + // Note records_per_group isn't used much anymore. See data_columns in CheckDataArray() + $this->records_per_group = max($this->num_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. + * The arrays are padded to data_columns which is the maximum number of data sets. + * See CheckDataArray() for the calculation. */ - function PadArrays() + protected function PadArrays() { - array_pad_array($this->line_widths, $this->records_per_group); - array_pad_array($this->line_styles, $this->records_per_group); + $this->pad_array($this->line_widths, $this->data_columns); + $this->pad_array($this->line_styles, $this->data_columns); + $this->pad_array($this->ndx_data_colors, $this->data_columns); + $this->pad_array($this->ndx_data_border_colors, $this->data_columns); + // Other data color arrays are handled in the Need*Colors() functions. - array_pad_array($this->data_colors, $this->records_per_group); - array_pad_array($this->data_border_colors, $this->records_per_group); - array_pad_array($this->error_bar_colors, $this->records_per_group); + return TRUE; + } - $this->SetDataColors(); - $this->SetDataBorderColors(); - $this->SetErrorBarColors(); + /* + * Pads an array with itself. This only works on 0-based sequential integer indexed arrays. + * $arr : The array (or scalar) to pad. This argument is modified. + * $size : 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++]; + } + + /* + * Truncate an array to a maximum size. + * This only works on 0-based sequential integer indexed arrays. + * $arr : The array to truncate. + * $size : Maximum size of the resulting array. + */ + protected function truncate_array(&$arr, $size) + { + for ($n = count($arr) - 1; $n >= $size; $n--) unset($arr[$n]); + } + + /* + * Format a floating-point number. + * $number : A floating point number to format + * $decimals : Number of decimal places in the result + * Returns the formatted result. + * This is like PHP's number_format, but uses class variables for separators. + * The separators will default to locale-specific values, if available. + */ + protected 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: whatever value (if any) was returned by the callback. + */ + 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... + return call_user_func_array($function, $args); + } + + /* + * Allocate colors for the plot. + * This is called by DrawGraph to allocate the colors needed for the plot. Each selectable + * color has already been validated, parsed into an array (r,g,b,a), and stored into a member + * variable. Now the GD color indexes are assigned and stored into the ndx_*_color variables. + * This is deferred here to avoid allocating unneeded colors and to avoid order dependencies, + * especially with the transparent color. + * + * For drawing data elements, only the main data colors and border colors are allocated here. + * Dark colors and error bar colors are allocated by Need*Color() functions. + * (Data border colors default to just black, so there is no cost to always allocating.) + * + * Data color allocation works as follows. If there is a data_color callback, then allocate all + * defined data colors (because the callback can use them however it wants). Otherwise, truncate + * the array to the number of colors that will be used. This is the larger of the number of data + * sets and the number of legend lines. + */ + protected function SetColorIndexes() + { + $this->ndx_bg_color = $this->GetColorIndex($this->bg_color); // Background first + $this->ndx_plot_bg_color = $this->GetColorIndex($this->plot_bg_color); + if ($this->image_border_type != 'none') { + $this->ndx_i_border = $this->GetColorIndex($this->i_border); + $this->ndx_i_border_dark = $this->GetDarkColorIndex($this->i_border); + } + + // Handle defaults for X and Y title colors. + $this->ndx_title_color = $this->GetColorIndex($this->title_color); + if (empty($this->x_title_color)) { + $this->ndx_x_title_color = $this->ndx_title_color; + } else { + $this->ndx_x_title_color = $this->GetColorIndex($this->x_title_color); + } + if (empty($this->y_title_color)) { + $this->ndx_y_title_color = $this->ndx_title_color; + } else { + $this->ndx_y_title_color = $this->GetColorIndex($this->y_title_color); + } + + $this->ndx_text_color = $this->GetColorIndex($this->text_color); + $this->ndx_grid_color = $this->GetColorIndex($this->grid_color); + $this->ndx_light_grid_color = $this->GetColorIndex($this->light_grid_color); + $this->ndx_tick_color = $this->GetColorIndex($this->tick_color); + + // If no data_color callback is being used, only allocate needed colors. + if (!$this->GetCallback('data_color')) { + $data_colors_needed = max($this->data_columns, empty($this->legend) ? 0 : count($this->legend)); + $this->truncate_array($this->data_colors, $data_colors_needed); + $this->truncate_array($this->data_border_colors, $data_colors_needed); + $this->truncate_array($this->error_bar_colors, $data_colors_needed); + } + + // Allocate main data colors. For other colors used for data, see the functions which follow. + $getcolor_cb = array($this, 'GetColorIndex'); + $this->ndx_data_colors = array_map($getcolor_cb, $this->data_colors); + $this->ndx_data_border_colors = array_map($getcolor_cb, $this->data_border_colors); + + // Set up a color as transparent, if SetTransparentColor was used. + if (!empty($this->transparent_color)) { + imagecolortransparent($this->img, $this->GetColorIndex($this->transparent_color)); + } + } + + /* + * Allocate dark-shade data colors. Called if needed by graph drawing functions. + */ + protected function NeedDataDarkColors() + { + $getdarkcolor_cb = array($this, 'GetDarkColorIndex'); + $this->ndx_data_dark_colors = array_map($getdarkcolor_cb, $this->data_colors); + $this->pad_array($this->ndx_data_dark_colors, $this->data_columns); + } + + /* + * Allocate error bar colors. Called if needed by graph drawing functions. + */ + protected function NeedErrorBarColors() + { + $getcolor_cb = array($this, 'GetColorIndex'); + $this->ndx_error_bar_colors = array_map($getcolor_cb, $this->error_bar_colors); + $this->pad_array($this->ndx_error_bar_colors, $this->data_columns); + } ////////////////////////////////////////////////////////// /////////// DATA ANALYSIS, SCALING AND TRANSLATION ////////////////////////////////////////////////////////// - /*! - * Analizes data and sets up internal maxima and minima - * Needed by: CalcMargins(), ... - * Text-Data is different than data-data graphs. For them what - * we have, instead of X values, is # of records equally spaced on data. - * text-data is passed in as $data[] = (title, y1, y2, y3, y4, ...) - * data-data is passed in as $data[] = (title, x, y1, y2, y3, y4, ...) - */ - function FindDataLimits() - { - // Set some default min and max values before running through the data - switch ($this->data_type) { - case 'text-data': - $minx = 0; - $maxx = $this->num_data_rows - 1 ; - $miny = $this->data[0][1]; - $maxy = $miny; - break; - default: //Everything else: data-data, etc, take first value - $minx = $this->data[0][1]; - $maxx = $minx; - $miny = $this->data[0][2]; - $maxy = $miny; - break; - } - - $mine = 0; // Maximum value for the -error bar (assume error bars always > 0) - $maxe = 0; // Maximum value for the +error bar (assume error bars always > 0) - $maxt = 0; // Maximum number of characters in text labels + /* + * 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. + * + * This has to know how certain plot types use the data. 'area' and 'pie' use absolute + * values, 'stackedbars' sums values, and 'stackedarea' sums absolute values. + * + * 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. + */ + protected function FindDataLimits() + { + // Special case processing for certain plot types: + $sum_abs = ($this->plot_type == 'stackedarea'); // Sum of absolute values + $sum_val = ($this->plot_type == 'stackedbars'); // Sum of values + $abs_val = ($this->plot_type == 'area' || $this->plot_type == 'pie'); // Absolute values - $minminy = $miny; - $maxmaxy = $maxy; + // These need to be initialized in case there are multiple plots and missing data points. + $this->data_min = array(); + $this->data_max = array(); - if ($this->plot_type == 'stackedbars') { $maxmaxy = $minminy = 0; } + // Independent values are in the data array or assumed? + if ($this->datatype_implied) { + $all_iv = array(0, $this->num_data_rows - 1); + } else { + $all_iv = array(); + } - // Process each row of data - for ($i=0; $i < $this->num_data_rows; $i++) { - $j=0; - // Extract maximum text label length - $val = @ strlen($this->data[$i][$j++]); - $maxt = ($val > $maxt) ? $val : $maxt; + // 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 (!$this->datatype_implied) { + $all_iv[] = (double)$this->data[$i][$j++]; + } - if ($this->plot_type == 'stackedbars') { $maxy = $miny = 0; } + if ($sum_abs || $sum_val) { + $all_dv = array(0, 0); // One limit is 0, other calculated below + } else { + $all_dv = array(); + } + while ($j < $n_vals) { + if (is_numeric($this->data[$i][$j])) { + $val = (double)$this->data[$i][$j++]; - switch ($this->data_type) { - case 'text-data': // Data is passed in as (title, y1, y2, y3, ...) - case 'text-data-single': // This one is for some pie charts - // $numrecs = @ count($this->data[$i]); - $miny = $maxy = (double)$this->data[$i][$j]; - for (; $j < $this->num_recs[$i]; $j++) { - $val = (double)$this->data[$i][$j]; - if ($this->plot_type == 'stackedbars') { - $maxy += abs($val); // only positive values for the moment + if ($this->datatype_error_bars) { + $all_dv[] = $val + (double)$this->data[$i][$j++]; + $all_dv[] = $val - (double)$this->data[$i][$j++]; + } elseif ($sum_abs) { + $all_dv[1] += abs($val); // Sum of absolute values + } elseif ($sum_val) { + $all_dv[1] += $val; // Sum of values + } elseif ($abs_val) { + $all_dv[] = abs($val); // List of all absolute values } else { - $maxy = ($val > $maxy) ? $val : $maxy; - $miny = ($val < $miny) ? $val : $miny; + $all_dv[] = $val; // List of all values } + } else { // Missing DV value + $j++; + if ($this->datatype_error_bars) $j += 2; } - break; - case 'data-data': // Data is passed in as (title, x, y, y2, y3, ...) - // X value: - $val = (double)$this->data[$i][$j++]; - $maxx = ($val > $maxx) ? $val : $maxx; - $minx = ($val < $minx) ? $val : $minx; - - $miny = $maxy = (double)$this->data[$i][$j]; - // $numrecs = @ count($this->data[$i]); - for (; $j < $this->num_recs[$i]; $j++) { - $val = (double)$this->data[$i][$j]; - $maxy = ($val > $maxy) ? $val : $maxy; - $miny = ($val < $miny) ? $val : $miny; - } - break; - case 'data-data-error': // Data is passed in as (title, x, y, err+, err-, y2, err2+, err2-,...) - // X value: - $val = (double)$this->data[$i][$j++]; - $maxx = ($val > $maxx) ? $val : $maxx; - $minx = ($val < $minx) ? $val : $minx; - - $miny = $maxy = (double)$this->data[$i][$j]; - // $numrecs = @ count($this->data[$i]); - for (; $j < $this->num_recs[$i];) { - // Y value: - $val = (double)$this->data[$i][$j++]; - $maxy = ($val > $maxy) ? $val : $maxy; - $miny = ($val < $miny) ? $val : $miny; - // Error +: - $val = (double)$this->data[$i][$j++]; - $maxe = ($val > $maxe) ? $val : $maxe; - // Error -: - $val = (double)$this->data[$i][$j++]; - $mine = ($val > $mine) ? $val : $mine; - } - $maxy = $maxy + $maxe; - $miny = $miny - $mine; // assume error bars are always > 0 - break; - default: - $this->PrintError("FindDataLimits(): Unknown data type '$data_type'."); - break; } - $this->data[$i][MINY] = $miny; // This row's min Y, for DrawXDataLine() - $this->data[$i][MAXY] = $maxy; // This row's max Y, for DrawXDataLine() - - $minminy = ($miny < $minminy) ? $miny : $minminy; // global min - $maxmaxy = ($maxy > $maxmaxy) ? $maxy : $maxmaxy; // global max + if (!empty($all_dv)) { + $this->data_min[$i] = min($all_dv); // Store per-row DV range + $this->data_max[$i] = max($all_dv); + } } - $this->min_x = $minx; - $this->max_x = $maxx; - $this->min_y = $minminy; - $this->max_y = $maxmaxy; - $this->max_t = $maxt; - - $this->data_limits_done = TRUE; + if ($this->datatype_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. * - * FIXME: fix x_data_label_pos behaviour. Now we are leaving room for it AND x_tick_label_pos - * maybe it shouldn't be so... + * A picture of the locations of elements and spacing can be found in the + * PHPlot Reference Manual. * - * FIXME: y_data_label_pos is not yet used... + * Calculates the following (class variables unless noted): * - * TODO: add x_tick_label_width and y_tick_label_height and use them to calculate - * max_x_labels and max_y_labels, to be used by drawing functions to avoid overlapping. - */ - function CalcMargins() - { - // Temporary variables for label size calculation - $xlab = $this->FormatLabel('x', $this->max_x); - $ylab = $this->FormatLabel('y', $this->max_y); + * 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; - // dirty fix: - // max_t is the maximum data label length (from column 0 of each data row). - if ($this->max_t > strlen ($xlab)) - $xlab = sprintf ("%{$this->max_t}s","_"); + return TRUE; + } - //////// Calculate maximum X/Y axis label height and width: + // 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; + } - // TTFonts: - if ($this->use_ttf) { - // Maximum X axis label height - $size = $this->TTFBBoxSize($this->x_label_font['size'], $this->x_label_angle, - $this->x_label_font['font'], $xlab); - $this->x_tick_label_height = $size[1]; - - // Maximum Y axis label width - $size = $this->TTFBBoxSize($this->y_label_font['size'], $this->y_label_angle, - $this->y_label_font['font'], $ylab); - $this->y_tick_label_width = $size[0]; - } - // Fixed fonts: - else { - // Maximum X axis label height - if ($this->x_label_angle == 90) - $this->x_tick_label_height = strlen($xlab) * $this->x_label_font['width']; - else - $this->x_tick_label_height = $this->x_label_font['height']; + // 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; + } - // Maximum Y axis label width - $this->y_tick_label_width = strlen($ylab) * $this->y_label_font['width']; + // 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: - // Upper title, ticks and tick labels, and data labels: - $this->y_top_margin = $this->title_height + $this->safe_margin * 2; - - if ($this->x_title_pos == 'plotup' || $this->x_title_pos == 'both') - $this->y_top_margin += $this->x_title_height + $this->safe_margin; - - if ($this->x_tick_label_pos == 'plotup' || $this->x_tick_label_pos == 'both') - $this->y_top_margin += $this->x_tick_label_height; - - if ($this->x_tick_pos == 'plotup' || $this->x_tick_pos == 'both') - $this->y_top_margin += $this->x_tick_length * 2; - - if ($this->x_data_label_pos == 'plotup' || $this->x_data_label_pos == 'both') - $this->y_top_margin += $this->x_tick_label_height; - - // Lower title, ticks and tick labels, and data labels: - $this->y_bot_margin = $this->safe_margin * 2; - - if ($this->x_title_pos == 'plotdown' || $this->x_title_pos == 'both') - $this->y_bot_margin += $this->x_title_height; - - if ($this->x_tick_pos == 'plotdown' || $this->x_tick_pos == 'both') - $this->y_bot_margin += $this->x_tick_length * 2; - - if ($this->x_tick_pos == 'xaxis' && ($this->x_axis_position == '' || $this->x_axis_position == 0)) - $this->y_bot_margin += $this->x_tick_length * 2; + // 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; + } - if ($this->x_tick_label_pos == 'plotdown' || $this->x_tick_label_pos == 'both') - $this->y_bot_margin += $this->x_tick_label_height; + // 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; + } - if ($this->x_tick_label_pos == 'xaxis' && ($this->x_axis_position == '' || $this->x_axis_position == 0)) - $this->y_bot_margin += $this->x_tick_label_height; + // 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; + } - if ($this->x_data_label_pos == 'plotdown' || $this->x_data_label_pos == 'both') - $this->y_bot_margin += $this->x_tick_label_height; + // 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; + } - // Left title, ticks and tick labels: - $this->x_left_margin = $this->safe_margin * 2; + // 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; + } - if ($this->y_title_pos == 'plotleft' || $this->y_title_pos == 'both') - $this->x_left_margin += $this->y_title_width + $this->safe_margin; + // 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; + } - if ($this->y_tick_label_pos == 'plotleft' || $this->y_tick_label_pos == 'both') - $this->x_left_margin += $this->y_tick_label_width; + // 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; + } - if ($this->y_tick_pos == 'plotleft' || $this->y_tick_pos == 'both') - $this->x_left_margin += $this->y_tick_length * 2 ; + // 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; + } - // Right title, ticks and tick labels: - $this->x_right_margin = $this->safe_margin * 2; + // 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; + } - if ($this->y_title_pos == 'plotright' || $this->y_title_pos == 'both') - $this->x_right_margin += $this->y_title_width + $this->safe_margin; + // 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; + } - if ($this->y_tick_label_pos == 'plotright' || $this->y_tick_label_pos == 'both') - $this->x_right_margin += $this->y_tick_label_width; + // 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; + } - if ($this->y_tick_pos == 'plotright' || $this->y_tick_pos == 'both') - $this->x_right_margin += $this->y_tick_length * 2; + // 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; + } - $this->x_tot_margin = $this->x_left_margin + $this->x_right_margin; - $this->y_tot_margin = $this->y_top_margin + $this->y_bot_margin; + /* + * 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]; - return; + $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, $which_rm, $which_tm, $which_bm) - { - + 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->x_tot_margin = $which_lm + $which_rm; - $this->y_top_margin = $which_tm; $this->y_bot_margin = $which_bm; - $this->y_tot_margin = $which_tm + $which_bm; - - $this->SetPlotAreaPixels(); - return; + return TRUE; } - - /*! - * Sets the limits for the plot area. If no arguments are supplied, uses - * values calculated from CalcMargins(); - * Like in GD, (0,0) is upper left - * - * This resets the scale if SetPlotAreaWorld() was already called - */ - function SetPlotAreaPixels($x1=NULL, $y1=NULL, $x2=NULL, $y2=NULL) - { - if ($x2 && $y2) { - $this->plot_area = array($x1, $y1, $x2, $y2); - } else { - if (! isset($this->x_tot_margin)) - $this->CalcMargins(); - - $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]; - - // Reset the scale with the new plot area. - if (isset($this->plot_max_x)) - $this->CalcTranslation(); + /* + * 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; - } - - /*! - * Sets minimum and maximum x and y values in the plot using FindDataLimits() - * or from the supplied parameters, if any. + /* + * 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. * - * This resets the scale if SetPlotAreaPixels() was already called - */ - function SetPlotAreaWorld($xmin=NULL, $ymin=NULL, $xmax=NULL, $ymax=NULL) - { - if (! isset($this->data_limits_done)) { // For automatic setting of data we need data limits - $this->FindDataLimits() ; - } - - if ($xmin === NULL || $xmin === '') { - if ($this->data_type == 'text-data') // Valid for data without X values only. - $xmin = 0; - else - $xmin = $this->min_x; - } - if ($xmax === NULL || $xmax === '') { - if ($this->data_type == 'text-data') // Valid for data without X values only. - $xmax = $this->max_x + 1; - else - $xmax = $this->max_x; - } + * 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() + { + // Data array omits X or Y? + $implied_x = $this->datatype_implied && !$this->datatype_swapped_xy; + $implied_y = $this->datatype_implied && $this->datatype_swapped_xy; + + 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 ($this->datatype_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 ($this->datatype_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 ($this->datatype_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 ($this->datatype_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); - // Leave room above and below the highest and lowest data points. - - if ($ymin === NULL || $ymin === '') { - if ($this->min_y < 0) - $ymin = ceil($this->min_y * 1.1); - else - $ymin = floor($this->min_y * 0.9); - } - if ($ymax === NULL || $ymax === '') { - if ($this->max_y < 0) - $ymax = floor($this->max_y * 0.9); - else - $ymax = ceil($this->max_y * 1.1); - } - // Error checking - - if ($ymin == $ymax) // Minimum height - $ymax += 1; + + if ($ymin == $ymax) + $ymax++; + if ($xmin == $xmax) + $xmax++; if ($this->yscale_type == 'log') { - if ($ymin <= 0) { + if ($ymin <= 0) { $ymin = 1; } if ($ymax <= 0) { - $this->PrintError('SetPlotAreaWorld(): Log plots need data greater than 0'); - return FALSE; + // Note: Error messages reference the user function, not this function. + return $this->PrintError('SetPlotAreaWorld(): Log plots need data greater than 0'); } } - + if ($ymax <= $ymin) { - $this->DrawError('SetPlotAreaWorld(): Error in data - max not greater than min'); - return FALSE; + return $this->PrintError('SetPlotAreaWorld(): Error in data - max not greater than min'); } - - - // Reset (if it was already set) the scale with the new maxs and mins - + $this->plot_min_x = $xmin; $this->plot_max_x = $xmax; $this->plot_min_y = $ymin; $this->plot_max_y = $ymax; - - if (isset($this->plot_area_width)) { - $this->CalcTranslation(); + 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; - } //function SetPlotAreaWorld - + } - /*! - * For bar plots, which have equally spaced x variables. + /* + * 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 CalcBarWidths() + function SetPlotAreaWorld($xmin=NULL, $ymin=NULL, $xmax=NULL, $ymax=NULL) { - $group_width = ($this->plot_area[2] - $this->plot_area[0]) / - $this->num_data_rows * $this->group_frac_width; - if ($this->plot_type == 'bars') { - $this->record_bar_width = $group_width / $this->records_per_group; - } else if ($this->plot_type == 'stackedbars') { - $this->record_bar_width = $group_width; - } - $this->data_group_space = $group_width / 2; + $this->plot_min_x = $xmin; + $this->plot_max_x = $xmax; + $this->plot_min_y = $ymin; + $this->plot_max_y = $ymax; + return TRUE; + } + + /* + * 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. + if ($this->plot_type == 'stackedbars') { + $num_spots = 1 + $this->bar_extra_space; + } else { + $num_spots = $this->data_columns + $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). + * + * For vertical plots, the X axis defaults to Y=0 if that is inside the plot range, else whichever + * of the top or bottom that has the smallest absolute value (that is, the value closest to 0). + * The Y axis defaults to the left edge. For horizontal plots, the axis roles and defaults are switched. + */ + protected function CalcAxisPositions() + { + // Validate user-provided X axis position, or calculate a default if not provided: + if ($this->x_axis_position !== '') { + // Force user-provided X axis position to be within the plot 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->datatype_swapped_xy || $this->plot_min_y > 0) { + // Horizontal plot, or Vertical Plot with all Y > 0: Place X axis on the bottom. + $this->x_axis_position = $this->plot_min_y; + } elseif ($this->plot_max_y < 0) { + // Vertical plot with all Y < 0, so place the X axis at the top. + $this->x_axis_position = $this->plot_max_y; + } else { + // Vertical plot range includes Y=0, so place X axis at 0. + $this->x_axis_position = 0; + } + + // Validate user-provided Y axis position, or calculate a default if not provided: + if ($this->y_axis_position !== '') { + // Force user-provided Y axis position to be within the plot range: + $this->y_axis_position = min(max($this->plot_min_x, $this->y_axis_position), $this->plot_max_x); + } elseif ($this->xscale_type == 'log') { + // Always use 1 for Y axis position on log scale plots. + $this->y_axis_position = 1; + } elseif (!$this->datatype_swapped_xy || $this->plot_min_x > 0) { + // Vertical plot, or Horizontal Plot with all X > 0: Place Y axis on left side. + $this->y_axis_position = $this->plot_min_x; + } elseif ($this->plot_max_x < 0) { + // Horizontal plot with all X < 0, so place the Y axis on the right side. + $this->y_axis_position = $this->plot_max_x; + } else { + // Horizontal plot range includes X=0: place Y axis at 0. + $this->y_axis_position = 0; + } + + 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... */ - function CalcTranslation() + 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)); + $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); + $this->xscale = $this->plot_area_width / ($this->plot_max_x - $this->plot_min_x); } } @@ -2187,9 +3390,10 @@ class PHPlot { $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)); + $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); + $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 @@ -2200,233 +3404,497 @@ class PHPlot { } if ($this->yscale_type == 'log') { $this->plot_origin_y = $this->plot_area[3] + ($this->yscale * log10($this->plot_min_y)); - } else { + } else { $this->plot_origin_y = $this->plot_area[3] + ($this->yscale * $this->plot_min_y); } - $this->scale_is_set = TRUE; + // 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); - /************** FIXME?? *************/ - // There should be a better place for this. - - // User provided y axis position? - if ($this->y_axis_position != '') { - // Make sure we draw our axis inside the plot - $this->y_axis_position = ($this->y_axis_position < $this->plot_min_x) - ? $this->plot_min_x : $this->y_axis_position; - $this->y_axis_position = ($this->y_axis_position > $this->plot_max_x) - ? $this->plot_max_x : $this->y_axis_position; - $this->y_axis_x_pixels = $this->xtr($this->y_axis_position); - } else { - // Default to left axis - $this->y_axis_x_pixels = $this->xtr($this->plot_min_x); - } - // User provided x axis position? - if ($this->x_axis_position != '') { - // Make sure we draw our axis inside the plot - $this->x_axis_position = ($this->x_axis_position < $this->plot_min_y) - ? $this->plot_min_y : $this->x_axis_position; - $this->x_axis_position = ($this->x_axis_position > $this->plot_max_y) - ? $this->plot_max_y : $this->x_axis_position; - $this->x_axis_y_pixels = $this->ytr($this->x_axis_position); - } else { - if ($this->yscale_type == 'log') - $this->x_axis_y_pixels = $this->ytr(1); - else - // Default to axis at 0 or plot_min_y (should be 0 anyway, from SetPlotAreaWorld()) - $this->x_axis_y_pixels = ($this->plot_min_y <= 0) && (0 <= $this->plot_max_y) - ? $this->ytr(0) : $this->ytr($this->plot_min_y); + 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)); } - } // function CalcTranslation() - + return TRUE; + } - /*! + /* * Translate X world coordinate into pixel coordinate - * Needs values calculated by _CalcTranslation() + * See CalcTranslation() for calculation of xscale. + * Note: This function should be 'protected', but is left public for historical reasons. + * See GetDeviceXY() for a preferred public method. */ - function xtr($x_world) + function xtr($x_world) { - //$x_pixels = $this->x_left_margin + ($this->image_width - $this->x_tot_margin)* - // (($x_world - $this->plot_min_x) / ($this->plot_max_x - $this->plot_min_x)) ; - //which with a little bit of math reduces to ... - if ($this->xscale_type == 'log') { + if ($this->xscale_type == 'log') { $x_pixels = $this->plot_origin_x + log10($x_world) * $this->xscale ; - } else { + } else { $x_pixels = $this->plot_origin_x + $x_world * $this->xscale ; } return round($x_pixels); } - - /*! + /* * Translate Y world coordinate into pixel coordinate. - * Needs values calculated by _CalcTranslation() + * See CalcTranslation() for calculation of yscale. + * Note: This function should be 'protected', but is left public for historical reasons. + * See GetDeviceXY() for a preferred public method. */ - function ytr($y_world) + 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 ; + } else { + $y_pixels = $this->plot_origin_y - $y_world * $this->yscale ; } return round($y_pixels); } - /*! - * Formats a tick or data label. + /* 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 * - * \note Time formatting suggested by Marlin Viss + * 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') + { + if ($which == 'x') { + if ($this->datatype_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 (!$this->datatype_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 ($this->datatype_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. */ - function FormatLabel($which_pos, $which_lab) + protected function CalcGridSettings() { - switch ($which_pos) { - case 'x': - case 'plotx': - switch ($this->x_label_type) { - case 'title': - $lab = @ $this->data[$which_lab][0]; + if (!isset($this->draw_x_grid)) + $this->draw_x_grid = $this->datatype_swapped_xy; + if (!isset($this->draw_y_grid)) + $this->draw_y_grid = !$this->datatype_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. + + // X Label position fixups, for x_data_label_pos and x_tick_label_pos: + if ($this->datatype_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 (!$this->datatype_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': - $lab = number_format($which_lab, $this->x_precision, '.', ',').$this->data_units_text; + $which_lab = $format['prefix'] + . $this->number_format($which_lab, $format['precision']) + . $this->data_units_text // Obsolete + . $format['suffix']; break; case 'time': - $lab = strftime($this->x_time_format, $which_lab); + $which_lab = strftime($format['time_format'], $which_lab); break; - default: - // Unchanged from whatever format it is passed in - $lab = $which_lab; - break; - } - break; - case 'y': - case 'ploty': - switch ($this->y_label_type) { - case 'data': - $lab = number_format($which_lab, $this->y_precision, '.', ',').$this->data_units_text; + case 'printf': + $which_lab = sprintf($format['printf_format'], $which_lab); break; - case 'time': - $lab = strftime($this->y_time_format, $which_lab); - break; - default: - // Unchanged from whatever format it is passed in - $lab = $which_lab; + case 'custom': + $which_lab = call_user_func($format['custom_callback'], $which_lab, $format['custom_arg']); break; + } - break; - default: - $this->PrintError("FormatLabel(): Unknown label type $which_type"); - return NULL; } + return $which_lab; + } - return $lab; - } //function FormatLabel - - - -///////////////////////////////////////////// +///////////////////////////////////////////// /////////////// TICKS -///////////////////////////////////////////// +///////////////////////////////////////////// - /*! - * Use either this or SetNumXTicks() to set where to place x tick marks + /* + * Set the step (interval) between X ticks. + * Use either this or SetNumXTicks(), not both, to control the X tick marks. */ - function SetXTickIncrement($which_ti=NULL) + function SetXTickIncrement($which_ti='') { - if ($which_ti) { - $this->x_tick_inc = $which_ti; //world coordinates - } else { - if (! isset($this->data_limits_done)) { - $this->FindDataLimits(); //Get maxima and minima for scaling - } - $this->x_tick_inc = ($this->plot_max_x - $this->plot_min_x )/10; + $this->x_tick_inc = $which_ti; + if (!empty($which_ti)) { + $this->num_x_ticks = ''; } - $this->num_x_ticks = ''; //either use num_y_ticks or y_tick_inc, not both return TRUE; } - /*! - * Use either this or SetNumYTicks() to set where to place y tick marks + /* + * Set the step (interval) between Y ticks. + * Use either this or SetNumYTicks(), not both, to control the Y tick marks. */ - function SetYTickIncrement($which_ti=NULL) + function SetYTickIncrement($which_ti='') { - if ($which_ti) { - $this->y_tick_inc = $which_ti; //world coordinates - } else { - if (! isset($this->data_limits_done)) { - $this->FindDataLimits(); //Get maxima and minima for scaling - } - if (! isset($this->plot_max_y)) - $this->SetPlotAreaWorld(); - - $this->y_tick_inc = ($this->plot_max_y - $this->plot_min_y )/10; + $this->y_tick_inc = $which_ti; + if (!empty($which_ti)) { + $this->num_y_ticks = ''; } - $this->num_y_ticks = ''; //either use num_y_ticks or y_tick_inc, not both return TRUE; } - - function SetNumXTicks($which_nt) + /* + * Set the number of X tick marks. + * Use either this or SetXTickIncrement(), not both, to control the X tick marks. + */ + function SetNumXTicks($which_nt) { $this->num_x_ticks = $which_nt; - $this->x_tick_inc = ''; //either use num_x_ticks or x_tick_inc, not both + if (!empty($which_nt)) { + $this->x_tick_inc = ''; + } return TRUE; } - function SetNumYTicks($which_nt) + /* + * Set the number of Y tick marks. + * Use either this or SetYTickIncrement(), not both, to control the Y tick marks. + */ + function SetNumYTicks($which_nt) { $this->num_y_ticks = $which_nt; - $this->y_tick_inc = ''; //either use num_y_ticks or y_tick_inc, not both + if (!empty($which_nt)) { + $this->y_tick_inc = ''; //either use num_y_ticks or y_tick_inc, not both + } return TRUE; } - /*! - * + /* + * Set the position for the X tick marks. + * These can be above the plot, below, both positions, at the X axis, or suppressed. */ - function SetYTickPos($which_tp) - { - $this->y_tick_pos = $this->CheckOption($which_tp, 'plotleft, plotright, both, yaxis, none', __FUNCTION__); - return TRUE; + function SetXTickPos($which_tp) + { + $this->x_tick_pos = $this->CheckOption($which_tp, 'plotdown, plotup, both, xaxis, none', + __FUNCTION__); + return (boolean)$this->x_tick_pos; } - /*! - * + + /* + * Set the position for the Y tick marks. + * These can be left of the plot, right, both positions, at the Y axis, or suppressed. */ - function SetXTickPos($which_tp) - { - $this->x_tick_pos = $this->CheckOption($which_tp, 'plotdown, plotup, both, xaxis, none', __FUNCTION__); - 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; } - /*! - * \param skip bool - */ + /* + * Skip the top-most Y axis tick mark and label if $skip is true. + */ function SetSkipTopTick($skip) { $this->skip_top_tick = (bool)$skip; return TRUE; } - /*! - * \param skip bool + /* + * Skip the bottom-most Y axis tick mark and label if $skip is true. */ - function SetSkipBottomTick($skip) + function SetSkipBottomTick($skip) { $this->skip_bottom_tick = (bool)$skip; return TRUE; } - /*! - * \param skip bool - */ + /* + * Skip the left-most X axis tick mark and label if $skip is true. + */ function SetSkipLeftTick($skip) { $this->skip_left_tick = (bool)$skip; return TRUE; } - /*! - * \param skip bool + /* + * Skip the right-most X axis tick mark and label if $skip is true. */ function SetSkipRightTick($skip) { @@ -2434,39 +3902,54 @@ class PHPlot { return TRUE; } + /* + * Set the outer length of X tick marks to $which_xln pixels. + * This is the part of the tick mark that sticks out from the plot area. + */ function SetXTickLength($which_xln) { $this->x_tick_length = $which_xln; return TRUE; } + /* + * Set the outer length of Y tick marks to $which_yln pixels. + * This is the part of the tick mark that sticks out from the plot area. + */ function SetYTickLength($which_yln) { $this->y_tick_length = $which_yln; return TRUE; } + /* + * Set the crossing length of X tick marks to $which_xc pixels. + * This is the part of the tick mark that sticks into the plot area. + */ function SetXTickCrossing($which_xc) { $this->x_tick_cross = $which_xc; return TRUE; } + /* + * Set the crossing length of Y tick marks to $which_yc pixels. + * This is the part of the tick mark that sticks into the plot area. + */ function SetYTickCrossing($which_yc) { $this->y_tick_cross = $which_yc; return TRUE; } - ///////////////////////////////////////////// //////////////////// GENERIC DRAWING ///////////////////////////////////////////// - /*! - * Fills the background. + /* + * Fill the image background, with a tiled image file or solid color. */ - function DrawBackground() + protected function DrawBackground() { // Don't draw this twice if drawing two plots on one image if (! $this->background_done) { @@ -2477,99 +3960,64 @@ class PHPlot { $this->ndx_bg_color); } $this->background_done = TRUE; - return TRUE; // Done } - return FALSE; // Nothing done + return TRUE; } - - /*! - * Fills the plot area background. + /* + * Fill the plot area background, with a tiled image file or solid color. */ - function DrawPlotAreaBackground() + 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); + } elseif ($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); } - 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. - * - * \param $file string Filename of the picture to be used as tile. - * \param $xorig int X coordinate of the plot where the tile is to begin. - * \param $yorig int Y coordinate of the plot where the tile is to begin. - * \param $width int Width of the area to be tiled. - * \param $height int Height of the area to be tiled. - * \param $mode string One of 'centeredtile', 'tile', 'scale'. - */ - function tile_img($file, $xorig, $yorig, $width, $height, $mode) - { - $size = getimagesize($file); - $input_format = $size[2]; - - switch($input_format) { - case 1: - $im = @ imagecreatefromGIF ($file); - if (! $im) { - $this->PrintError("tile_img:() Unable to open $file as a GIF."); - return FALSE; - } - break; - case 2: - $im = @ imagecreatefromJPEG ($file); - if (! $im) { - $this->PrintError("tile_img(): Unable to open $file as a JPG."); - return FALSE; - } - break; - case 3: - $im = @ imagecreatefromPNG ($file); - if (! $im) { - $this->PrintError("tile_img(): Unable to open $file as a PNG."); - return FALSE; - } - break; - default: - $this->PrintError('tile_img(): Please select a gif, jpg, or png image.'); - return FALSE; - break; - } - + * $file : Filename of the picture to be used as tile. + * $xorig : X device coordinate of where the tile is to begin. + * $yorig : Y device coordinate of where the tile is to begin. + * $width : Width of the area to be tiled. + * $height : Height of the area to be tiled. + * $mode : Tiling mode. 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') { - imagecopyresized($this->img, $im, $xorig, $yorig, 0, 0, $width, $height, $size[0],$size[1]); + imagecopyresampled($this->img, $im, $xorig, $yorig, 0, 0, $width, $height, + $tile_width, $tile_height); return TRUE; - } else if ($mode == 'centeredtile') { - $x0 = - floor($size[0]/2); // Make the tile look better - $y0 = - floor($size[1]/2); - } else if ($mode = 'tile') { + } + + 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); + // Draw the tile onto a temporary image first. + $tmp = imagecreate($width, $height); if (! $tmp) - $this->PrintError('tile_img(): Could not create image resource.'); + return $this->PrintError('tile_img(): Could not create image resource.'); - for ($x = $x0; $x < $width; $x += $size[0]) - for ($y = $y0; $y < $height; $y += $size[1]) - imagecopy($tmp, $im, $x, $y, 0, 0, $size[0], $size[1]); + 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. + // Copy the temporary image onto the final one. imagecopy($this->img, $tmp, $xorig, $yorig, 0,0, $width, $height); // Free resources @@ -2577,125 +4025,159 @@ class PHPlot { imagedestroy($im); return TRUE; - } // function tile_img - + } - /*! - * Draws a border around the final image. + /* + * Return the image border width. + * This is used by CalcMargins() and DrawImageBorder(). */ - function 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': - ImageLine($this->img, 0, 0, $this->image_width-1, 0, $this->ndx_i_border); - ImageLine($this->img, 1, 1, $this->image_width-2, 1, $this->ndx_i_border); - ImageLine($this->img, 0, 0, 0, $this->image_height-1, $this->ndx_i_border); - ImageLine($this->img, 1, 1, 1, $this->image_height-2, $this->ndx_i_border); - ImageLine($this->img, $this->image_width-1, 0, $this->image_width-1, - $this->image_height-1, $this->ndx_i_border_dark); - ImageLine($this->img, 0, $this->image_height-1, $this->image_width-1, - $this->image_height-1, $this->ndx_i_border_dark); - ImageLine($this->img, $this->image_width-2, 1, $this->image_width-2, - $this->image_height-2, $this->ndx_i_border_dark); - ImageLine($this->img, 1, $this->image_height-2, $this->image_width-2, - $this->image_height-2, $this->ndx_i_border_dark); - break; - case 'plain': - ImageLine($this->img, 0, 0, $this->image_width, 0, $this->ndx_i_border_dark); - ImageLine($this->img, $this->image_width-1, 0, $this->image_width-1, - $this->image_height, $this->ndx_i_border_dark); - ImageLine($this->img, $this->image_width-1, $this->image_height-1, 0, $this->image_height-1, - $this->ndx_i_border_dark); - ImageLine($this->img, 0, 0, 0, $this->image_height, $this->ndx_i_border_dark); + // 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 'none': + 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: - $this->DrawError("DrawImageBorder(): unknown image_border_type: '$this->image_border_type'"); - return FALSE; + return $this->PrintError( + "DrawImageBorder(): unknown image_border_type: '$this->image_border_type'"); } return TRUE; } - - /*! - * Adds the title to the graph. + /* + * 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. */ - function DrawTitle() + protected function DrawTitle() { - // Center of the plot area - //$xpos = ($this->plot_area[0] + $this->plot_area_width )/ 2; + 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->safe_margin; - - $this->DrawText($this->title_font, $this->title_angle, $xpos, $ypos, - $this->ndx_title_color, $this->title_txt, 'center', 'bottom'); + $ypos = $this->title_offset; - return TRUE; + $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 */ - function DrawXTitle() + protected function DrawXTitle() { if ($this->x_title_pos == 'none') - return; + 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->safe_margin + $this->title_height + $this->safe_margin; - $this->DrawText($this->x_title_font, $this->x_title_angle, - $xpos, $ypos, $this->ndx_title_color, $this->x_title_txt, 'center'); + $ypos = $this->plot_area[1] - $this->x_title_top_offset; + $this->DrawText($this->fonts['x_title'], 0, $xpos, $ypos, $this->ndx_x_title_color, + $this->x_title_txt, 'center', 'bottom'); } // Lower title if ($this->x_title_pos == 'plotdown' || $this->x_title_pos == 'both') { - $ypos = $this->image_height - $this->x_title_height - $this->safe_margin; - $this->DrawText($this->x_title_font, $this->x_title_angle, - $xpos, $ypos, $this->ndx_title_color, $this->x_title_txt, 'center'); + $ypos = $this->plot_area[3] + $this->x_title_bot_offset; + $this->DrawText($this->fonts['x_title'], 0, $xpos, $ypos, $this->ndx_x_title_color, + $this->x_title_txt, 'center', 'top'); } return TRUE; } - /*! + /* * Draws the Y-Axis Title */ - function DrawYTitle() + protected function DrawYTitle() { if ($this->y_title_pos == 'none') - return; + return TRUE; - // Center the title vertically to the plot + // 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->safe_margin; - $this->DrawText($this->y_title_font, 90, $xpos, $ypos, $this->ndx_title_color, - $this->y_title_txt, 'left', 'center'); + $xpos = $this->plot_area[0] - $this->y_title_left_offset; + $this->DrawText($this->fonts['y_title'], 90, $xpos, $ypos, $this->ndx_y_title_color, + $this->y_title_txt, 'right', 'center'); } if ($this->y_title_pos == 'plotright' || $this->y_title_pos == 'both') { - $xpos = $this->image_width - $this->safe_margin - $this->y_title_width - $this->safe_margin; - $this->DrawText($this->y_title_font, 90, $xpos, $ypos, $this->ndx_title_color, + $xpos = $this->plot_area[2] + $this->y_title_right_offset; + $this->DrawText($this->fonts['y_title'], 90, $xpos, $ypos, $this->ndx_y_title_color, $this->y_title_txt, 'left', 'center'); } return TRUE; } + /* + * Draw the X axis, including ticks and labels, and X (vertical) grid lines. + */ + protected function DrawXAxis() + { + // Draw ticks, labels and grid + $this->DrawXTicks(); + + //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; + } /* - * \note Horizontal grid lines overwrite horizontal axis with y=0, so call this first, then DrawXAxis() + * Draw the Y axis, including ticks and labels, and Y (horizontal) grid lines. + * Horizontal grid lines overwrite horizontal axis with y=0, so call this first, then DrawXAxis() */ - function DrawYAxis() + protected function DrawYAxis() { // Draw ticks, labels and grid, if any $this->DrawYTicks(); @@ -2708,85 +4190,108 @@ class PHPlot { } /* - * + * Draw one X tick mark and its tick label. + * $which_xlab : Formatted X value for the label. + * $which_xpix : X device coordinate for this tick mark. */ - function DrawXAxis() + protected function DrawXTick($which_xlab, $which_xpix) { - // Draw ticks, labels and grid - $this->DrawXTicks(); + // 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); + } - /* 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); + // 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); } - */ - //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); + // 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; } - /*! - * Draw Just one Tick, called from DrawYTicks() and DrawXAxis() - * TODO? Move this inside DrawYTicks() and Modify DrawXAxis() ? + /* + * Draw one Y tick mark and its tick label. Called from DrawYTicks() and DrawXAxis() + * $which_ylab : Formatted Y value for the label. + * $which_ypix : Y device coordinate for this tick mark. */ - function DrawYTick($which_ylab, $which_ypix) + 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); - } - - // Labels on Y axis - if ($this->y_tick_label_pos == 'yaxis') { - $this->DrawText($this->y_label_font, $this->y_label_angle, - $this->y_axis_x_pixels - $this->y_tick_length * 1.5, $which_ypix, - $this->ndx_text_color, $which_ylab, 'right', 'center'); + $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); + 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); + 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->y_label_font, $this->y_label_angle, - $this->plot_area[0] - $this->y_tick_length * 1.5, $which_ypix, + $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->y_label_font, $this->y_label_angle, - $this->plot_area[2] + $this->y_tick_length * 1.5, $which_ypix, + $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'); } - } // Function DrawYTick() - + return TRUE; + } - /*! - * 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 - * TODO: marks at whole numbers (-10, 10, 20, 30 ...) no matter where the plot begins (-3, 4.7, etc.) + /* + * 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 + * + * Original vertical code submitted by Marlin Viss */ - function DrawYTicks() + protected function DrawXTicks() { // Sets the line style for IMG_COLOR_STYLED lines (grid) if ($this->dashed_grid) { @@ -2796,51 +4301,36 @@ class PHPlot { $style = $this->ndx_light_grid_color; } - // maxy is always > miny so delta_y is always positive - if ($this->y_tick_inc) { - $delta_y = $this->y_tick_inc; - } elseif ($this->num_y_ticks) { - $delta_y = ($this->plot_max_y - $this->plot_min_y) / $this->num_y_ticks; - } else { - $delta_y = ($this->plot_max_y - $this->plot_min_y) / 10 ; - } - - // NOTE: When working with floats, because of approximations when adding $delta_y, - // $y_tmp never equals $y_end at the for loop, so one spurious line would get drawn where - // not for the substraction to $y_end here. - $y_tmp = (double)$this->plot_min_y; - $y_end = (double)$this->plot_max_y - ($delta_y/2); - - if ($this->skip_bottom_tick) - $y_tmp += $delta_y; + // Calculate the tick start, end, and step: + list($x_start, $x_end, $delta_x) = $this->CalcTicks('x'); - if ($this->skip_top_tick) - $y_end -= $delta_y; - - for (;$y_tmp < $y_end; $y_tmp += $delta_y) { - $ylab = $this->FormatLabel('y', $y_tmp); - $y_pixels = $this->ytr($y_tmp); + // 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); - // 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); + // 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 ticks - $this->DrawYTick($ylab, $y_pixels); + // 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 DrawYTicks - + } - /*! - * 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 + /* + * Draw the grid, ticks, and tick labels along the Y axis. + * Ticks and tick labels 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 */ - function DrawXTicks() + protected function DrawYTicks() { // Sets the line style for IMG_COLOR_STYLED lines (grid) if ($this->dashed_grid) { @@ -2850,118 +4340,106 @@ class PHPlot { $style = $this->ndx_light_grid_color; } - // Calculate x increment between ticks - if ($this->x_tick_inc) { - $delta_x = $this->x_tick_inc; - } elseif ($this->num_x_ticks) { - $delta_x = ($this->plot_max_x - $this->plot_min_x) / $this->num_x_ticks; - } else { - $delta_x =($this->plot_max_x - $this->plot_min_x) / 10 ; - } - - // NOTE: When working with decimals, because of approximations when adding $delta_x, - // $x_tmp never equals $x_end at the for loop, so one spurious line would get drawn where - // not for the substraction to $x_end here. - $x_tmp = (double)$this->plot_min_x; - $x_end = (double)$this->plot_max_x - ($delta_x/2); - - // Should the leftmost tick be drawn? - if ($this->skip_left_tick) - $x_tmp += $delta_x; - - // And the rightmost? - if (! $this->skip_right_tick) - $x_end += $delta_x; - - for (;$x_tmp < $x_end; $x_tmp += $delta_x) { - $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); - } - - // Tick on X Axis - if ($this->x_tick_pos == 'xaxis') { + // Calculate the tick start, end, and step: + list($y_start, $y_end, $delta_y) = $this->CalcTicks('y'); - ImageLine($this->img, $x_pixels, $this->x_axis_y_pixels - $this->x_tick_cross, - $x_pixels, $this->x_axis_y_pixels + $this->x_tick_length, $this->ndx_tick_color); - } + // 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); - // Label on X axis - if ($this->x_tick_label_pos == 'xaxis') { - $this->DrawText($this->x_label_font, $this->x_label_angle, $x_pixels, - $this->x_axis_y_pixels + $this->x_tick_length*1.5, $this->ndx_text_color, - $xlab, 'center', 'bottom'); - } - - // Top of the plot area tick - if ($this->x_tick_pos == 'plotup' || $this->x_tick_pos == 'both') { - ImageLine($this->img, $x_pixels, $this->plot_area[1] - $this->x_tick_length, - $x_pixels, $this->plot_area[1] + $this->x_tick_cross, $this->ndx_tick_color); - } - // Bottom of the plot area tick - if ($this->x_tick_pos == 'plotdown' || $this->x_tick_pos == 'both') { - ImageLine($this->img, $x_pixels, $this->plot_area[3] + $this->x_tick_length, - $x_pixels, $this->plot_area[3] - $this->x_tick_cross, $this->ndx_tick_color); + // 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); } - // Top of the plot area tick label - if ($this->x_tick_label_pos == 'plotup' || $this->x_tick_label_pos == 'both') { - $this->DrawText($this->x_label_font, $this->x_label_angle, $x_pixels, - $this->plot_area[1] - $this->x_tick_length*1.5, $this->ndx_text_color, - $xlab, 'center', 'top'); - } + // Draw tick mark(s) + $this->DrawYTick($ylab, $y_pixels); - // Bottom of the plot area tick label - if ($this->x_tick_label_pos == 'plotdown' || $this->x_tick_label_pos == 'both') { - $this->DrawText($this->x_label_font, $this->x_label_angle, $x_pixels, - $this->plot_area[3] + $this->x_tick_length*1.5, $this->ndx_text_color, - $xlab, 'center', 'bottom'); - } + // Step to next Y, without accumulating error + $y_tmp = $y_start + ++$n * $delta_y; } - return; - } // function DrawXTicks + return TRUE; + } + /* + * 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; + } - /*! - * + /* + * Draw the data value label associated with a point in the plot. + * This is used for bar and stacked bar charts. These are the labels above, + * to the right, or within the bars, not the axis labels. + * + * $x_or_y : Specify 'x' or 'y' labels. This selects font, angle, and formatting. + * $x_world, $y_world : World coordinates of the text (see also x/y_adjustment). + * $text : The text to draw, after formatting with FormatLabel(). + * $halign, $valign : Selects from 9-point text alignment. + * $x_adjustment, $y_adjustment : Text position offsets, in device coordinates. + * $min_width, $min_height : If supplied, suppress the text if it will not fit. + * Returns True, if the text was drawn, or False, if it will not fit. + * */ - function DrawPlotBorder() - { - switch ($this->plot_border_type) { - case 'left': // for past compatibility - case 'plotleft': - ImageLine($this->img, $this->plot_area[0], $this->ytr($this->plot_min_y), - $this->plot_area[0], $this->ytr($this->plot_max_y), $this->ndx_grid_color); - break; - case 'right': - case 'plotright': - ImageLine($this->img, $this->plot_area[2], $this->ytr($this->plot_min_y), - $this->plot_area[2], $this->ytr($this->plot_max_y), $this->ndx_grid_color); - break; - case 'both': - case 'sides': - ImageLine($this->img, $this->plot_area[0], $this->ytr($this->plot_min_y), - $this->plot_area[0], $this->ytr($this->plot_max_y), $this->ndx_grid_color); - ImageLine($this->img, $this->plot_area[2], $this->ytr($this->plot_min_y), - $this->plot_area[2], $this->ytr($this->plot_max_y), $this->ndx_grid_color); - break; - case 'none': - //Draw No Border - break; - case 'full': - default: - ImageRectangle($this->img, $this->plot_area[0], $this->ytr($this->plot_min_y), - $this->plot_area[2], $this->ytr($this->plot_max_y), $this->ndx_grid_color); - break; + protected function DrawDataValueLabel($x_or_y, $x_world, $y_world, $text, $halign, $valign, + $x_adjustment=0, $y_adjustment=0, $min_width=NULL, $min_height=NULL) + { + if ($x_or_y == 'x') { + $angle = $this->x_data_label_angle; + $font = $this->fonts['x_label']; + $formatted_text = $this->FormatLabel('xd', $text); + } else { // Assumed 'y' + $angle = $this->y_data_label_angle; + $font = $this->fonts['y_label']; + $formatted_text = $this->FormatLabel('yd', $text); + } + $color = $this->ndx_title_color; // Currently this is the same for X and Y labels + + // Check to see if the text fits in the available space, if requested. + if (isset($min_width) || isset($min_height)) { + list($width, $height) = $this->SizeText($font, $angle, $formatted_text); + if ((isset($min_width) && ($min_width - $width) < 2) + || (isset($min_height) && ($min_height - $height) < 2)) + return FALSE; } + + $this->DrawText($font, $angle, $this->xtr($x_world) + $x_adjustment, + $this->ytr($y_world) + $y_adjustment, + $color, $formatted_text, $halign, $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. @@ -2969,221 +4447,265 @@ class PHPlot { * 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()) */ - function DrawXDataLabel($xlab, $xpos, $row=FALSE) + protected function DrawXDataLabel($xlab, $xpos, $row=FALSE) { - // FIXME!! not working... - if (($this->_x_label_cnt++ % $this->x_label_inc) != 0) - return; - - $xlab = $this->FormatLabel('x', $xlab); + $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->x_label_font, $this->x_label_angle, $xpos, - $this->plot_area[3] + $this->x_tick_length, - $this->ndx_text_color, $xlab, 'center', 'bottom'); + $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->x_label_font, $this->x_label_angle, $xpos, - $this->plot_area[1] - $this->x_tick_length , - $this->ndx_text_color, $xlab, 'center', 'top'); + $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'); - if ($row && $this->draw_x_data_label_lines) + // $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. + * $xpos : position in pixels of the line. + * $row : index of the data row being drawn. */ - function DrawXDataLine($xpos, $row) + protected function DrawXDataLine($xpos, $row) { // Sets the line style for IMG_COLOR_STYLED lines (grid) - if($this->dashed_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') { + // Lines from the bottom up ImageLine($this->img, $xpos, $this->plot_area[3], $xpos, $this->plot_area[1], $style); - } - // Lines coming from the bottom of the plot - else if ($this->x_data_label_pos == 'plotdown') { - // See FindDataLimits() to see why 'MAXY' index. - $ypos = $this->ytr($this->data[$row][MAXY]); + } elseif ($this->x_data_label_pos == 'plotdown' && isset($this->data_max[$row])) { + // Lines from the bottom of the plot up to the max Y value at this X: + $ypos = $this->ytr($this->data_max[$row]); ImageLine($this->img, $xpos, $ypos, $xpos, $this->plot_area[3], $style); - } - // Lines coming from the top of the plot - else if ($this->x_data_label_pos == 'plotup') { - // See FindDataLimits() to see why 'MINY' index. - $ypos = $this->ytr($this->data[$row][MINY]); + } elseif ($this->x_data_label_pos == 'plotup' && isset($this->data_min[$row])) { + // Lines from the top of the plot down to the min Y value at this X: + $ypos = $this->ytr($this->data_min[$row]); ImageLine($this->img, $xpos, $this->plot_area[1], $xpos, $ypos, $style); } - } - -/* - function DrawPlotLabel($xlab, $xpos, $ypos) - { - $this->DrawText($this->x_label_font, $this->x_label_angle, $xpos, $this -*/ + return TRUE; + } - /*! + /* * Draws the graph legend * - * \note Base code submitted by Marlin Viss - * FIXME: maximum label length should be calculated more accurately for TT fonts - * Performing a BBox calculation for every legend element, for example. + * Base code submitted by Marlin Viss */ - function DrawLegend($which_x1, $which_y1, $which_boxtype) + protected function DrawLegend() { - // Find maximum legend label length - $max_len = 0; - foreach ($this->legend as $leg) { - $len = strlen($leg); - $max_len = ($len > $max_len) ? $len : $max_len; + $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; } - $max_len += 5; // Leave room for the boxes and margins - /////// Calculate legend labels sizes: FIXME - dirty hack - FIXME - // TTF: - if ($this->use_ttf) { - $size = $this->TTFBBoxSize($this->legend_font['size'], 0, - $this->legend_font['font'], '_'); - $char_w = $size[0]; - - $size = $this->TTFBBoxSize($this->legend_font['size'], 0, - $this->legend_font['font'], '|'); - $char_h = $size[1]; - } - // Fixed fonts: - else { - $char_w = $this->legend_font['width']; - $char_h = $this->legend_font['height']; - } - - $v_margin = $char_h/2; // Between vertical borders and labels - $dot_height = $char_h + $this->line_spacing; // Height of the small colored boxes - $width = $char_w * $max_len; - - //////// Calculate box size - // upper Left - if ( (! $which_x1) || (! $which_y1) ) { - $box_start_x = $this->plot_area[2] - $width; - $box_start_y = $this->plot_area[1] + 5; - } else { - $box_start_x = $which_x1; - $box_start_y = $which_y1; + // 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; } - // Lower right corner - $box_end_y = $box_start_y + $dot_height*(count($this->legend)) + 2*$v_margin; - $box_end_x = $box_start_x + $width - 5; + //////// 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); + 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; - $dot_left_x = $box_end_x - $char_w * 2; - $dot_right_x = $box_end_x - $char_w; - $y_pos = $box_start_y + $v_margin; + // 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) { - // Text right aligned to the little box - $this->DrawText($this->legend_font, 0, $dot_left_x - $char_w, $y_pos, - $this->ndx_text_color, $leg, 'right'); - // Draw a box in the data color - ImageFilledRectangle($this->img, $dot_left_x, $y_pos + 1, $dot_right_x, - $y_pos + $dot_height-1, $this->ndx_data_colors[$color_index]); - // Draw a rectangle around the box - ImageRectangle($this->img, $dot_left_x, $y_pos + 1, $dot_right_x, - $y_pos + $dot_height-1, $this->ndx_text_color); - - $y_pos += $char_h + $this->line_spacing; + // 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) + if ($color_index > $max_color_index) $color_index = 0; } - } // Function DrawLegend() - - - /*! - * TODO Draws a legend over (or below) an axis of the plot. - */ - function DrawAxisLegend() - { - // Calculate available room - // Calculate length of all items (boxes included) - // Calculate number of lines and room it would take. FIXME: this should be known in CalcMargins() - // Draw. + return TRUE; } ///////////////////////////////////////////// //////////////////// PLOT DRAWING ///////////////////////////////////////////// - - /*! - * Draws a pie chart. Data has to be 'text-data' type. + /* + * 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. * - * This can work in two ways: the classical, with a column for each sector - * (computes the column totals and draws the pie with that) - * OR - * Takes each row as a sector and uses it's first value. This has the added - * advantage of using the labels provided, which is not the case with the - * former method. This might prove useful for pie charts from GROUP BY sql queries + * 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. */ - function DrawPieChart() + protected function DrawPieChart() { + if (!$this->CheckDataType('text-data, text-data-single, data-data')) + return FALSE; + + // Allocate dark colors only if they will be used for shading. + if ($this->shading > 0) + $this->NeedDataDarkColors(); + $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; - // Get sum of each column? One pie slice per column - if ($this->data_type === 'text-data') { - for ($i = 0; $i < $this->num_data_rows; $i++) { - for ($j = 1; $j < $this->num_recs[$i]; $j++) { // Label ($row[0]) unused in these pie charts - @ $sumarr[$j] += abs($this->data[$i][$j]); // NOTE! sum > 0 to make pie charts - } - } - } - // Or only one column per row, one pie slice per row? - else if ($this->data_type == 'text-data-single') { - for ($i = 0; $i < $this->num_data_rows; $i++) { - $legend[$i] = $this->data[$i][0]; // Set the legend to column labels - $sumarr[$i] = $this->data[$i][1]; + $num_slices = $this->data_columns; // See CheckDataArray which calculates this for us. + if ($num_slices < 1) return TRUE; // Give up early if there is no data at all. + $sumarr = array_fill(0, $num_slices, 0); + + if ($this->datatype_pie_single) { + // text-data-single: One data column per row, one pie slice per row. + 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 if ($this->data_type == 'data-data') { + } else { + // text-data: Sum each column (skipping label), one pie slice per column. + // data-data: Sum each column (skipping X value and label), one pie slice per column. + $skip = ($this->datatype_implied) ? 1 : 2; // Leading values to skip in each row. for ($i = 0; $i < $this->num_data_rows; $i++) { - for ($j = 2; $j < $this->num_recs[$i]; $j++) { - @ $sumarr[$j] += abs($this->data[$i][$j]); + for ($j = $skip; $j < $this->num_recs[$i]; $j++) { + if (is_numeric($this->data[$i][$j])) + $sumarr[$j-$skip] += abs($this->data[$i][$j]); } } } - else { - $this->DrawError("DrawPieChart(): Data type '$this->data_type' not supported."); - return FALSE; - } $total = array_sum($sumarr); if ($total == 0) { - $this->DrawError('DrawPieChart(): Empty data set'); - return FALSE; + // 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) { @@ -3191,214 +4713,334 @@ class PHPlot { } else { $diam2 = $diameter; } - $max_data_colors = count ($this->data_colors); + $max_data_colors = count($this->ndx_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; - foreach ($sumarr as $val) { + 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 = number_format(($val / $total * 100), $this->y_precision, '.', ', ') . '%'; + $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; - $mid_angle = deg2rad($end_angle - ($val / 2)); - - // Draw the slice - ImageFilledArc($this->img, $xpos, $ypos+$h, $diameter, $diam2, - 360-$end_angle, 360-$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, - 360-$end_angle, 360-$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->generic_font, 0, $label_x, $label_y, $this->ndx_grid_color, - $label_txt, 'center', 'center'); + // 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'); + } } - $color_index++; - $color_index = $color_index % $max_data_colors; + if (++$color_index >= $max_data_colors) + $color_index = 0; } // end for } // end for + return TRUE; + } + + /* + * Get data color to use for plotting. + * $row, $idx : Index arguments for the current data point. + * &$vars : Variable storage. Caller makes an empty array, and this function uses it. + * &$data_color : Returned result - Color index for the data point. + * $extra : Extra info flag passed through to data color callback. + */ + protected function GetDataColor($row, $idx, &$vars, &$data_color, $extra = 0) + { + // Initialize or extract variables: + if (empty($vars)) { + $custom_color = (bool)$this->GetCallback('data_color'); + $num_data_colors = count($this->ndx_data_colors); + $vars = compact('custom_color', 'num_data_colors'); + } else { + extract($vars); + } + + // Select the colors. + if ($custom_color) { + $col_i = $this->DoCallback('data_color', $row, $idx, $extra); // Custom color index + $data_color = $this->ndx_data_colors[$col_i % $num_data_colors]; + } else { + $data_color = $this->ndx_data_colors[$idx]; + } } + /* + * Get data color and error bar color to use for plotting. + * $row, $idx : Index arguments for the current bar. + * &$vars : Variable storage. Caller makes an empty array, and this function uses it. + * &$data_color : Returned result - Color index for the data (bar fill) + * &$error_color : Returned result - Color index for the error bars + * $extra : Extra info flag passed through to data color callback. + */ + protected function GetDataErrorColors($row, $idx, &$vars, &$data_color, &$error_color, $extra = 0) + { + // Initialize or extract variables: + if (empty($vars)) { + $this->NeedErrorBarColors(); // This plot needs error bar colors. + $custom_color = (bool)$this->GetCallback('data_color'); + $num_data_colors = count($this->ndx_data_colors); + $num_error_colors = count($this->ndx_error_bar_colors); + $vars = compact('custom_color', 'num_data_colors', 'num_error_colors'); + } else { + extract($vars); + } + + // Select the colors. + if ($custom_color) { + $col_i = $this->DoCallback('data_color', $row, $idx, $extra); // Custom color index + $data_color = $this->ndx_data_colors[$col_i % $num_data_colors]; + $error_color = $this->ndx_error_bar_colors[$col_i % $num_error_colors]; + } else { + $data_color = $this->ndx_data_colors[$idx]; + $error_color = $this->ndx_error_bar_colors[$idx]; + } + } - /*! - * Supported data formats: data-data-error, text-data-error (doesn't exist yet) - * ( data comes in as array("title", x, y, error+, error-, y2, error2+, error2-, ...) ) + /* + * 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-, ...) + * This is called from DrawDots, with data type already checked. + * $paired is true for linepoints error plots, to make sure elements are + * only drawn once. If true, data labels are drawn by DrawLinesError, and error + * bars are drawn by DrawDotsError. (This choice is for backwards compatibility.) */ - function DrawDotsError() + protected function DrawDotsError($paired = FALSE) { - $this->CheckOption($this->data_type, 'data-data-error', __FUNCTION__); + // Adjust the point shapes and point sizes arrays: + $this->CheckPointParams(); + + $gcvars = array(); // For GetDataErrorColors, which initializes and uses this. + // Special flag for data color callback to indicate the 'points' part of 'linepoints': + $alt_flag = $paired ? 1 : 0; - for($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { + for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { $record = 1; // Skip record #0 (title) - // Do we have a value for X? - if ($this->data_type == 'data-data-error') - $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 = $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') + if ($this->x_data_label_pos != 'none' && !$paired) $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row); - while ($record < $this->num_recs[$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 + + // Select the colors: + $this->GetDataErrorColors($row, $idx, $gcvars, $data_color, $error_color, $alt_flag); + // Y: - $y_now = $this->data[$row][$record]; - $this->DrawDot($x_now, $y_now, $record, $this->ndx_data_colors[$record++]); + $y_now = $this->data[$row][$record++]; + $this->DrawDot($x_now, $y_now, $idx, $data_color); // Error + - $val = $this->data[$row][$record]; - $this->DrawYErrorBar($x_now, $y_now, $val, $this->error_bar_shape, - $this->ndx_error_bar_colors[$record++]); + $val = $this->data[$row][$record++]; + $this->DrawYErrorBar($x_now, $y_now, $val, $this->error_bar_shape, $error_color); // Error - - $val = $this->data[$row][$record]; - $this->DrawYErrorBar($x_now, $y_now, -$val, $this->error_bar_shape, - $this->ndx_error_bar_colors[$record++]); + $val = $this->data[$row][$record++]; + $this->DrawYErrorBar($x_now, $y_now, -$val, $this->error_bar_shape, $error_color); + } else { + $record += 3; // Skip over missing Y and its error values + } } } - } // function DrawDotsError() - + return TRUE; + } /* - * Supported data types: - * - data-data ("title", x, y1, y2, y3, ...) - * - text-data ("title", y1, y2, y3, ...) + * Draw a points plot, or the points for a linepoints plot + * Data format can be text-data (label, y1, y2, ...) or data-data (label, x, y1, y2, ...) + * Points plot with error bars (data-data-error format) is redirected to DrawDotsError. + * $paired is true for linepoints plots, to make sure elements are only drawn once. */ - function DrawDots() + protected function DrawDots($paired = FALSE) { - $this->CheckOption($this->data_type, 'text-data, data-data', __FUNCTION__); + if (!$this->CheckDataType('text-data, data-data, data-data-error')) + return FALSE; + if ($this->datatype_error_bars) + return $this->DrawDotsError($paired); // Redirect for points+errorbars plot + + // Adjust the point shapes and point sizes arrays: + $this->CheckPointParams(); + + $gcvars = array(); // For GetDataColor, which initializes and uses this. + // Special flag for data color callback to indicate the 'points' part of 'linepoints': + $alt_flag = $paired ? 1 : 0; 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 + if ($this->datatype_implied) // Implied X values? + $x_now = 0.5 + $cnt++; // Place text-data at X = 0.5, 1.5, 2.5, etc... else - $x_now = 0.5 + $cnt++; // Place text-data at X = 0.5, 1.5, 2.5, etc... + $x_now = $this->data[$row][$rec++]; // Read it, advance record index $x_now_pixels = $this->xtr($x_now); // Draw X Data labels? - if ($this->x_data_label_pos != 'none') + if ($this->x_data_label_pos != 'none' && !$paired) $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row); // Proceed with Y values - for($idx = 0;$rec < $this->num_recs[$row]; $rec++, $idx++) { + 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], - $rec, $this->ndx_data_colors[$idx]); + + // Select the color: + $this->GetDataColor($row, $idx, $gcvars, $data_color, $alt_flag); + // Draw the marker: + $this->DrawDot($x_now, $this->data[$row][$rec], $idx, $data_color); } } } - } //function DrawDots + return TRUE; + } + /* + * 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 for vertical plots, + * and data-data-yx and text-data-yx for horizontal plots. + * Note that although this plot type supports multiple data sets, it rarely makes + * sense to have more than 1, because the lines will overlay. + * This one function does both vertical and horizontal plots. "iv" is used for the + * independent variable (X for vertical plots, Y for horizontal) and "dv" is used + * for the dependent variable (Y for vertical plots, X for horizontal). + */ + protected function DrawThinBarLines() + { + if (!$this->CheckDataType('text-data, data-data, text-data-yx, data-data-yx')) + return FALSE; - /*! - * A clean, fast routine for when you just want charts like stock volume charts - */ - function DrawThinBarLines() - { - $this->CheckOption($this->data_type, 'text-data, data-data', __FUNCTION__); + $gcvars = array(); // For GetDataColor, which initializes and uses this. 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 + if ($this->datatype_implied) // Implied independent variable values? + $iv_now = 0.5 + $cnt++; // Place text-data at 0.5, 1.5, 2.5, etc... 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); + $iv_now = $this->data[$row][$rec++]; // Read it, advance record index - // Draw X Data labels? - if ($this->x_data_label_pos != 'none') - $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); + if ($this->datatype_swapped_xy) { + $y_now_pixels = $this->ytr($iv_now); + // Draw Y Data labels? + if ($this->y_data_label_pos != 'none') + $this->DrawYDataLabel($this->data[$row][0], $y_now_pixels); + } else { + $x_now_pixels = $this->xtr($iv_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 + // Proceed with dependent values + for ($idx = 0; $rec < $this->num_recs[$row]; $rec++, $idx++) { + if (is_numeric($this->data[$row][$rec])) { // Allow for missing data + $dv = $this->data[$row][$rec]; 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]); + + // Select the color: + $this->GetDataColor($row, $idx, $gcvars, $data_color); + + if ($this->datatype_swapped_xy) { + // Draw a line from user defined y axis position right (or left) to xtr($dv) + ImageLine($this->img, $this->y_axis_x_pixels, $y_now_pixels, + $this->xtr($dv), $y_now_pixels, $data_color); + } else { + // Draw a line from user defined x axis position up (or down) to ytr($dv) + ImageLine($this->img, $x_now_pixels, $this->x_axis_y_pixels, + $x_now_pixels, $this->ytr($dv), $data_color); + } } } } ImageSetThickness($this->img, 1); - } //function DrawThinBarLines + return TRUE; + } - /*! - * + /* + * Draw an Error Bar set. Used by DrawDotsError and DrawLinesError */ - function DrawYErrorBar($x_world, $y_world, $error_height, $error_bar_type, $color) + 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->error_font, 90, $x1, $y2, - $color, $label, 'center', 'top'); - */ - $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': + if ($error_bar_type == '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. - * Supported types: 'halfline', 'line', 'plus', 'cross', 'rect', 'circle', 'dot', - * 'diamond', 'triangle', 'trianglemid' + * 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. */ - function DrawDot($x_world, $y_world, $record, $color) + protected function DrawDot($x_world, $y_world, $record, $color) { - // TODO: optimize, avoid counting every time we are called. - $record = $record % count ($this->point_shapes); + $index = $record % $this->point_counts; + $point_size = $this->point_sizes[$index]; - $half_point = $this->point_sizes[$record] / 2; + $half_point = (int)($point_size / 2); $x_mid = $this->xtr($x_world); $y_mid = $this->ytr($y_world); @@ -3408,7 +5050,7 @@ class PHPlot { $y1 = $y_mid - $half_point; $y2 = $y_mid + $half_point; - switch ($this->point_shapes[$record]) { + switch ($this->point_shapes[$index]) { case 'halfline': ImageLine($this->img, $x1, $y_mid, $x_mid, $y_mid, $color); break; @@ -3423,15 +5065,11 @@ class PHPlot { ImageLine($this->img, $x1, $y1, $x2, $y2, $color); ImageLine($this->img, $x1, $y2, $x2, $y1, $color); break; - case 'rect': - ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $color); - break; case 'circle': - ImageArc($this->img, $x_mid, $y_mid, $this->point_sizes[$record], $this->point_sizes[$record], 0, 360, $color); + ImageArc($this->img, $x_mid, $y_mid, $point_size, $point_size, 0, 360, $color); break; case 'dot': - ImageFilledArc($this->img, $x_mid, $y_mid, $this->point_sizes[$record], $this->point_sizes[$record], 0, 360, - $color, IMG_ARC_PIE); + 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); @@ -3445,108 +5083,174 @@ class PHPlot { $arrpoints = array( $x1, $y1, $x2, $y1, $x_mid, $y_mid); ImageFilledPolygon($this->img, $arrpoints, 3, $color); break; - default: + 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 plot. Supported data types: - * 'text-data' - * 'data-data' - * NOTE: This function used to add first and last data values even on incomplete - * sets. That is not the behaviour now. As for missing data in between, - * there are two posibilities: replace the point with one on the X axis (previous - * way), or forget about it and use the preceding and following ones to draw the polygon. - * There is the possibility to use both, we just need to add the method to set - * it. Something like SetMissingDataBehaviour(), for example. - */ - function DrawArea() - { - $incomplete_data_defaults_to_x_axis = FALSE; // TODO: make this configurable + /* + * 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; - for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { + $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(); + + // Make sure each row has the same number of values. Note records_per_group is max(num_recs). + if ($this->records_per_group != min($this->num_recs)) { + return $this->PrintError("DrawArea(): Data array must contain the same number" + . " of Y values for each X"); + } + + // 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 ($this->data_type == 'data-data') // Do we have a value for X? - $x_now = $this->data[$row][$rec++]; // Read it, advance record index + if ($this->datatype_implied) // Implied X values? + $x_now = 0.5 + $row; // Place text-data at X = 0.5, 1.5, 2.5, etc... 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 + $x_now = $this->data[$row][$rec++]; // Read it, advance record index + $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); - // Proceed with Y values - // Create array of points for imagefilledpolygon() - for($idx = 0; $rec < $this->num_recs[$row]; $rec++, $idx++) { - if (is_numeric($this->data[$row][$rec])) { // Allow for missing Y data - $y_now_pixels = $this->ytr($this->data[$row][$rec]); - - $posarr[$idx][] = $x_now_pixels; - $posarr[$idx][] = $y_now_pixels; - - $num_points[$idx] = isset($num_points[$idx]) ? $num_points[$idx]+1 : 1; - } - // If there's missing data... - else { - if (isset ($incomplete_data_defaults_to_x_axis)) { - $posarr[$idx][] = $x_now_pixels; - $posarr[$idx][] = $this->x_axis_y_pixels; - $num_points[$idx] = isset($num_points[$idx]) ? $num_points[$idx]+1 : 1; - } + // 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 < $this->records_per_group) { + 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++; } - } // end for - - $end = count($posarr); - for ($i = 0; $i < $end; $i++) { - // Prepend initial points. X = first point's X, Y = x_axis_y_pixels - $x = $posarr[$i][0]; - array_unshift($posarr[$i], $x, $this->x_axis_y_pixels); - - // Append final points. X = last point's X, Y = x_axis_y_pixels - $x = $posarr[$i][count($posarr[$i])-2]; - array_push($posarr[$i], $x, $this->x_axis_y_pixels); - - $num_points[$i] += 2; - // Draw the poligon - ImageFilledPolygon($this->img, $posarr[$i], $num_points[$i], $this->ndx_data_colors[$i]); + if (!$do_stacked) + $yd[$row][] = $this->x_axis_y_pixels; } - } // function DrawArea() + // Now draw the filled polygons. + // Note data_columns is the number of Y points (columns excluding label and X), and the + // number of entries in the yd[] arrays is data_columns+1. + $prev_row = 0; + for ($row = 1; $row <= $this->data_columns; $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; + } - /*! - * Draw Lines. Supported data-types: - * 'data-data', - * 'text-data' - * NOTE: Please see the note regarding incomplete data sets on DrawArea() + /* + * Draw a line plot, or the lines part of a linepoints plot + * Data format can be text-data (label, y1, y2, ...) or data-data (label, x, y1, y2, ...) + * Line plot with error bars (data-data-error format) is redirected to DrawLinesError. + * $paired is true for linepoints plots, to make sure elements are only drawn once. */ - function DrawLines() + protected function DrawLines($paired = FALSE) { - // This will tell us if lines have already begun to be drawn. - // It is an array to keep separate information for every line, with a single - // variable we would sometimes get "undefined offset" errors and no plot... - $start_lines = array_fill(0, $this->records_per_group, FALSE); + if (!$this->CheckDataType('text-data, data-data, data-data-error')) + return FALSE; + if ($this->datatype_error_bars) + return $this->DrawLinesError($paired); // Redirect for lines+errorbar plot - if ($this->data_type == 'text-data') { - $lastx[0] = $this->xtr(0); - $lasty[0] = $this->xtr(0); - } + // 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->data_columns, FALSE); + + $gcvars = array(); // For GetDataColor, which initializes and uses this. 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 + if ($this->datatype_implied) // Implied X values? $x_now = 0.5 + $cnt++; // Place text-data at X = 0.5, 1.5, 2.5, etc... + else + $x_now = $this->data[$row][$record++]; // Read it, advance record index $x_now_pixels = $this->xtr($x_now); // Absolute coordinates @@ -3554,50 +5258,53 @@ class PHPlot { $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row); for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) { - if (is_numeric($this->data[$row][$record])) { //Allow for missing Y data + 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 + + // Select the color: + $this->GetDataColor($row, $idx, $gcvars, $data_color); + $y_now_pixels = $this->ytr($this->data[$row][$record]); - if ($start_lines[$idx] == TRUE) { + if ($start_lines[$idx]) { // 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, $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]); + if ($line_style == 'dashed') { + $this->SetDashedStyle($data_color); + $data_color = IMG_COLOR_STYLED; } - + ImageLine($this->img, $x_now_pixels, $y_now_pixels, + $lastx[$idx], $lasty[$idx], $data_color); } $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) { + } elseif ($this->draw_broken_lines) { // Y data missing, leave a gap. $start_lines[$idx] = FALSE; } } // end for } // end for - ImageSetThickness($this->img, 1); // Revert to original state for lines to be drawn later. - } // function DrawLines() - + ImageSetThickness($this->img, 1); // Revert to original state for lines to be drawn later. + return TRUE; + } - /*! - * Draw lines with error bars - data comes in as - * array("label", x, y, error+, error-, y2, error2+, error2-, ...); + /* + * 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-, ...) + * This is called from DrawLines, with data type already checked. + * $paired is true for linepoints error plots, to make sure elements are + * only drawn once. If true, data labels are drawn by DrawLinesError, and error + * bars are drawn by DrawDotsError. (This choice is for backwards compatibility.) */ - function DrawLinesError() + protected function DrawLinesError($paired = FALSE) { - if ($this->data_type != 'data-data-error') { - $this->DrawError("DrawLinesError(): Data type '$this->data_type' not supported."); - return FALSE; - } + $start_lines = array_fill(0, $this->data_columns, FALSE); - $start_lines = array_fill(0, $this->records_per_group, FALSE); + $gcvars = array(); // For GetDataErrorColors, which initializes and uses this. for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { $record = 1; // Skip record #0 (data label) @@ -3605,296 +5312,622 @@ class PHPlot { $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++) { - // Y - $y_now = $this->data[$row][$record++]; - $y_now_pixels = $this->ytr($y_now); + 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 - if ($start_lines[$idx] == TRUE) { - ImageSetThickness($this->img, $this->line_widths[$idx]); + // Select the colors: + $this->GetDataErrorColors($row, $idx, $gcvars, $data_color, $error_color); - if ($this->line_styles[$idx] == '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]); + // 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($data_color); + $data_color = IMG_COLOR_STYLED; + } + ImageLine($this->img, $x_now_pixels, $y_now_pixels, + $lastx[$idx], $lasty[$idx], $data_color); } - } - // Error+ - $val = $this->data[$row][$record++]; - $this->DrawYErrorBar($x_now, $y_now, $val, $this->error_bar_shape, - $this->ndx_error_bar_colors[$idx]); + if ($paired) { + $record += 2; // Skip error bars - done in the 'points' part of 'linepoints'. + } else { + // Error+ + $val = $this->data[$row][$record++]; + $this->DrawYErrorBar($x_now, $y_now, $val, $this->error_bar_shape, $error_color); - // 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, $error_color); + } - // Update indexes: - $start_lines[$idx] = TRUE; // Tells us if we already drew the first column of points, + // 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; - } // end while + $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. - } // function DrawLinesError() - + return TRUE; + } + /* + * Draw a Lines+Points plot (linepoints). + * This just uses DrawLines and DrawDots. They handle the error-bar case themselves. + */ + protected function DrawLinePoints() + { + // This check is redundant, as DrawLines and DrawDots do it, but left here as insurance. + if (!$this->CheckDataType('text-data, data-data, data-data-error')) + return FALSE; + $this->DrawLines(TRUE); + $this->DrawDots(TRUE); + return TRUE; + } - /*! - * This is a mere copy of DrawLines() with one more line drawn for each point + /* + * Draw a Squared Line plot. + * Data format can be text-data (label, y1, y2, ...) or data-data (label, x, y1, y2, ...) + * This is based on DrawLines(), with one more line drawn for each point. */ - function DrawSquared() + protected function DrawSquared() { - // This will tell us if lines have already begun to be drawn. - // It is an array to keep separate information for every line, for with a single - // variable we could sometimes get "undefined offset" errors and no plot... - $start_lines = array_fill(0, $this->records_per_group, FALSE); + 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->data_columns, FALSE); + + $gcvars = array(); // For GetDataColor, which initializes and uses this. - if ($this->data_type == 'text-data') { - $lastx[0] = $this->xtr(0); - $lasty[0] = $this->xtr(0); - } - 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 + if ($this->datatype_implied) // Implied X values? $x_now = 0.5 + $cnt++; // Place text-data at X = 0.5, 1.5, 2.5, etc... + else + $x_now = $this->data[$row][$record++]; // Read it, 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); // notice there is no last param. - - // Draw Lines + 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]) { + // Set line width, revert it to normal at the end + ImageSetThickness($this->img, $this->line_widths[$idx]); + + // Select the color: + $this->GetDataColor($row, $idx, $gcvars, $data_color); + + if ($this->line_styles[$idx] == 'dashed') { + $this->SetDashedStyle($data_color); + $data_color = IMG_COLOR_STYLED; + } + ImageLine($this->img, $lastx[$idx], $lasty[$idx], + $x_now_pixels, $lasty[$idx], $data_color); + ImageLine($this->img, $x_now_pixels, $lasty[$idx], + $x_now_pixels, $y_now_pixels, $data_color); + } + $lastx[$idx] = $x_now_pixels; + $lasty[$idx] = $y_now_pixels; + $start_lines[$idx] = TRUE; + } elseif ($this->draw_broken_lines) { // Y data missing, leave a gap. + $start_lines[$idx] = FALSE; + } + } + } // end while + + ImageSetThickness($this->img, 1); + return TRUE; + } + + /* + * Draw a bar (or segment of a bar), with optional shading or border. + * This is used by the bar and stackedbar plots, vertical and horizontal. + * $x1, $y1 : One corner of the bar. + * $x2, $y2 : Other corner of the bar. + * $data_color : Color index to use for the bar fill. + * $alt_color : Color index to use for the shading (if shading is on), else for the border. + * Note the same color is NOT used for shading and border - just the same argument. + * See GetBarColors() for where these arguments come from. + * $shade_top : Shade the top? (Suppressed for downward stack segments except first.) + * $shade_side : Shade the right side? (Suppressed for leftward stack segments except first.) + * Only one of $shade_top or $shade_side can be FALSE. Both default to TRUE. + */ + protected function DrawBar($x1, $y1, $x2, $y2, $data_color, $alt_color, + $shade_top = TRUE, $shade_side = TRUE) + { + // Sort the points so x1,y1 is upper left and x2,y2 is lower right. This + // is needed in order to get the shading right, and imagerectangle may require it. + if ($x1 > $x2) { + $t = $x1; $x1 = $x2; $x2 = $t; + } + if ($y1 > $y2) { + $t = $y1; $y1 = $y2; $y2 = $t; + } + + // Draw the bar + ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $data_color); + + // Draw a shade, or a border. + if (($shade = $this->shading) > 0) { + if ($shade_top && $shade_side) { + $npts = 6; + $pts = array($x1, $y1, $x1 + $shade, $y1 - $shade, $x2 + $shade, $y1 - $shade, + $x2 + $shade, $y2 - $shade, $x2, $y2, $x2, $y1); + } else { + $npts = 4; + if ($shade_top) { // Suppress side shading + $pts = array($x1, $y1, $x1 + $shade, $y1 - $shade, $x2 + $shade, $y1 - $shade, $x2, $y1); + } else { // Suppress top shading + $pts = array($x2, $y2, $x2, $y1, $x2 + $shade, $y1 - $shade, $x2 + $shade, $y2 - $shade); + } + } + ImageFilledPolygon($this->img, $pts, $npts, $alt_color); + } else { + ImageRectangle($this->img, $x1, $y1, $x2,$y2, $alt_color); + } + } + + /* + * Get colors to use for a bar chart. There is a data color, and either a border color + * or a shading color (data dark color). + * $row, $idx : Index arguments for the current bar. + * &$vars : Variable storage. Caller makes an empty array, and this function uses it. + * &$data_color : Returned result - Color index for the data (bar fill). + * &$alt_color : Returned result - Color index for the shading or outline. + */ + protected function GetBarColors($row, $idx, &$vars, &$data_color, &$alt_color) + { + // Initialize or extract variables: + if (empty($vars)) { + if ($this->shading > 0) // This plot needs dark colors if shading is on. + $this->NeedDataDarkColors(); + $custom_color = (bool)$this->GetCallback('data_color'); + $num_data_colors = count($this->ndx_data_colors); + $num_border_colors = count($this->ndx_data_border_colors); + $vars = compact('custom_color', 'num_data_colors', 'num_border_colors'); + } else { + extract($vars); + } + + // Select the colors. + if ($custom_color) { + $col_i = $this->DoCallback('data_color', $row, $idx); // Custom color index + $i_data = $col_i % $num_data_colors; // Index for data colors and dark colors + $i_border = $col_i % $num_border_colors; // Index for data borders (if used) + } else { + $i_data = $i_border = $idx; + } + $data_color = $this->ndx_data_colors[$i_data]; + if ($this->shading > 0) { + $alt_color = $this->ndx_data_dark_colors[$i_data]; + } else { + $alt_color = $this->ndx_data_border_colors[$i_border]; + } + } + + /* + * Draw a Bar chart + * Supports text-data format, with each row in the form array(label, y1, y2, y3, ...) + * Horizontal bars (text-data-yx format) are sent to DrawHorizBars() instead. + */ + protected function DrawBars() + { + if (!$this->CheckDataType('text-data, text-data-yx')) + return FALSE; + if ($this->datatype_swapped_xy) + return $this->DrawHorizBars(); + $this->CalcBarWidths(); + + // 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->data_columns * $this->record_bar_width) / 2 - $this->bar_adjust_gap; + + $gcvars = array(); // For GetBarColors, which initializes and uses this. + + 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 + $y = $this->data[$row][$record]; + $x2 = $x1 + $this->actual_bar_width; + + if (($upgoing_bar = $y >= $this->x_axis_position)) { + $y1 = $this->ytr($y); + $y2 = $this->x_axis_y_pixels; + } else { + $y1 = $this->x_axis_y_pixels; + $y2 = $this->ytr($y); + } + + // Select the colors: + $this->GetBarColors($row, $idx, $gcvars, $data_color, $alt_color); + + // Draw the bar, and the shade or border: + $this->DrawBar($x1, $y1, $x2, $y2, $data_color, $alt_color); + + // 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->DrawDataValueLabel('y', $row+0.5, $y, $y, '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; + } + + /* + * Draw a Horizontal Bar chart + * Supports only text-data-yx format, with each row in the form array(label, x1, x2, x3, ...) + * Note that the data values are X not Y, and the bars are drawn horizontally. + * This is called from DrawBars, which has already checked the data type. + */ + protected function DrawHorizBars() + { + $this->CalcBarWidths(FALSE); // Calculate bar sizes for horizontal plots + + // 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->data_columns * $this->record_bar_width) / 2 - $this->bar_adjust_gap; + + $gcvars = array(); // For GetBarColors, which initializes and uses this. + + 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 Y data - $y_now_pixels = $this->ytr($this->data[$row][$record]); + if (is_numeric($this->data[$row][$record])) { // Allow for missing X data + $x = $this->data[$row][$record]; + $y2 = $y1 - $this->actual_bar_width; - if ($start_lines[$idx] == TRUE) { - // Set line width, revert it to normal at the end - ImageSetThickness($this->img, $this->line_widths[$idx]); + if (($rightwards_bar = $x >= $this->y_axis_position)) { + $x1 = $this->xtr($x); + $x2 = $this->y_axis_x_pixels; + } else { + $x1 = $this->y_axis_x_pixels; + $x2 = $this->xtr($x); + } - 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); + // Select the colors: + $this->GetBarColors($row, $idx, $gcvars, $data_color, $alt_color); + + // Draw the bar, and the shade or border: + $this->DrawBar($x1, $y1, $x2, $y2, $data_color, $alt_color); + + // Draw optional data labels to the right of the bars (or left, if the bar + // goes left of the Y axis line). + if ($this->x_data_label_pos == 'plotin') { + if ($rightwards_bar) { + $h_align = 'left'; + $x_offset = 5 + $this->shading; } 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]); + $h_align = 'right'; + $x_offset = -2; } + $this->DrawDataValueLabel('x', $x, $row+0.5, $x, $h_align, 'center', + $x_offset, $y_first_bar - ($idx + 0.5) * $this->record_bar_width); } - $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); - } // function DrawSquared() + } + // Step to next bar in group: + $y1 -= $this->record_bar_width; + } // end for + } // end for + return TRUE; + } - /*! - * Data comes in as array("title", x, y, y2, y3, ...) + /* + * Draw a Stacked Bar chart + * Supports text-data format, with each row in the form array(label, y1, y2, y3, ...) + * Horizontal stacked bars (text-data-yx format) are sent to DrawHorizStackedBars() instead. + * Original stacked bars idea by Laurent Kruk < lolok at users.sourceforge.net > */ - function DrawBars() + protected function DrawStackedBars() { - if ($this->data_type != 'text-data') { - $this->DrawError('DrawBars(): Bar plots must be text-data: use function SetDataType("text-data")'); + if (!$this->CheckDataType('text-data, text-data-yx')) return FALSE; - } + if ($this->datatype_swapped_xy) + return $this->DrawHorizStackedBars(); + $this->CalcBarWidths(); + + // 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; + + $gcvars = array(); // For GetBarColors, which initializes and uses this. + + // Determine if any data labels are on: + $data_labels_within = ($this->y_data_label_pos == 'plotstack'); + $data_labels_end = $data_labels_within || ($this->y_data_label_pos == 'plotin'); + $data_label_y_offset = -5 - $this->shading; // For upward labels only. 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? TODO:labels on top of bars. + if ($this->x_data_label_pos != 'none') // Draw X Data labels? $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); - // Draw the bar + // 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. + $wy1 = 0; // World coordinates Y1, current sum of values + $wy2 = $this->x_axis_position; // World coordinates Y2, last drawn value + $first = TRUE; + for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) { - if (is_numeric($this->data[$row][$record])) { // Allow for missing Y data - $x1 = $x_now_pixels - $this->data_group_space + ($idx * $this->record_bar_width); - $x2 = $x1 + ($this->bar_width_adjust * $this->record_bar_width); - if ($this->data[$row][$record] < $this->x_axis_position) { - $y1 = $this->x_axis_y_pixels; - $y2 = $this->ytr($this->data[$row][$record]); - } else { - $y1 = $this->ytr($this->data[$row][$record]); - $y2 = $this->x_axis_y_pixels; - } + // Skip missing Y values, and ignore Y=0 values. + if (is_numeric($this->data[$row][$record]) + && ($this_y = $this->data[$row][$record]) != 0) { - 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]); + // First non-zero value sets the direction, $upward. Note this compares to 0, + // not the axis position. Segments are based at 0 but clip to the axis. + if ($first) + $upward = ($this_y > 0); + + $wy1 += $this_y; // Keep the running total for this bar stack + + // Draw nothing if this segment would not increase the bar height. + // Upward bars: draw if wy1>wy2. Downward bars: Draw if wy1ytr($wy1); // Convert to device coordinates. $y1 is outermost value. + $y2 = $this->ytr($wy2); // $y2 is innermost (closest to axis). + + // Select the colors: + $this->GetBarColors($row, $idx, $gcvars, $data_color, $alt_color); + + // Draw the bar, and the shade or border: + $this->DrawBar($x1, $y1, $x2, $y2, $data_color, $alt_color, + // Only shade the top for upward bars, or the first segment of downward bars: + $upward || $first, TRUE); + + // Draw optional data label for this bar segment just inside the end. + // Text value is the current Y, but position is the cumulative Y. + // The label is only drawn if it fits in the segment height |y2-y1|. + if ($data_labels_within) { + $this->DrawDataValueLabel('y', $row+0.5, $wy1, $this_y, + 'center', $upward ? 'top' : 'bottom', + 0, $upward ? 3 : -3, NULL, abs($y1 - $y2)); + } + // Mark the new end of the bar, conditional on segment height > 0. + $wy2 = $wy1; } - // Draw the bar - ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $this->ndx_data_colors[$idx]); + $first = FALSE; } } // end for - } // end for - } //function DrawBars + // Draw optional data label above the bar with the total value. + // Value is wy1 (total value), but position is wy2 (end of the bar stack). + // These differ only with wrong-direction segments, or a stack completely clipped by the axis. + if ($data_labels_end) { + $this->DrawDataValueLabel('y', $row+0.5, $wy2, $wy1, 'center', $upward ? 'bottom' : 'top', + 0, $upward ? $data_label_y_offset : 5); + } + } // end for + return TRUE; + } - /*! - * Data comes in as array("title", x, y, y2, y3, ...) - * \note Original stacked bars idea by Laurent Kruk < lolok at users.sourceforge.net > + /* + * Draw a Horizontal Stacked Bar chart + * Supports only text-data-yx format, with each row in the form array(label, x1, x2, x3, ...) + * Note that the data values are X not Y, and the bars are drawn horizontally. + * This is called from DrawStackedBars, which has already checked the data type. */ - function DrawStackedBars() + protected function DrawHorizStackedBars() { - if ($this->data_type != 'text-data') { - $this->DrawError('DrawStackedBars(): Bar plots must be text-data: use SetDataType("text-data")'); - return FALSE; - } + $this->CalcBarWidths(FALSE); // Calculate bar sizes for horizontal plots + + // This is the Y offset from the bar's label center point to the bottom of the bar + $y_first_bar = $this->record_bar_width / 2 - $this->bar_adjust_gap; + + $gcvars = array(); // For GetBarColors, which initializes and uses this. + + // Determine if any data labels are on: + $data_labels_within = ($this->x_data_label_pos == 'plotstack'); + $data_labels_end = $data_labels_within || ($this->x_data_label_pos == 'plotin'); + $data_label_x_offset = 5 + $this->shading; // For rightward labels only 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... + $y_now_pixels = $this->ytr(0.5 + $row); // Place bars at Y=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); + if ($this->y_data_label_pos != 'none') // Draw Y Data labels? + $this->DrawYDataLabel($this->data[$row][0], $y_now_pixels); + + // Lower left and upper left Y of the bars in this stack: + $y1 = $y_now_pixels + $y_first_bar; + $y2 = $y1 - $this->actual_bar_width; + + // Draw the bar segments in this stack: + $wx1 = 0; // World coordinates X1, current sum of values + $wx2 = $this->y_axis_position; // World coordinates X2, last drawn value + $first = TRUE; - // Draw the bars - $oldv = 0; for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) { - if (is_numeric($this->data[$row][$record])) { // Allow for missing Y data - $x1 = $x_now_pixels - $this->data_group_space; - $x2 = $x_now_pixels + $this->data_group_space; - - $y1 = $this->ytr(abs($this->data[$row][$record]) + $oldv); - $y2 = $this->ytr($this->x_axis_position + $oldv); - $oldv += abs($this->data[$row][$record]); - - 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]); + + // Skip missing X values, and ignore X<0 values. + if (is_numeric($this->data[$row][$record]) + && ($this_x = $this->data[$row][$record]) != 0) { + + // First non-zero value sets the direction, $rightward. Note this compares to 0, + // not the axis position. Segments are based at 0 but clip to the axis. + if ($first) + $rightward = ($this_x > 0); + + $wx1 += $this_x; // Keep the running total for this bar stack + + // Draw nothing if this segment would not increase the bar length. + // Rightward bars: draw if wx1>wx2. Leftward bars: Draw if wx1xtr($wx1); // Convert to device coordinates. $x1 is outermost value. + $x2 = $this->xtr($wx2); // $x2 is innermost (closest to axis). + + // Select the colors: + $this->GetBarColors($row, $idx, $gcvars, $data_color, $alt_color); + + // Draw the bar, and the shade or border: + $this->DrawBar($x1, $y1, $x2, $y2, $data_color, $alt_color, + // Only shade the side for rightward bars, or the first segment of leftward bars: + TRUE, $rightward || $first); + // Draw optional data label for this bar segment just inside the end. + // Text value is the current X, but position is the cumulative X. + // The label is only drawn if it fits in the segment width |x2-x1|. + if ($data_labels_within) { + $this->DrawDataValueLabel('x', $wx1, $row+0.5, $this_x, + $rightward ? 'right' : 'left', 'center', + $rightward ? -3 : 3, 0, abs($x1 - $x2), NULL); + } + // Mark the new end of the bar, conditional on segment width > 0. + $wx2 = $wx1; } - // Draw the bar - ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $this->ndx_data_colors[$idx]); - - } + $first = FALSE; + } } // end for + + // Draw optional data label right of the bar with the total value. + // Value is wx1 (total value), but position is wx2 (end of the bar stack). + // These differ only with wrong-direction segments, or a stack completely clipped by the axis. + if ($data_labels_end) { + $this->DrawDataValueLabel('x', $wx2, $row+0.5, $wx1, $rightward ? 'left' : 'right', 'center', + $rightward ? $data_label_x_offset : -5, 0); + } } // end for - } //function DrawStackedBars + return TRUE; + } - - /*! - * + /* + * 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. + * Note: It is possible for this to be called multiple times. */ function DrawGraph() { - if (! $this->img) { - $this->DrawError('DrawGraph(): No image resource allocated'); + // Test for missing image, missing data, empty data: + if (!$this->CheckDataArray()) + return FALSE; // Error message already reported. + + // Allocate colors for the plot: + $this->SetColorIndexes(); + + // 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; - } - if (! is_array($this->data)) { - $this->DrawError("DrawGraph(): No array of data in \$data"); + // Set plot area world values (plot_max_x, etc.): + if (!$this->CalcPlotAreaWorld()) return FALSE; - } - if (! isset($this->data_limits_done)) - $this->FindDataLimits(); // Get maxima and minima for scaling + // Calculate X and Y axis positions in World Coordinates: + $this->CalcAxisPositions(); - if ($this->total_records == 0) { // Check for empty data sets - $this->DrawError('Empty data set'); - return FALSE; - } + // Process label-related parameters: + $this->CheckLabels(); - $this->CalcMargins(); // Calculate margins + // Apply grid defaults: + $this->CalcGridSettings(); - if (! isset($this->plot_area_width)) // Set plot area pixel values (plot_area[]) - $this->SetPlotAreaPixels(); + // Calculate the plot margins, if needed. + // For pie charts, set the $maximize argument to maximize space usage. + $this->CalcMargins(!$draw_axes); - if (! isset($this->plot_max_y)) // Set plot area world values (plot_max_x, etc.) - $this->SetPlotAreaWorld(); + // Calculate the actual plot area in device coordinates: + $this->CalcPlotAreaPixels(); - if ($this->plot_type == 'bars' || $this->plot_type == 'stackedbars') // Calculate bar widths - $this->CalcBarWidths(); -/* FIXME!! this sort of thing should not be done without user's consent - if ($this->x_data_label_pos != 'none') { // Default: do not draw tick stuff if - $this->x_tick_label_pos = 'none'; // there are data labels. - $this->x_tick_pos = 'none'; - } -*/ - $this->PadArrays(); // Pad color and style arrays to fit records per group. + // Calculate the mapping between world and device coordinates: + $this->CalcTranslation(); - $this->DrawBackground(); + // 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(); - $this->DrawXTitle(); - $this->DrawYTitle(); - - // Pie charts are drawn differently, handle them first - if ($this->plot_type == 'pie') { - // Pie charts can maximize image space usage. - $this->SetPlotAreaPixels($this->safe_margin, $this->title_height, - $this->image_width - $this->safe_margin, - $this->image_height - $this->safe_margin); - $this->DrawPieChart(); - - if ($this->legend) - $this->DrawLegend($this->legend_x_pos, $this->legend_y_pos, ''); - - if ($this->print_image) - $this->PrintImage(); - - return; + if ($draw_axes) { // If no axes (pie chart), no axis titles either + $this->DrawXTitle(); + $this->DrawYTitle(); } + $this->DoCallback('draw_titles'); - ////// All other chart types: - - if (! $this->grid_at_foreground) { // Usually one wants grids to go back, but... - $this->DrawYAxis(); // Y axis must be drawn before X axis (see DrawYAxis()) + 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) { @@ -3908,277 +5941,249 @@ class PHPlot { $this->DrawSquared(); break; case 'lines': - if ( $this->data_type == 'data-data-error') { - $this->DrawLinesError(); - } else { - $this->DrawLines(); - } + $this->DrawLines(); break; - case 'linepoints': // FIXME !!! DrawXDataLabel gets called in DrawLines() and DrawDots() - if ( $this->data_type == 'data-data-error') { - $this->DrawLinesError(); - $this->DrawDotsError(); - } else { - $this->DrawLines(); - $this->DrawDots(); - } + case 'linepoints': + $this->DrawLinePoints(); break; case 'points'; - if ( $this->data_type == 'data-data-error') { - $this->DrawDotsError(); - } else { - $this->DrawDots(); - } + $this->DrawDots(); + break; + case 'pie': + $this->DrawPieChart(); break; case 'stackedbars': $this->DrawStackedBars(); - break; - case 'bars': - $this->DrawBars(); break; + case 'stackedarea': + $this->DrawArea(TRUE); + break; + // case 'bars': default: - $this->plot_type = 'bars'; // Set it if it wasn't already set. $this->DrawBars(); break; } // end switch + $this->DoCallback('draw_graph', $this->plot_area); - if ($this->grid_at_foreground) { // Usually one wants grids to go back, but... - $this->DrawYAxis(); // Y axis must be drawn before X axis (see DrawYAxis()) + 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'); } - $this->DrawPlotBorder(); - - if ($this->legend) - $this->DrawLegend($this->legend_x_pos, $this->legend_y_pos, ''); + if ($draw_axes) { + $this->DrawPlotBorder(); + $this->DoCallback('draw_border'); + } - if ($this->print_image) - $this->PrintImage(); + if ($this->legend) { + $this->DrawLegend(); + $this->DoCallback('draw_legend'); + } + $this->DoCallback('draw_all', $this->plot_area); + + if ($this->print_image && !$this->PrintImage()) + return FALSE; - } //function DrawGraph() + return TRUE; + } ///////////////////////////////////////////// ////////////////// DEPRECATED METHODS ///////////////////////////////////////////// - /*! + /* + * Note on deprecated methods - as these pre-date the PHPlot Reference + * Manual, and there is minimal documentation about them, I have neither + * removed them nor changed them. They are not tested or documented, and + * should not be used. + */ + + /* * Deprecated, use SetYTickPos() */ - function SetDrawVertTicks($which_dvt) + function SetDrawVertTicks($which_dvt) { if ($which_dvt != 1) $this->SetYTickPos('none'); return TRUE; - } + } - /*! + /* * Deprecated, use SetXTickPos() */ - function SetDrawHorizTicks($which_dht) + function SetDrawHorizTicks($which_dht) { if ($which_dht != 1) $this->SetXTickPos('none'); return TRUE; } - /*! - * \deprecated Use SetNumXTicks() + /* + * Deprecated - use SetNumXTicks() */ - function SetNumHorizTicks($n) + function SetNumHorizTicks($n) { return $this->SetNumXTicks($n); } - /*! - * \deprecated Use SetNumYTicks() + /* + * Deprecated - use SetNumYTicks() */ - function SetNumVertTicks($n) + function SetNumVertTicks($n) { return $this->SetNumYTicks($n); } - /*! - * \deprecated Use SetXTickIncrement() + /* + * Deprecated - use SetXTickIncrement() */ - function SetHorizTickIncrement($inc) + function SetHorizTickIncrement($inc) { return $this->SetXTickIncrement($inc); } - - /*! - * \deprecated Use SetYTickIncrement() + /* + * Deprecated - use SetYTickIncrement() */ - function SetVertTickIncrement($inc) + function SetVertTickIncrement($inc) { return $this->SetYTickIncrement($inc); } - /*! - * \deprecated Use SetYTickPos() + /* + * Deprecated - use SetYTickPos() */ - function SetVertTickPosition($which_tp) - { - return $this->SetYTickPos($which_tp); + function SetVertTickPosition($which_tp) + { + return $this->SetYTickPos($which_tp); } - /*! - * \deprecated Use SetXTickPos() + /* + * Deprecated - use SetXTickPos() */ - function SetHorizTickPosition($which_tp) - { + function SetHorizTickPosition($which_tp) + { return $this->SetXTickPos($which_tp); } - /*! - * \deprecated Use SetFont() + /* + * Deprecated - use SetFont() */ - function SetTitleFontSize($which_size) + function SetTitleFontSize($which_size) { return $this->SetFont('title', $which_size); } - /*! - * \deprecated Use SetFont() + /* + * Deprecated - use SetFont() */ - function SetAxisFontSize($which_size) + function SetAxisFontSize($which_size) { $this->SetFont('x_label', $which_size); - $this->SetFont('y_label', $whic_size); + $this->SetFont('y_label', $which_size); } - /*! - * \deprecated Use SetFont() + /* + * Deprecated - use SetFont() */ - function SetSmallFontSize($which_size) + function SetSmallFontSize($which_size) { return $this->SetFont('generic', $which_size); } - /*! - * \deprecated Use SetFont() + /* + * Deprecated - use SetFont() */ function SetXLabelFontSize($which_size) { return $this->SetFont('x_title', $which_size); } - /*! - * \deprecated Use SetFont() + /* + * Deprecated - use SetFont() */ - function SetYLabelFontSize($which_size) + function SetYLabelFontSize($which_size) { return $this->SetFont('y_title', $which_size); } - /*! - * \deprecated Use SetXTitle() + /* + * Deprecated - use SetXTitle() */ - function SetXLabel($which_xlab) + function SetXLabel($which_xlab) { return $this->SetXTitle($which_xlab); } - /*! - * \deprecated Use SetYTitle() - */ - function SetYLabel($which_ylab) - { - return $this->SetYTitle($which_ylab); - } - - /*! - * \deprecated This is now an Internal function - please set width and - * height via PHPlot() upon object construction + /* + * Deprecated - use SetYTitle() */ - function SetImageArea($which_iw, $which_ih) + function SetYLabel($which_ylab) { - $this->image_width = $which_iw; - $this->image_height = $which_ih; - - return TRUE; + return $this->SetYTitle($which_ylab); } - /*! - * \deprecated Use SetXTickLength() and SetYTickLength() instead. + /* + * Deprecated - use SetXTickLength() and SetYTickLength() instead. */ - function SetTickLength($which_tl) + function SetTickLength($which_tl) { $this->SetXTickLength($which_tl); $this->SetYTickLength($which_tl); return TRUE; } - /*! - * \deprecated Use SetYLabelType() + /* + * Deprecated - use SetYLabelType() */ - function SetYGridLabelType($which_yglt) + function SetYGridLabelType($which_yglt) { return $this->SetYLabelType($which_yglt); } - /*! - * \deprecated Use SetXLabelType() + /* + * Deprecated - use SetXLabelType() */ - function SetXGridLabelType($which_xglt) + function SetXGridLabelType($which_xglt) { return $this->SetXLabelType($which_xglt); } - /*! - * \deprecated Use SetYTickLabelPos() + /* + * Deprecated - use SetYTickLabelPos() */ - function SetYGridLabelPos($which_yglp) + function SetYGridLabelPos($which_yglp) { return $this->SetYTickLabelPos($which_yglp); } - /*! - * \deprecated Use SetXTickLabelPos() + /* + * Deprecated - use SetXTickLabelPos() */ - function SetXGridLabelPos($which_xglp) + function SetXGridLabelPos($which_xglp) { return $this->SetXTickLabelPos($which_xglp); } - - /*! - * \deprecated Use SetXtitle() + /* + * Deprecated - use SetXtitle() */ - function SetXTitlePos($xpos) + function SetXTitlePos($xpos) { $this->x_title_pos = $xpos; return TRUE; } - /*! - * \deprecated Use SetYTitle() + /* + * Deprecated - use SetYTitle() */ - function SetYTitlePos($xpos) + function SetYTitlePos($xpos) { $this->y_title_pos = $xpos; return TRUE; } - /*! - * \deprecated Use DrawDots() - */ - function DrawDotSeries() - { - $this->DrawDots(); - } - - /*! - * \deprecated Use SetXLabelAngle() - */ - function SetXDataLabelAngle($which_xdla) - { - return $this->SetXLabelAngle($which_xdla); - } - - /*! - * 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() + /* + * Deprecated - use SetXDataLabelPos() */ function SetDrawXDataLabels($which_dxdl) { @@ -4188,114 +6193,16 @@ class PHPlot { $this->SetXDataLabelPos('none'); } - /*! - * \deprecated This method was intended to improve performance by being specially - * written for 'data-data'. However, the improvement didn't pay. Use DrawLines() instead - */ - function DrawLineSeries() - { - return $this->DrawLines(); - } - - /*! - * \deprecated Calculates maximum X-Axis label height. Now inside CalcMargins() - */ - function CalcXHeights() - { - // TTF - if ($this->use_ttf) { - $xstr = str_repeat('.', $this->max_t); - $size = $this->TTFBBoxSize($this->x_label_font['size'], $this->x_label_angle, - $this->x_label_font['font'], $xstr); - $this->x_tick_label_height = $size[1]; - } - // Fixed font - else { // For Non-TTF fonts we can have only angles 0 or 90 - if ($this->x_label_angle == 90) - $this->x_tick_label_height = $this->max_t * $this->x_label_font['width']; - else - $this->x_tick_label_height = $this->x_label_font['height']; - } - - return TRUE; - } - - - /*! - * \deprecated Calculates Maximum Y-Axis tick label width. Now inside CalcMargins() - */ - function CalcYWidths() - { - //the "." is for space. It isn't actually printed - $ylab = number_format($this->max_y, $this->y_precision, '.', ', ') . $this->data_units_text . '.'; - - // TTF - if ($this->use_ttf) { - // Maximum Y tick label width - $size = $this->TTFBBoxSize($this->y_label_font['size'], 0, $this->y_label_font['font'], $ylab); - $this->y_tick_label_width = $size[0]; - - } - // Fixed font - else { - // Y axis title width - $this->y_tick_label_width = strlen($ylab) * $this->y_label_font['width']; - } - - return TRUE; - } - - /*! - * \deprecated Superfluous. - */ - function DrawLabels() - { - $this->DrawTitle(); - $this->DrawXTitle(); - $this->DrawYTitle(); - } - - /*! - * Set up the image resource 'img' - * \deprecated The constructor should init 'img' - */ - function InitImage() - { - $this->img = ImageCreate($this->image_width, $this->image_height); - - if (! $this->img) - $this->PrintError('InitImage(): Could not create image resource'); - return TRUE; - } - - /*! - * \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() + /* + * Deprecated - use SetPlotAreaPixels() */ - function SetColor($which_color) + function SetNewPlotAreaPixels($x1, $y1, $x2, $y2) { - $this->SetRGBColor($which_color); - return TRUE; + return $this->SetPlotAreaPixels($x1, $y1, $x2, $y2); } /* - * \deprecated Use SetLineWidths(). + * Deprecated - use SetLineWidths(). */ function SetLineWidth($which_lw) { @@ -4308,79 +6215,8 @@ class PHPlot { return TRUE; } - /*! - * \deprecated - */ - function DrawDashedLine($x1, $y1, $x2, $y2 , $dash_length, $dash_space, $color) - { - if ($dash_length) - $dashes = array_fill(0, $dash_length, $color); - else - $dashes = array(); - if ($dash_space) - $spaces = array_fill(0, $dash_space, IMG_COLOR_TRANSPARENT); - else - $spaces = array(); - - $style = array_merge($dashes, $spaces); - ImageSetStyle($this->img, $style); - ImageLine($this->img, $x1, $y1, $x2, $y2, IMG_COLOR_STYLED); - } - - /*! - * \deprecated Selects an input file to be used as background for the whole graph. - * This resizes the graph to the image's size. - */ - function SetInputFile($which_input_file) - { - $size = GetImageSize($which_input_file); - $input_type = $size[2]; - - switch($input_type) { - case 1: - $im = @ ImageCreateFromGIF ($which_input_file); - if (!$im) { // See if it failed - $this->PrintError("Unable to open $which_input_file as a GIF"); - return FALSE; - } - break; - case 3: - $im = @ ImageCreateFromPNG ($which_input_file); - if (!$im) { // See if it failed - $this->PrintError("Unable to open $which_input_file as a PNG"); - return FALSE; - } - break; - case 2: - $im = @ ImageCreateFromJPEG ($which_input_file); - if (!$im) { // See if it failed - $this->PrintError("Unable to open $which_input_file as a JPG"); - return FALSE; - } - break; - default: - $this->PrintError('SetInputFile(): Please select gif, jpg, or png for image type!'); - return FALSE; - break; - } - - // Set Width and Height of Image - $this->image_width = $size[0]; - $this->image_height = $size[1]; - - // Deallocate any resources previously allocated - if ($this->img) - imagedestroy($this->img); - - $this->img = $im; - - return TRUE; - - } - - /* - * \deprecated Use SetPointShapes(). + * Deprecated - use SetPointShapes(). */ function SetPointShape($which_pt) { @@ -4389,60 +6225,51 @@ class PHPlot { } /* - * \deprecated Use SetPointSizes(). + * Deprecated - use SetPointSizes(). */ function SetPointSize($which_ps) { $this->SetPointSizes($which_ps); return TRUE; } -} // class PHPlot - - - -//////////////////////// - - -/*! - * Pads an array with another or with itself. - * \param arr array Original array (reference) - * \param size int Size of the resulting array. - * \param arr2 array If specified, array to use for padding. If unspecified, pad with $arr. - */ -function array_pad_array(&$arr, $size, $arr2=NULL) -{ - if (! is_array($arr2)) { - $arr2 = $arr; // copy the original array - } - while (count($arr) < $size) - $arr = array_merge_php4($arr, $arr2); // append until done } -/*! - * Fixes problem with array_merge() in PHP5. - * \note I simply copied this from a bug report. I am not running php5 yet, so - * I cannot reproduce it, which is why I trust the reporter. +/* + * The PHPlot_truecolor class extends PHPlot to use GD truecolor images. */ -function array_merge_php4($array1,$array2) + +class PHPlot_truecolor extends PHPlot { - $return=array(); + /* + * 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 : Image width in pixels. + * $which_height : Image height in pixels. + * $which_output_file : Filename for output. + * $which_input_file : 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); - foreach(func_get_args() as $arg){ - if(!is_array($arg)){ - $arg=array($arg); - } - foreach($arg as $key=>$val){ - if(!is_int($key)){ - $return[$key]=$val; - }else{ - $return[]=$val; - } - } - } - return $return; - } - - + 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/gui/bacula-web/external_packages/phplot/phplot_data.php b/gui/bacula-web/external_packages/phplot/phplot_data.php deleted file mode 100644 index 37351504c1..0000000000 --- a/gui/bacula-web/external_packages/phplot/phplot_data.php +++ /dev/null @@ -1,258 +0,0 @@ -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 - 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/gui/bacula-web/external_packages/phplot/rgb.inc.php b/gui/bacula-web/external_packages/phplot/rgb.inc.php index 3353e28e50..41256ee25d 100644 --- a/gui/bacula-web/external_packages/phplot/rgb.inc.php +++ b/gui/bacula-web/external_packages/phplot/rgb.inc.php @@ -1,7 +1,16 @@ 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), @@ -741,4 +750,3 @@ $ColorArray = array( "grey99" => array(252, 252, 252), "gray100" => array(255, 255, 255) ); -?>