From 79e3c052e20257d2a20efae47691f354947717e9 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Thu, 11 Dec 2014 11:56:18 +0900 Subject: [PATCH] Update pChart to 2.1.4, fix small bug in Basic class For the host name set, if there is no data in the HTTP_HOST var, then set it to NA instead. Also default set to port 80 if no port info could be found. --- www/libs/Class.Basic.inc | 7 +- www/libs/pChart | 2 +- www/libs/pChart2.1.3/GPLv3.txt | 675 - www/libs/pChart2.1.3/fonts/tahoma.ttf | Bin 383804 -> 0 bytes .../class/pBarcode128.class.php | 366 +- .../class/pBarcode39.class.php | 398 +- .../class/pBubble.class.php | 650 +- .../class/pCache.class.php | 558 +- .../class/pData.class.php | 1573 +- .../class/pDraw.class.php | 12409 ++++++++-------- .../class/pImage.class.php | 942 +- .../class/pIndicator.class.php | 480 +- .../class/pPie.class.php | 2998 ++-- .../class/pRadar.class.php | 1360 +- .../class/pScatter.class.php | 2314 +-- .../class/pSplit.class.php | 260 +- .../class/pSpring.class.php | 1734 +-- .../class/pStock.class.php | 430 +- .../class/pSurface.class.php | 628 +- .../{pChart2.1.3 => pChart2.1.4}/data/128B.db | 0 .../{pChart2.1.3 => pChart2.1.4}/data/39.db | 0 .../fonts/Bedizen.ttf | Bin .../fonts/Forgotte.ttf | Bin .../fonts/GeosansLight.ttf | Bin .../fonts/MankSans.ttf | Bin .../fonts/Silkscreen.ttf | Bin .../fonts/advent_light.ttf | Bin .../fonts/calibri.ttf | Bin .../fonts/pf_arma_five.ttf | Bin .../fonts/verdana.ttf | Bin .../palettes/autumn.color | 0 .../palettes/blind.color | 0 .../palettes/evening.color | 0 .../palettes/kitchen.color | 0 .../palettes/light.color | 0 .../palettes/navy.color | 0 .../palettes/shade.color | 0 .../palettes/spring.color | 0 .../palettes/summer.color | 0 39 files changed, 13568 insertions(+), 14216 deletions(-) delete mode 100644 www/libs/pChart2.1.3/GPLv3.txt delete mode 100644 www/libs/pChart2.1.3/fonts/tahoma.ttf rename www/libs/{pChart2.1.3 => pChart2.1.4}/class/pBarcode128.class.php (96%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/class/pBarcode39.class.php (96%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/class/pBubble.class.php (97%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/class/pCache.class.php (96%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/class/pData.class.php (97%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/class/pDraw.class.php (96%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/class/pImage.class.php (96%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/class/pIndicator.class.php (97%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/class/pPie.class.php (97%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/class/pRadar.class.php (97%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/class/pScatter.class.php (98%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/class/pSplit.class.php (96%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/class/pSpring.class.php (97%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/class/pStock.class.php (97%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/class/pSurface.class.php (97%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/data/128B.db (100%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/data/39.db (100%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/fonts/Bedizen.ttf (100%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/fonts/Forgotte.ttf (100%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/fonts/GeosansLight.ttf (100%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/fonts/MankSans.ttf (100%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/fonts/Silkscreen.ttf (100%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/fonts/advent_light.ttf (100%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/fonts/calibri.ttf (100%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/fonts/pf_arma_five.ttf (100%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/fonts/verdana.ttf (100%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/palettes/autumn.color (100%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/palettes/blind.color (100%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/palettes/evening.color (100%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/palettes/kitchen.color (100%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/palettes/light.color (100%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/palettes/navy.color (100%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/palettes/shade.color (100%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/palettes/spring.color (100%) rename www/libs/{pChart2.1.3 => pChart2.1.4}/palettes/summer.color (100%) diff --git a/www/libs/Class.Basic.inc b/www/libs/Class.Basic.inc index 6e99e453..3a20c500 100644 --- a/www/libs/Class.Basic.inc +++ b/www/libs/Class.Basic.inc @@ -807,8 +807,11 @@ // get the host name without the port as given by the SELF var public function get_host_name() { - list($host_name, $port) = explode(":", $_SERVER['HTTP_HOST']); - $this->host_port = $port; + if ($_SERVER['HTTP_HOST']) + list($host_name, $port) = explode(":", $_SERVER['HTTP_HOST']); + else + $host_name = 'NA'; + $this->host_port = $port ? $port : 80; return $host_name; } diff --git a/www/libs/pChart b/www/libs/pChart index aba99551..f2963ead 120000 --- a/www/libs/pChart +++ b/www/libs/pChart @@ -1 +1 @@ -pChart2.1.3/ \ No newline at end of file +pChart2.1.4/ \ No newline at end of file diff --git a/www/libs/pChart2.1.3/GPLv3.txt b/www/libs/pChart2.1.3/GPLv3.txt deleted file mode 100644 index 10926e87..00000000 --- a/www/libs/pChart2.1.3/GPLv3.txt +++ /dev/null @@ -1,675 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. 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 -them 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 prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. 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. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey 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; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If 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 convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU 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 that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - 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. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -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. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - 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 -state 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 3 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, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program 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, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU 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 Lesser General -Public License instead of this License. But first, please read -. - diff --git a/www/libs/pChart2.1.3/fonts/tahoma.ttf b/www/libs/pChart2.1.3/fonts/tahoma.ttf deleted file mode 100644 index 59b14a2d2d45b78eecbb2f22e1d2e084678e8d94..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 383804 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 O@`a}xV@&j7=>7#Q^<=;R diff --git a/www/libs/pChart2.1.3/class/pBarcode128.class.php b/www/libs/pChart2.1.4/class/pBarcode128.class.php similarity index 96% rename from www/libs/pChart2.1.3/class/pBarcode128.class.php rename to www/libs/pChart2.1.4/class/pBarcode128.class.php index 670ca631..d1c29cc6 100644 --- a/www/libs/pChart2.1.3/class/pBarcode128.class.php +++ b/www/libs/pChart2.1.4/class/pBarcode128.class.php @@ -1,184 +1,184 @@ -Codes = ""; - $this->Reverse = ""; - - $FileHandle = @fopen($BasePath."data/128B.db", "r"); - - if (!$FileHandle) { die("Cannot find barcode database (".$BasePath."128B.db)."); } - - while (!feof($FileHandle)) - { - $Buffer = fgets($FileHandle,4096); - $Buffer = str_replace(chr(10),"",$Buffer); - $Buffer = str_replace(chr(13),"",$Buffer); - $Values = preg_split("/;/",$Buffer); - - $this->Codes[$Values[1]]["ID"] = $Values[0]; - $this->Codes[$Values[1]]["Code"] = $Values[2]; - $this->Reverse[$Values[0]]["Code"] = $Values[2]; - $this->Reverse[$Values[0]]["Asc"] = $Values[1]; - } - fclose($FileHandle); - } - - /* Return the projected size of a barcode */ - function getSize($TextString,$Format="") - { - $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0; - $ShowLegend = isset($Format["ShowLegend"]) ? $Format["ShowLegend"] : FALSE; - $LegendOffset = isset($Format["LegendOffset"]) ? $Format["LegendOffset"] : 5; - $DrawArea = isset($Format["DrawArea"]) ? $Format["DrawArea"] : FALSE; - $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : 12; - $Height = isset($Format["Height"]) ? $Format["Height"] : 30; - - $TextString = $this->encode128($TextString); - $BarcodeLength = strlen($this->Result); - - if ( $DrawArea ) { $WOffset = 20; } else { $WOffset = 0; } - if ( $ShowLegend ) { $HOffset = $FontSize+$LegendOffset+$WOffset; } else { $HOffset = 0; } - - $X1 = cos($Angle * PI / 180) * ($WOffset+$BarcodeLength); - $Y1 = sin($Angle * PI / 180) * ($WOffset+$BarcodeLength); - - $X2 = $X1 + cos(($Angle+90) * PI / 180) * ($HOffset+$Height); - $Y2 = $Y1 + sin(($Angle+90) * PI / 180) * ($HOffset+$Height); - - - $AreaWidth = max(abs($X1),abs($X2)); - $AreaHeight = max(abs($Y1),abs($Y2)); - - return(array("Width"=>$AreaWidth,"Height"=>$AreaHeight)); - } - - function encode128($Value,$Format="") - { - $this->Result = "11010010000"; - $this->CRC = 104; - $TextString = ""; - - for($i=1;$i<=strlen($Value);$i++) - { - $CharCode = ord($this->mid($Value,$i,1)); - if ( isset($this->Codes[$CharCode]) ) - { - $this->Result = $this->Result.$this->Codes[$CharCode]["Code"]; - $this->CRC = $this->CRC + $i*$this->Codes[$CharCode]["ID"]; - $TextString = $TextString.chr($CharCode); - } - } - $this->CRC = $this->CRC - floor($this->CRC/103)*103; - - $this->Result = $this->Result.$this->Reverse[$this->CRC]["Code"]; - $this->Result = $this->Result."1100011101011"; - - return($TextString); - } - - /* Create the encoded string */ - function draw($Object,$Value,$X,$Y,$Format="") - { - $this->pChartObject = $Object; - - $R = isset($Format["R"]) ? $Format["R"] : 0; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $Height = isset($Format["Height"]) ? $Format["Height"] : 30; - $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0; - $ShowLegend = isset($Format["ShowLegend"]) ? $Format["ShowLegend"] : FALSE; - $LegendOffset = isset($Format["LegendOffset"]) ? $Format["LegendOffset"] : 5; - $DrawArea = isset($Format["DrawArea"]) ? $Format["DrawArea"] : FALSE; - $AreaR = isset($Format["AreaR"]) ? $Format["AreaR"] : 255; - $AreaG = isset($Format["AreaG"]) ? $Format["AreaG"] : 255; - $AreaB = isset($Format["AreaB"]) ? $Format["AreaB"] : 255; - $AreaBorderR = isset($Format["AreaBorderR"]) ? $Format["AreaBorderR"] : $AreaR; - $AreaBorderG = isset($Format["AreaBorderG"]) ? $Format["AreaBorderG"] : $AreaG; - $AreaBorderB = isset($Format["AreaBorderB"]) ? $Format["AreaBorderB"] : $AreaB; - - $TextString = $this->encode128($Value); - - if ( $DrawArea ) - { - $X1 = $X + cos(($Angle-135) * PI / 180) * 10; - $Y1 = $Y + sin(($Angle-135) * PI / 180) * 10; - - $X2 = $X1 + cos($Angle * PI / 180) * (strlen($this->Result)+20); - $Y2 = $Y1 + sin($Angle * PI / 180) * (strlen($this->Result)+20); - - if ( $ShowLegend ) - { - $X3 = $X2 + cos(($Angle+90) * PI / 180) * ($Height+$LegendOffset+$this->pChartObject->FontSize+10); - $Y3 = $Y2 + sin(($Angle+90) * PI / 180) * ($Height+$LegendOffset+$this->pChartObject->FontSize+10); - } - else - { - $X3 = $X2 + cos(($Angle+90) * PI / 180) * ($Height+20); - $Y3 = $Y2 + sin(($Angle+90) * PI / 180) * ($Height+20); - } - - $X4 = $X3 + cos(($Angle+180) * PI / 180) * (strlen($this->Result)+20); - $Y4 = $Y3 + sin(($Angle+180) * PI / 180) * (strlen($this->Result)+20); - - $Polygon = array($X1,$Y1,$X2,$Y2,$X3,$Y3,$X4,$Y4); - $Settings = array("R"=>$AreaR,"G"=>$AreaG,"B"=>$AreaB,"BorderR"=>$AreaBorderR,"BorderG"=>$AreaBorderG,"BorderB"=>$AreaBorderB); - $this->pChartObject->drawPolygon($Polygon,$Settings); - } - - for($i=1;$i<=strlen($this->Result);$i++) - { - if ( $this->mid($this->Result,$i,1) == 1 ) - { - $X1 = $X + cos($Angle * PI / 180) * $i; - $Y1 = $Y + sin($Angle * PI / 180) * $i; - $X2 = $X1 + cos(($Angle+90) * PI / 180) * $Height; - $Y2 = $Y1 + sin(($Angle+90) * PI / 180) * $Height; - - $Settings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); - $this->pChartObject->drawLine($X1,$Y1,$X2,$Y2,$Settings); - } - } - - if ( $ShowLegend ) - { - $X1 = $X + cos($Angle * PI / 180) * (strlen($this->Result)/2); - $Y1 = $Y + sin($Angle * PI / 180) * (strlen($this->Result)/2); - - $LegendX = $X1 + cos(($Angle+90) * PI / 180) * ($Height+$LegendOffset); - $LegendY = $Y1 + sin(($Angle+90) * PI / 180) * ($Height+$LegendOffset); - - $Settings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Angle"=>-$Angle,"Align"=>TEXT_ALIGN_TOPMIDDLE); - $this->pChartObject->drawText($LegendX,$LegendY,$TextString,$Settings); - } - } - - function left($value,$NbChar) { return substr($value,0,$NbChar); } - function right($value,$NbChar) { return substr($value,strlen($value)-$NbChar,$NbChar); } - function mid($value,$Depart,$NbChar) { return substr($value,$Depart-1,$NbChar); } - } +Codes = ""; + $this->Reverse = ""; + + $FileHandle = @fopen($BasePath."data/128B.db", "r"); + + if (!$FileHandle) { die("Cannot find barcode database (".$BasePath."128B.db)."); } + + while (!feof($FileHandle)) + { + $Buffer = fgets($FileHandle,4096); + $Buffer = str_replace(chr(10),"",$Buffer); + $Buffer = str_replace(chr(13),"",$Buffer); + $Values = preg_split("/;/",$Buffer); + + $this->Codes[$Values[1]]["ID"] = $Values[0]; + $this->Codes[$Values[1]]["Code"] = $Values[2]; + $this->Reverse[$Values[0]]["Code"] = $Values[2]; + $this->Reverse[$Values[0]]["Asc"] = $Values[1]; + } + fclose($FileHandle); + } + + /* Return the projected size of a barcode */ + function getSize($TextString,$Format="") + { + $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0; + $ShowLegend = isset($Format["ShowLegend"]) ? $Format["ShowLegend"] : FALSE; + $LegendOffset = isset($Format["LegendOffset"]) ? $Format["LegendOffset"] : 5; + $DrawArea = isset($Format["DrawArea"]) ? $Format["DrawArea"] : FALSE; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : 12; + $Height = isset($Format["Height"]) ? $Format["Height"] : 30; + + $TextString = $this->encode128($TextString); + $BarcodeLength = strlen($this->Result); + + if ( $DrawArea ) { $WOffset = 20; } else { $WOffset = 0; } + if ( $ShowLegend ) { $HOffset = $FontSize+$LegendOffset+$WOffset; } else { $HOffset = 0; } + + $X1 = cos($Angle * PI / 180) * ($WOffset+$BarcodeLength); + $Y1 = sin($Angle * PI / 180) * ($WOffset+$BarcodeLength); + + $X2 = $X1 + cos(($Angle+90) * PI / 180) * ($HOffset+$Height); + $Y2 = $Y1 + sin(($Angle+90) * PI / 180) * ($HOffset+$Height); + + + $AreaWidth = max(abs($X1),abs($X2)); + $AreaHeight = max(abs($Y1),abs($Y2)); + + return(array("Width"=>$AreaWidth,"Height"=>$AreaHeight)); + } + + function encode128($Value,$Format="") + { + $this->Result = "11010010000"; + $this->CRC = 104; + $TextString = ""; + + for($i=1;$i<=strlen($Value);$i++) + { + $CharCode = ord($this->mid($Value,$i,1)); + if ( isset($this->Codes[$CharCode]) ) + { + $this->Result = $this->Result.$this->Codes[$CharCode]["Code"]; + $this->CRC = $this->CRC + $i*$this->Codes[$CharCode]["ID"]; + $TextString = $TextString.chr($CharCode); + } + } + $this->CRC = $this->CRC - floor($this->CRC/103)*103; + + $this->Result = $this->Result.$this->Reverse[$this->CRC]["Code"]; + $this->Result = $this->Result."1100011101011"; + + return($TextString); + } + + /* Create the encoded string */ + function draw($Object,$Value,$X,$Y,$Format="") + { + $this->pChartObject = $Object; + + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Height = isset($Format["Height"]) ? $Format["Height"] : 30; + $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0; + $ShowLegend = isset($Format["ShowLegend"]) ? $Format["ShowLegend"] : FALSE; + $LegendOffset = isset($Format["LegendOffset"]) ? $Format["LegendOffset"] : 5; + $DrawArea = isset($Format["DrawArea"]) ? $Format["DrawArea"] : FALSE; + $AreaR = isset($Format["AreaR"]) ? $Format["AreaR"] : 255; + $AreaG = isset($Format["AreaG"]) ? $Format["AreaG"] : 255; + $AreaB = isset($Format["AreaB"]) ? $Format["AreaB"] : 255; + $AreaBorderR = isset($Format["AreaBorderR"]) ? $Format["AreaBorderR"] : $AreaR; + $AreaBorderG = isset($Format["AreaBorderG"]) ? $Format["AreaBorderG"] : $AreaG; + $AreaBorderB = isset($Format["AreaBorderB"]) ? $Format["AreaBorderB"] : $AreaB; + + $TextString = $this->encode128($Value); + + if ( $DrawArea ) + { + $X1 = $X + cos(($Angle-135) * PI / 180) * 10; + $Y1 = $Y + sin(($Angle-135) * PI / 180) * 10; + + $X2 = $X1 + cos($Angle * PI / 180) * (strlen($this->Result)+20); + $Y2 = $Y1 + sin($Angle * PI / 180) * (strlen($this->Result)+20); + + if ( $ShowLegend ) + { + $X3 = $X2 + cos(($Angle+90) * PI / 180) * ($Height+$LegendOffset+$this->pChartObject->FontSize+10); + $Y3 = $Y2 + sin(($Angle+90) * PI / 180) * ($Height+$LegendOffset+$this->pChartObject->FontSize+10); + } + else + { + $X3 = $X2 + cos(($Angle+90) * PI / 180) * ($Height+20); + $Y3 = $Y2 + sin(($Angle+90) * PI / 180) * ($Height+20); + } + + $X4 = $X3 + cos(($Angle+180) * PI / 180) * (strlen($this->Result)+20); + $Y4 = $Y3 + sin(($Angle+180) * PI / 180) * (strlen($this->Result)+20); + + $Polygon = array($X1,$Y1,$X2,$Y2,$X3,$Y3,$X4,$Y4); + $Settings = array("R"=>$AreaR,"G"=>$AreaG,"B"=>$AreaB,"BorderR"=>$AreaBorderR,"BorderG"=>$AreaBorderG,"BorderB"=>$AreaBorderB); + $this->pChartObject->drawPolygon($Polygon,$Settings); + } + + for($i=1;$i<=strlen($this->Result);$i++) + { + if ( $this->mid($this->Result,$i,1) == 1 ) + { + $X1 = $X + cos($Angle * PI / 180) * $i; + $Y1 = $Y + sin($Angle * PI / 180) * $i; + $X2 = $X1 + cos(($Angle+90) * PI / 180) * $Height; + $Y2 = $Y1 + sin(($Angle+90) * PI / 180) * $Height; + + $Settings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); + $this->pChartObject->drawLine($X1,$Y1,$X2,$Y2,$Settings); + } + } + + if ( $ShowLegend ) + { + $X1 = $X + cos($Angle * PI / 180) * (strlen($this->Result)/2); + $Y1 = $Y + sin($Angle * PI / 180) * (strlen($this->Result)/2); + + $LegendX = $X1 + cos(($Angle+90) * PI / 180) * ($Height+$LegendOffset); + $LegendY = $Y1 + sin(($Angle+90) * PI / 180) * ($Height+$LegendOffset); + + $Settings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Angle"=>-$Angle,"Align"=>TEXT_ALIGN_TOPMIDDLE); + $this->pChartObject->drawText($LegendX,$LegendY,$TextString,$Settings); + } + } + + function left($value,$NbChar) { return substr($value,0,$NbChar); } + function right($value,$NbChar) { return substr($value,strlen($value)-$NbChar,$NbChar); } + function mid($value,$Depart,$NbChar) { return substr($value,$Depart-1,$NbChar); } + } ?> \ No newline at end of file diff --git a/www/libs/pChart2.1.3/class/pBarcode39.class.php b/www/libs/pChart2.1.4/class/pBarcode39.class.php similarity index 96% rename from www/libs/pChart2.1.3/class/pBarcode39.class.php rename to www/libs/pChart2.1.4/class/pBarcode39.class.php index 554be9be..fd73c0eb 100644 --- a/www/libs/pChart2.1.3/class/pBarcode39.class.php +++ b/www/libs/pChart2.1.4/class/pBarcode39.class.php @@ -1,200 +1,200 @@ -MOD43 = $EnableMOD43; - $this->Codes = ""; - $this->Reverse = ""; - - $FileHandle = @fopen($BasePath."data/39.db", "r"); - - if (!$FileHandle) { die("Cannot find barcode database (".$BasePath."data/39.db)."); } - - while (!feof($FileHandle)) - { - $Buffer = fgets($FileHandle,4096); - $Buffer = str_replace(chr(10),"",$Buffer); - $Buffer = str_replace(chr(13),"",$Buffer); - $Values = preg_split("/;/",$Buffer); - - $this->Codes[$Values[0]] = $Values[1]; - } - fclose($FileHandle); - } - - /* Return the projected size of a barcode */ - function getSize($TextString,$Format="") - { - $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0; - $ShowLegend = isset($Format["ShowLegend"]) ? $Format["ShowLegend"] : FALSE; - $LegendOffset = isset($Format["LegendOffset"]) ? $Format["LegendOffset"] : 5; - $DrawArea = isset($Format["DrawArea"]) ? $Format["DrawArea"] : FALSE; - $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : 12; - $Height = isset($Format["Height"]) ? $Format["Height"] : 30; - - $TextString = $this->encode39($TextString); - $BarcodeLength = strlen($this->Result); - - if ( $DrawArea ) { $WOffset = 20; } else { $WOffset = 0; } - if ( $ShowLegend ) { $HOffset = $FontSize+$LegendOffset+$WOffset; } else { $HOffset = 0; } - - $X1 = cos($Angle * PI / 180) * ($WOffset+$BarcodeLength); - $Y1 = sin($Angle * PI / 180) * ($WOffset+$BarcodeLength); - - $X2 = $X1 + cos(($Angle+90) * PI / 180) * ($HOffset+$Height); - $Y2 = $Y1 + sin(($Angle+90) * PI / 180) * ($HOffset+$Height); - - - $AreaWidth = max(abs($X1),abs($X2)); - $AreaHeight = max(abs($Y1),abs($Y2)); - - return(array("Width"=>$AreaWidth,"Height"=>$AreaHeight)); - } - - /* Create the encoded string */ - function encode39($Value) - { - $this->Result = "100101101101"."0"; - $TextString = ""; - for($i=1;$i<=strlen($Value);$i++) - { - $CharCode = ord($this->mid($Value,$i,1)); - if ( $CharCode >= 97 && $CharCode <= 122 ) { $CharCode = $CharCode - 32; } - - if ( isset($this->Codes[chr($CharCode)]) ) - { - $this->Result = $this->Result.$this->Codes[chr($CharCode)]."0"; - $TextString = $TextString.chr($CharCode); - } - } - - if ( $this->MOD43 ) - { - $Checksum = $this->checksum($TextString); - $this->Result = $this->Result.$this->Codes[$Checksum]."0"; - } - - $this->Result = $this->Result."100101101101"; - $TextString = "*".$TextString."*"; - - return($TextString); - } - - /* Create the encoded string */ - function draw($Object,$Value,$X,$Y,$Format="") - { - $this->pChartObject = $Object; - - $R = isset($Format["R"]) ? $Format["R"] : 0; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $Height = isset($Format["Height"]) ? $Format["Height"] : 30; - $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0; - $ShowLegend = isset($Format["ShowLegend"]) ? $Format["ShowLegend"] : FALSE; - $LegendOffset = isset($Format["LegendOffset"]) ? $Format["LegendOffset"] : 5; - $DrawArea = isset($Format["DrawArea"]) ? $Format["DrawArea"] : FALSE; - $AreaR = isset($Format["AreaR"]) ? $Format["AreaR"] : 255; - $AreaG = isset($Format["AreaG"]) ? $Format["AreaG"] : 255; - $AreaB = isset($Format["AreaB"]) ? $Format["AreaB"] : 255; - $AreaBorderR = isset($Format["AreaBorderR"]) ? $Format["AreaBorderR"] : $AreaR; - $AreaBorderG = isset($Format["AreaBorderG"]) ? $Format["AreaBorderG"] : $AreaG; - $AreaBorderB = isset($Format["AreaBorderB"]) ? $Format["AreaBorderB"] : $AreaB; - - $TextString = $this->encode39($Value); - - if ( $DrawArea ) - { - $X1 = $X + cos(($Angle-135) * PI / 180) * 10; - $Y1 = $Y + sin(($Angle-135) * PI / 180) * 10; - - $X2 = $X1 + cos($Angle * PI / 180) * (strlen($this->Result)+20); - $Y2 = $Y1 + sin($Angle * PI / 180) * (strlen($this->Result)+20); - - if ( $ShowLegend ) - { - $X3 = $X2 + cos(($Angle+90) * PI / 180) * ($Height+$LegendOffset+$this->pChartObject->FontSize+10); - $Y3 = $Y2 + sin(($Angle+90) * PI / 180) * ($Height+$LegendOffset+$this->pChartObject->FontSize+10); - } - else - { - $X3 = $X2 + cos(($Angle+90) * PI / 180) * ($Height+20); - $Y3 = $Y2 + sin(($Angle+90) * PI / 180) * ($Height+20); - } - - $X4 = $X3 + cos(($Angle+180) * PI / 180) * (strlen($this->Result)+20); - $Y4 = $Y3 + sin(($Angle+180) * PI / 180) * (strlen($this->Result)+20); - - $Polygon = array($X1,$Y1,$X2,$Y2,$X3,$Y3,$X4,$Y4); - $Settings = array("R"=>$AreaR,"G"=>$AreaG,"B"=>$AreaB,"BorderR"=>$AreaBorderR,"BorderG"=>$AreaBorderG,"BorderB"=>$AreaBorderB); - $this->pChartObject->drawPolygon($Polygon,$Settings); - } - - for($i=1;$i<=strlen($this->Result);$i++) - { - if ( $this->mid($this->Result,$i,1) == 1 ) - { - $X1 = $X + cos($Angle * PI / 180) * $i; - $Y1 = $Y + sin($Angle * PI / 180) * $i; - $X2 = $X1 + cos(($Angle+90) * PI / 180) * $Height; - $Y2 = $Y1 + sin(($Angle+90) * PI / 180) * $Height; - - $Settings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); - $this->pChartObject->drawLine($X1,$Y1,$X2,$Y2,$Settings); - } - } - - if ( $ShowLegend ) - { - $X1 = $X + cos($Angle * PI / 180) * (strlen($this->Result)/2); - $Y1 = $Y + sin($Angle * PI / 180) * (strlen($this->Result)/2); - - $LegendX = $X1 + cos(($Angle+90) * PI / 180) * ($Height+$LegendOffset); - $LegendY = $Y1 + sin(($Angle+90) * PI / 180) * ($Height+$LegendOffset); - - $Settings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Angle"=>-$Angle,"Align"=>TEXT_ALIGN_TOPMIDDLE); - $this->pChartObject->drawText($LegendX,$LegendY,$TextString,$Settings); - } - } - - function checksum( $string ) - { - $checksum = 0; - $length = strlen( $string ); - $charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%'; - - for( $i=0; $i < $length; ++$i ) - $checksum += strpos( $charset, $string[$i] ); - - return substr( $charset, ($checksum % 43), 1 ); - } - - function left($value,$NbChar) { return substr($value,0,$NbChar); } - function right($value,$NbChar) { return substr($value,strlen($value)-$NbChar,$NbChar); } - function mid($value,$Depart,$NbChar) { return substr($value,$Depart-1,$NbChar); } - } +MOD43 = $EnableMOD43; + $this->Codes = ""; + $this->Reverse = ""; + + $FileHandle = @fopen($BasePath."data/39.db", "r"); + + if (!$FileHandle) { die("Cannot find barcode database (".$BasePath."data/39.db)."); } + + while (!feof($FileHandle)) + { + $Buffer = fgets($FileHandle,4096); + $Buffer = str_replace(chr(10),"",$Buffer); + $Buffer = str_replace(chr(13),"",$Buffer); + $Values = preg_split("/;/",$Buffer); + + $this->Codes[$Values[0]] = $Values[1]; + } + fclose($FileHandle); + } + + /* Return the projected size of a barcode */ + function getSize($TextString,$Format="") + { + $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0; + $ShowLegend = isset($Format["ShowLegend"]) ? $Format["ShowLegend"] : FALSE; + $LegendOffset = isset($Format["LegendOffset"]) ? $Format["LegendOffset"] : 5; + $DrawArea = isset($Format["DrawArea"]) ? $Format["DrawArea"] : FALSE; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : 12; + $Height = isset($Format["Height"]) ? $Format["Height"] : 30; + + $TextString = $this->encode39($TextString); + $BarcodeLength = strlen($this->Result); + + if ( $DrawArea ) { $WOffset = 20; } else { $WOffset = 0; } + if ( $ShowLegend ) { $HOffset = $FontSize+$LegendOffset+$WOffset; } else { $HOffset = 0; } + + $X1 = cos($Angle * PI / 180) * ($WOffset+$BarcodeLength); + $Y1 = sin($Angle * PI / 180) * ($WOffset+$BarcodeLength); + + $X2 = $X1 + cos(($Angle+90) * PI / 180) * ($HOffset+$Height); + $Y2 = $Y1 + sin(($Angle+90) * PI / 180) * ($HOffset+$Height); + + + $AreaWidth = max(abs($X1),abs($X2)); + $AreaHeight = max(abs($Y1),abs($Y2)); + + return(array("Width"=>$AreaWidth,"Height"=>$AreaHeight)); + } + + /* Create the encoded string */ + function encode39($Value) + { + $this->Result = "100101101101"."0"; + $TextString = ""; + for($i=1;$i<=strlen($Value);$i++) + { + $CharCode = ord($this->mid($Value,$i,1)); + if ( $CharCode >= 97 && $CharCode <= 122 ) { $CharCode = $CharCode - 32; } + + if ( isset($this->Codes[chr($CharCode)]) ) + { + $this->Result = $this->Result.$this->Codes[chr($CharCode)]."0"; + $TextString = $TextString.chr($CharCode); + } + } + + if ( $this->MOD43 ) + { + $Checksum = $this->checksum($TextString); + $this->Result = $this->Result.$this->Codes[$Checksum]."0"; + } + + $this->Result = $this->Result."100101101101"; + $TextString = "*".$TextString."*"; + + return($TextString); + } + + /* Create the encoded string */ + function draw($Object,$Value,$X,$Y,$Format="") + { + $this->pChartObject = $Object; + + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Height = isset($Format["Height"]) ? $Format["Height"] : 30; + $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0; + $ShowLegend = isset($Format["ShowLegend"]) ? $Format["ShowLegend"] : FALSE; + $LegendOffset = isset($Format["LegendOffset"]) ? $Format["LegendOffset"] : 5; + $DrawArea = isset($Format["DrawArea"]) ? $Format["DrawArea"] : FALSE; + $AreaR = isset($Format["AreaR"]) ? $Format["AreaR"] : 255; + $AreaG = isset($Format["AreaG"]) ? $Format["AreaG"] : 255; + $AreaB = isset($Format["AreaB"]) ? $Format["AreaB"] : 255; + $AreaBorderR = isset($Format["AreaBorderR"]) ? $Format["AreaBorderR"] : $AreaR; + $AreaBorderG = isset($Format["AreaBorderG"]) ? $Format["AreaBorderG"] : $AreaG; + $AreaBorderB = isset($Format["AreaBorderB"]) ? $Format["AreaBorderB"] : $AreaB; + + $TextString = $this->encode39($Value); + + if ( $DrawArea ) + { + $X1 = $X + cos(($Angle-135) * PI / 180) * 10; + $Y1 = $Y + sin(($Angle-135) * PI / 180) * 10; + + $X2 = $X1 + cos($Angle * PI / 180) * (strlen($this->Result)+20); + $Y2 = $Y1 + sin($Angle * PI / 180) * (strlen($this->Result)+20); + + if ( $ShowLegend ) + { + $X3 = $X2 + cos(($Angle+90) * PI / 180) * ($Height+$LegendOffset+$this->pChartObject->FontSize+10); + $Y3 = $Y2 + sin(($Angle+90) * PI / 180) * ($Height+$LegendOffset+$this->pChartObject->FontSize+10); + } + else + { + $X3 = $X2 + cos(($Angle+90) * PI / 180) * ($Height+20); + $Y3 = $Y2 + sin(($Angle+90) * PI / 180) * ($Height+20); + } + + $X4 = $X3 + cos(($Angle+180) * PI / 180) * (strlen($this->Result)+20); + $Y4 = $Y3 + sin(($Angle+180) * PI / 180) * (strlen($this->Result)+20); + + $Polygon = array($X1,$Y1,$X2,$Y2,$X3,$Y3,$X4,$Y4); + $Settings = array("R"=>$AreaR,"G"=>$AreaG,"B"=>$AreaB,"BorderR"=>$AreaBorderR,"BorderG"=>$AreaBorderG,"BorderB"=>$AreaBorderB); + $this->pChartObject->drawPolygon($Polygon,$Settings); + } + + for($i=1;$i<=strlen($this->Result);$i++) + { + if ( $this->mid($this->Result,$i,1) == 1 ) + { + $X1 = $X + cos($Angle * PI / 180) * $i; + $Y1 = $Y + sin($Angle * PI / 180) * $i; + $X2 = $X1 + cos(($Angle+90) * PI / 180) * $Height; + $Y2 = $Y1 + sin(($Angle+90) * PI / 180) * $Height; + + $Settings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); + $this->pChartObject->drawLine($X1,$Y1,$X2,$Y2,$Settings); + } + } + + if ( $ShowLegend ) + { + $X1 = $X + cos($Angle * PI / 180) * (strlen($this->Result)/2); + $Y1 = $Y + sin($Angle * PI / 180) * (strlen($this->Result)/2); + + $LegendX = $X1 + cos(($Angle+90) * PI / 180) * ($Height+$LegendOffset); + $LegendY = $Y1 + sin(($Angle+90) * PI / 180) * ($Height+$LegendOffset); + + $Settings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Angle"=>-$Angle,"Align"=>TEXT_ALIGN_TOPMIDDLE); + $this->pChartObject->drawText($LegendX,$LegendY,$TextString,$Settings); + } + } + + function checksum( $string ) + { + $checksum = 0; + $length = strlen( $string ); + $charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%'; + + for( $i=0; $i < $length; ++$i ) + $checksum += strpos( $charset, $string[$i] ); + + return substr( $charset, ($checksum % 43), 1 ); + } + + function left($value,$NbChar) { return substr($value,0,$NbChar); } + function right($value,$NbChar) { return substr($value,strlen($value)-$NbChar,$NbChar); } + function mid($value,$Depart,$NbChar) { return substr($value,$Depart-1,$NbChar); } + } ?> \ No newline at end of file diff --git a/www/libs/pChart2.1.3/class/pBubble.class.php b/www/libs/pChart2.1.4/class/pBubble.class.php similarity index 97% rename from www/libs/pChart2.1.3/class/pBubble.class.php rename to www/libs/pChart2.1.4/class/pBubble.class.php index dbe123f0..06aaca0e 100644 --- a/www/libs/pChart2.1.3/class/pBubble.class.php +++ b/www/libs/pChart2.1.4/class/pBubble.class.php @@ -1,326 +1,326 @@ -pChartObject = $pChartObject; - $this->pDataObject = $pDataObject; - } - - /* Prepare the scale */ - function bubbleScale($DataSeries,$WeightSeries) - { - if ( !is_array($DataSeries) ) { $DataSeries = array($DataSeries); } - if ( !is_array($WeightSeries) ) { $WeightSeries = array($WeightSeries); } - - /* Parse each data series to find the new min & max boundaries to scale */ - $NewPositiveSerie = ""; $NewNegativeSerie = ""; $MaxValues = 0; $LastPositive = 0; $LastNegative = 0; - foreach($DataSeries as $Key => $SerieName) - { - $SerieWeightName = $WeightSeries[$Key]; - - $this->pDataObject->setSerieDrawable($SerieWeightName,FALSE); - - if ( count($this->pDataObject->Data["Series"][$SerieName]["Data"]) > $MaxValues ) { $MaxValues = count($this->pDataObject->Data["Series"][$SerieName]["Data"]); } - - foreach($this->pDataObject->Data["Series"][$SerieName]["Data"] as $Key => $Value) - { - if ( $Value >= 0 ) - { - $BubbleBounds = $Value + $this->pDataObject->Data["Series"][$SerieWeightName]["Data"][$Key]; - - if ( !isset($NewPositiveSerie[$Key]) ) - { $NewPositiveSerie[$Key] = $BubbleBounds; } - elseif ( $NewPositiveSerie[$Key] < $BubbleBounds ) - { $NewPositiveSerie[$Key] = $BubbleBounds; } - - $LastPositive = $BubbleBounds; - } - else - { - $BubbleBounds = $Value - $this->pDataObject->Data["Series"][$SerieWeightName]["Data"][$Key]; - - if ( !isset($NewNegativeSerie[$Key]) ) - { $NewNegativeSerie[$Key] = $BubbleBounds; } - elseif ( $NewNegativeSerie[$Key] > $BubbleBounds ) - { $NewNegativeSerie[$Key] = $BubbleBounds; } - - $LastNegative = $BubbleBounds; - } - } - } - - /* Check for missing values and all the fake positive serie */ - if ( $NewPositiveSerie != "" ) - { - for ($i=0; $i<$MaxValues; $i++) { if (!isset($NewPositiveSerie[$i])) { $NewPositiveSerie[$i] = $LastPositive; } } - - $this->pDataObject->addPoints($NewPositiveSerie,"BubbleFakePositiveSerie"); - } - - /* Check for missing values and all the fake negative serie */ - if ( $NewNegativeSerie != "" ) - { - for ($i=0; $i<$MaxValues; $i++) { if (!isset($NewNegativeSerie[$i])) { $NewNegativeSerie[$i] = $LastNegative; } } - - $this->pDataObject->addPoints($NewNegativeSerie,"BubbleFakeNegativeSerie"); - } - } - - function resetSeriesColors() - { - $Data = $this->pDataObject->getData(); - $Palette = $this->pDataObject->getPalette(); - - $ID = 0; - foreach($Data["Series"] as $SerieName => $SeriesParameters) - { - if ( $SeriesParameters["isDrawable"] ) - { - $this->pDataObject->Data["Series"][$SerieName]["Color"]["R"] = $Palette[$ID]["R"]; - $this->pDataObject->Data["Series"][$SerieName]["Color"]["G"] = $Palette[$ID]["G"]; - $this->pDataObject->Data["Series"][$SerieName]["Color"]["B"] = $Palette[$ID]["B"]; - $this->pDataObject->Data["Series"][$SerieName]["Color"]["Alpha"] = $Palette[$ID]["Alpha"]; - $ID++; - } - } - } - - /* Prepare the scale */ - function drawBubbleChart($DataSeries,$WeightSeries,$Format="") - { - $ForceAlpha = isset($Format["ForceAlpha"]) ? $Format["ForceAlpha"] : VOID; - $DrawBorder = isset($Format["DrawBorder"]) ? $Format["DrawBorder"] : TRUE; - $BorderWidth = isset($Format["BorderWidth"]) ? $Format["BorderWidth"] : 1; - $Shape = isset($Format["Shape"]) ? $Format["Shape"] : BUBBLE_SHAPE_ROUND; - $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 0; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 0; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 0; - $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 30; - $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; - - if ( !is_array($DataSeries) ) { $DataSeries = array($DataSeries); } - if ( !is_array($WeightSeries) ) { $WeightSeries = array($WeightSeries); } - - $Data = $this->pDataObject->getData(); - $Palette = $this->pDataObject->getPalette(); - - if ( isset($Data["Series"]["BubbleFakePositiveSerie"] ) ) { $this->pDataObject->setSerieDrawable("BubbleFakePositiveSerie",FALSE); } - if ( isset($Data["Series"]["BubbleFakeNegativeSerie"] ) ) { $this->pDataObject->setSerieDrawable("BubbleFakeNegativeSerie",FALSE); } - - $this->resetSeriesColors(); - - list($XMargin,$XDivs) = $this->pChartObject->scaleGetXSettings(); - - foreach($DataSeries as $Key => $SerieName) - { - $AxisID = $Data["Series"][$SerieName]["Axis"]; - $Mode = $Data["Axis"][$AxisID]["Display"]; - $Format = $Data["Axis"][$AxisID]["Format"]; - $Unit = $Data["Axis"][$AxisID]["Unit"]; - - if (isset($Data["Series"][$SerieName]["Description"])) { $SerieDescription = $Data["Series"][$SerieName]["Description"]; } else { $SerieDescription = $SerieName; } - - $XStep = ($this->pChartObject->GraphAreaX2-$this->pChartObject->GraphAreaX1-$XMargin*2)/$XDivs; - - $X = $this->pChartObject->GraphAreaX1 + $XMargin; - $Y = $this->pChartObject->GraphAreaY1 + $XMargin; - - $Color = array("R"=>$Palette[$Key]["R"],"G"=>$Palette[$Key]["G"],"B"=>$Palette[$Key]["B"],"Alpha"=>$Palette[$Key]["Alpha"]); - - if ( $ForceAlpha != VOID ) { $Color["Alpha"]=$ForceAlpha; } - - if ( $DrawBorder ) - { - if ( $BorderWidth != 1 ) - { - if ( $Surrounding != NULL ) - { $BorderR = $Palette[$Key]["R"]+$Surrounding; $BorderG = $Palette[$Key]["G"]+$Surrounding; $BorderB = $Palette[$Key]["B"]+$Surrounding; } - else - { $BorderR = $BorderR; $BorderG = $BorderG; $BorderB = $BorderB; } - if ( $ForceAlpha != VOID ) { $BorderAlpha = $ForceAlpha/2; } - $BorderColor = array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha); - } - else - { - $Color["BorderAlpha"] = $BorderAlpha; - - if ( $Surrounding != NULL ) - { $Color["BorderR"] = $Palette[$Key]["R"]+$Surrounding; $Color["BorderG"] = $Palette[$Key]["G"]+$Surrounding; $Color["BorderB"] = $Palette[$Key]["B"]+$Surrounding; } - else - { $Color["BorderR"] = $BorderR; $Color["BorderG"] = $BorderG; $Color["BorderB"] = $BorderB; } - if ( $ForceAlpha != VOID ) { $Color["BorderAlpha"] = $ForceAlpha/2; } - } - } - - foreach($Data["Series"][$SerieName]["Data"] as $iKey => $Point) - { - $Weight = $Point + $Data["Series"][$WeightSeries[$Key]]["Data"][$iKey]; - - $PosArray = $this->pChartObject->scaleComputeY($Point,array("AxisID"=>$AxisID)); - $WeightArray = $this->pChartObject->scaleComputeY($Weight,array("AxisID"=>$AxisID)); - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - if ( $XDivs == 0 ) { $XStep = 0; } else { $XStep = ($this->pChartObject->GraphAreaX2-$this->pChartObject->GraphAreaX1-$XMargin*2)/$XDivs; } - $Y = floor($PosArray); $CircleRadius = floor(abs($PosArray - $WeightArray)/2); - - if ( $Shape == BUBBLE_SHAPE_SQUARE ) - { - if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("RECT",floor($X-$CircleRadius).",".floor($Y-$CircleRadius).",".floor($X+$CircleRadius).",".floor($Y+$CircleRadius),$this->pChartObject->toHTMLColor($Palette[$Key]["R"],$Palette[$Key]["G"],$Palette[$Key]["B"]),$SerieDescription,$Data["Series"][$WeightSeries[$Key]]["Data"][$iKey]); } - - if ( $BorderWidth != 1 ) - { - $this->pChartObject->drawFilledRectangle($X-$CircleRadius-$BorderWidth,$Y-$CircleRadius-$BorderWidth,$X+$CircleRadius+$BorderWidth,$Y+$CircleRadius+$BorderWidth,$BorderColor); - $this->pChartObject->drawFilledRectangle($X-$CircleRadius,$Y-$CircleRadius,$X+$CircleRadius,$Y+$CircleRadius,$Color); - } - else - $this->pChartObject->drawFilledRectangle($X-$CircleRadius,$Y-$CircleRadius,$X+$CircleRadius,$Y+$CircleRadius,$Color); - } - elseif ( $Shape == BUBBLE_SHAPE_ROUND ) - { - if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".floor($CircleRadius),$this->pChartObject->toHTMLColor($Palette[$Key]["R"],$Palette[$Key]["G"],$Palette[$Key]["B"]),$SerieDescription,$Data["Series"][$WeightSeries[$Key]]["Data"][$iKey]); } - - if ( $BorderWidth != 1 ) - { - $this->pChartObject->drawFilledCircle($X,$Y,$CircleRadius+$BorderWidth,$BorderColor); - $this->pChartObject->drawFilledCircle($X,$Y,$CircleRadius,$Color); - } - else - $this->pChartObject->drawFilledCircle($X,$Y,$CircleRadius,$Color); - } - - $X = $X + $XStep; - } - elseif ( $Data["Orientation"] == SCALE_POS_TOPBOTTOM ) - { - if ( $XDivs == 0 ) { $XStep = 0; } else { $XStep = ($this->pChartObject->GraphAreaY2-$this->pChartObject->GraphAreaY1-$XMargin*2)/$XDivs; } - $X = floor($PosArray); $CircleRadius = floor(abs($PosArray - $WeightArray)/2); - - if ( $Shape == BUBBLE_SHAPE_SQUARE ) - { - if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("RECT",floor($X-$CircleRadius).",".floor($Y-$CircleRadius).",".floor($X+$CircleRadius).",".floor($Y+$CircleRadius),$this->pChartObject->toHTMLColor($Palette[$Key]["R"],$Palette[$Key]["G"],$Palette[$Key]["B"]),$SerieDescription,$Data["Series"][$WeightSeries[$Key]]["Data"][$iKey]); } - - if ( $BorderWidth != 1 ) - { - $this->pChartObject->drawFilledRectangle($X-$CircleRadius-$BorderWidth,$Y-$CircleRadius-$BorderWidth,$X+$CircleRadius+$BorderWidth,$Y+$CircleRadius+$BorderWidth,$BorderColor); - $this->pChartObject->drawFilledRectangle($X-$CircleRadius,$Y-$CircleRadius,$X+$CircleRadius,$Y+$CircleRadius,$Color); - } - else - $this->pChartObject->drawFilledRectangle($X-$CircleRadius,$Y-$CircleRadius,$X+$CircleRadius,$Y+$CircleRadius,$Color); - } - elseif ( $Shape == BUBBLE_SHAPE_ROUND ) - { - if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".floor($CircleRadius),$this->pChartObject->toHTMLColor($Palette[$Key]["R"],$Palette[$Key]["G"],$Palette[$Key]["B"]),$SerieDescription,$Data["Series"][$WeightSeries[$Key]]["Data"][$iKey]); } - - if ( $BorderWidth != 1 ) - { - $this->pChartObject->drawFilledCircle($X,$Y,$CircleRadius+$BorderWidth,$BorderColor); - $this->pChartObject->drawFilledCircle($X,$Y,$CircleRadius,$Color); - } - else - $this->pChartObject->drawFilledCircle($X,$Y,$CircleRadius,$Color); - } - - $Y = $Y + $XStep; - } - } - } - } - - function writeBubbleLabel($SerieName,$SerieWeightName,$Points,$Format="") - { - $OverrideTitle = isset($Format["OverrideTitle"]) ? $Format["OverrideTitle"] : NULL; - $DrawPoint = isset($Format["DrawPoint"]) ? $Format["DrawPoint"] : LABEL_POINT_BOX; - - if ( !is_array($Points) ) { $Point = $Points; $Points = ""; $Points[] = $Point; } - - $Data = $this->pDataObject->getData(); - $Palette = $this->pDataObject->getPalette(); - - if ( !isset($Data["Series"][$SerieName]) || !isset($Data["Series"][$SerieWeightName]) ) - return(0); - - list($XMargin,$XDivs) = $this->pChartObject->scaleGetXSettings(); - - $AxisID = $Data["Series"][$SerieName]["Axis"]; - $AxisMode = $Data["Axis"][$AxisID]["Display"]; - $AxisFormat = $Data["Axis"][$AxisID]["Format"]; - $AxisUnit = $Data["Axis"][$AxisID]["Unit"]; - $XStep = ($this->pChartObject->GraphAreaX2-$this->pChartObject->GraphAreaX1-$XMargin*2)/$XDivs; - - $X = $this->pChartObject->GraphAreaX1 + $XMargin; - $Y = $this->pChartObject->GraphAreaY1 + $XMargin; - - $Color = array("R"=>$Data["Series"][$SerieName]["Color"]["R"],"G"=>$Data["Series"][$SerieName]["Color"]["G"],"B"=>$Data["Series"][$SerieName]["Color"]["B"],"Alpha"=>$Data["Series"][$SerieName]["Color"]["Alpha"]); - - foreach($Points as $Key => $Point) - { - $Value = $Data["Series"][$SerieName]["Data"][$Point]; - $PosArray = $this->pChartObject->scaleComputeY($Value,array("AxisID"=>$AxisID)); - - if ( isset($Data["Abscissa"]) && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Point]) ) - $Abscissa = $Data["Series"][$Data["Abscissa"]]["Data"][$Point]." : "; - else - $Abscissa = ""; - - $Value = $this->pChartObject->scaleFormat($Value,$AxisMode,$AxisFormat,$AxisUnit); - $Weight = $Data["Series"][$SerieWeightName]["Data"][$Point]; - $Caption = $Abscissa.$Value." / ".$Weight; - - if ( isset($Data["Series"][$SerieName]["Description"]) ) - $Description = $Data["Series"][$SerieName]["Description"]; - else - $Description = "No description"; - - $Series = ""; - $Series[] = array("Format"=>$Color,"Caption"=>$Caption); - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - if ( $XDivs == 0 ) { $XStep = 0; } else { $XStep = ($this->pChartObject->GraphAreaX2-$this->pChartObject->GraphAreaX1-$XMargin*2)/$XDivs; } - - $X = floor($X + $Point * $XStep); - $Y = floor($PosArray); - } - else - { - if ( $XDivs == 0 ) { $YStep = 0; } else { $YStep = ($this->pChartObject->GraphAreaY2-$this->pChartObject->GraphAreaY1-$XMargin*2)/$XDivs; } - - $X = floor($PosArray); - $Y = floor($Y + $Point * $YStep); - } - - if ( $DrawPoint == LABEL_POINT_CIRCLE ) - $this->pChartObject->drawFilledCircle($X,$Y,3,array("R"=>255,"G"=>255,"B"=>255,"BorderR"=>0,"BorderG"=>0,"BorderB"=>0)); - elseif ( $DrawPoint == LABEL_POINT_BOX ) - $this->pChartObject->drawFilledRectangle($X-2,$Y-2,$X+2,$Y+2,array("R"=>255,"G"=>255,"B"=>255,"BorderR"=>0,"BorderG"=>0,"BorderB"=>0)); - - $this->pChartObject->drawLabelBox($X,$Y-3,$Description,$Series,$Format); - } - } - } +pChartObject = $pChartObject; + $this->pDataObject = $pDataObject; + } + + /* Prepare the scale */ + function bubbleScale($DataSeries,$WeightSeries) + { + if ( !is_array($DataSeries) ) { $DataSeries = array($DataSeries); } + if ( !is_array($WeightSeries) ) { $WeightSeries = array($WeightSeries); } + + /* Parse each data series to find the new min & max boundaries to scale */ + $NewPositiveSerie = ""; $NewNegativeSerie = ""; $MaxValues = 0; $LastPositive = 0; $LastNegative = 0; + foreach($DataSeries as $Key => $SerieName) + { + $SerieWeightName = $WeightSeries[$Key]; + + $this->pDataObject->setSerieDrawable($SerieWeightName,FALSE); + + if ( count($this->pDataObject->Data["Series"][$SerieName]["Data"]) > $MaxValues ) { $MaxValues = count($this->pDataObject->Data["Series"][$SerieName]["Data"]); } + + foreach($this->pDataObject->Data["Series"][$SerieName]["Data"] as $Key => $Value) + { + if ( $Value >= 0 ) + { + $BubbleBounds = $Value + $this->pDataObject->Data["Series"][$SerieWeightName]["Data"][$Key]; + + if ( !isset($NewPositiveSerie[$Key]) ) + { $NewPositiveSerie[$Key] = $BubbleBounds; } + elseif ( $NewPositiveSerie[$Key] < $BubbleBounds ) + { $NewPositiveSerie[$Key] = $BubbleBounds; } + + $LastPositive = $BubbleBounds; + } + else + { + $BubbleBounds = $Value - $this->pDataObject->Data["Series"][$SerieWeightName]["Data"][$Key]; + + if ( !isset($NewNegativeSerie[$Key]) ) + { $NewNegativeSerie[$Key] = $BubbleBounds; } + elseif ( $NewNegativeSerie[$Key] > $BubbleBounds ) + { $NewNegativeSerie[$Key] = $BubbleBounds; } + + $LastNegative = $BubbleBounds; + } + } + } + + /* Check for missing values and all the fake positive serie */ + if ( $NewPositiveSerie != "" ) + { + for ($i=0; $i<$MaxValues; $i++) { if (!isset($NewPositiveSerie[$i])) { $NewPositiveSerie[$i] = $LastPositive; } } + + $this->pDataObject->addPoints($NewPositiveSerie,"BubbleFakePositiveSerie"); + } + + /* Check for missing values and all the fake negative serie */ + if ( $NewNegativeSerie != "" ) + { + for ($i=0; $i<$MaxValues; $i++) { if (!isset($NewNegativeSerie[$i])) { $NewNegativeSerie[$i] = $LastNegative; } } + + $this->pDataObject->addPoints($NewNegativeSerie,"BubbleFakeNegativeSerie"); + } + } + + function resetSeriesColors() + { + $Data = $this->pDataObject->getData(); + $Palette = $this->pDataObject->getPalette(); + + $ID = 0; + foreach($Data["Series"] as $SerieName => $SeriesParameters) + { + if ( $SeriesParameters["isDrawable"] ) + { + $this->pDataObject->Data["Series"][$SerieName]["Color"]["R"] = $Palette[$ID]["R"]; + $this->pDataObject->Data["Series"][$SerieName]["Color"]["G"] = $Palette[$ID]["G"]; + $this->pDataObject->Data["Series"][$SerieName]["Color"]["B"] = $Palette[$ID]["B"]; + $this->pDataObject->Data["Series"][$SerieName]["Color"]["Alpha"] = $Palette[$ID]["Alpha"]; + $ID++; + } + } + } + + /* Prepare the scale */ + function drawBubbleChart($DataSeries,$WeightSeries,$Format="") + { + $ForceAlpha = isset($Format["ForceAlpha"]) ? $Format["ForceAlpha"] : VOID; + $DrawBorder = isset($Format["DrawBorder"]) ? $Format["DrawBorder"] : TRUE; + $BorderWidth = isset($Format["BorderWidth"]) ? $Format["BorderWidth"] : 1; + $Shape = isset($Format["Shape"]) ? $Format["Shape"] : BUBBLE_SHAPE_ROUND; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 0; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 0; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 0; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 30; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; + + if ( !is_array($DataSeries) ) { $DataSeries = array($DataSeries); } + if ( !is_array($WeightSeries) ) { $WeightSeries = array($WeightSeries); } + + $Data = $this->pDataObject->getData(); + $Palette = $this->pDataObject->getPalette(); + + if ( isset($Data["Series"]["BubbleFakePositiveSerie"] ) ) { $this->pDataObject->setSerieDrawable("BubbleFakePositiveSerie",FALSE); } + if ( isset($Data["Series"]["BubbleFakeNegativeSerie"] ) ) { $this->pDataObject->setSerieDrawable("BubbleFakeNegativeSerie",FALSE); } + + $this->resetSeriesColors(); + + list($XMargin,$XDivs) = $this->pChartObject->scaleGetXSettings(); + + foreach($DataSeries as $Key => $SerieName) + { + $AxisID = $Data["Series"][$SerieName]["Axis"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + if (isset($Data["Series"][$SerieName]["Description"])) { $SerieDescription = $Data["Series"][$SerieName]["Description"]; } else { $SerieDescription = $SerieName; } + + $XStep = ($this->pChartObject->GraphAreaX2-$this->pChartObject->GraphAreaX1-$XMargin*2)/$XDivs; + + $X = $this->pChartObject->GraphAreaX1 + $XMargin; + $Y = $this->pChartObject->GraphAreaY1 + $XMargin; + + $Color = array("R"=>$Palette[$Key]["R"],"G"=>$Palette[$Key]["G"],"B"=>$Palette[$Key]["B"],"Alpha"=>$Palette[$Key]["Alpha"]); + + if ( $ForceAlpha != VOID ) { $Color["Alpha"]=$ForceAlpha; } + + if ( $DrawBorder ) + { + if ( $BorderWidth != 1 ) + { + if ( $Surrounding != NULL ) + { $BorderR = $Palette[$Key]["R"]+$Surrounding; $BorderG = $Palette[$Key]["G"]+$Surrounding; $BorderB = $Palette[$Key]["B"]+$Surrounding; } + else + { $BorderR = $BorderR; $BorderG = $BorderG; $BorderB = $BorderB; } + if ( $ForceAlpha != VOID ) { $BorderAlpha = $ForceAlpha/2; } + $BorderColor = array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha); + } + else + { + $Color["BorderAlpha"] = $BorderAlpha; + + if ( $Surrounding != NULL ) + { $Color["BorderR"] = $Palette[$Key]["R"]+$Surrounding; $Color["BorderG"] = $Palette[$Key]["G"]+$Surrounding; $Color["BorderB"] = $Palette[$Key]["B"]+$Surrounding; } + else + { $Color["BorderR"] = $BorderR; $Color["BorderG"] = $BorderG; $Color["BorderB"] = $BorderB; } + if ( $ForceAlpha != VOID ) { $Color["BorderAlpha"] = $ForceAlpha/2; } + } + } + + foreach($Data["Series"][$SerieName]["Data"] as $iKey => $Point) + { + $Weight = $Point + $Data["Series"][$WeightSeries[$Key]]["Data"][$iKey]; + + $PosArray = $this->pChartObject->scaleComputeY($Point,array("AxisID"=>$AxisID)); + $WeightArray = $this->pChartObject->scaleComputeY($Weight,array("AxisID"=>$AxisID)); + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + if ( $XDivs == 0 ) { $XStep = 0; } else { $XStep = ($this->pChartObject->GraphAreaX2-$this->pChartObject->GraphAreaX1-$XMargin*2)/$XDivs; } + $Y = floor($PosArray); $CircleRadius = floor(abs($PosArray - $WeightArray)/2); + + if ( $Shape == BUBBLE_SHAPE_SQUARE ) + { + if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("RECT",floor($X-$CircleRadius).",".floor($Y-$CircleRadius).",".floor($X+$CircleRadius).",".floor($Y+$CircleRadius),$this->pChartObject->toHTMLColor($Palette[$Key]["R"],$Palette[$Key]["G"],$Palette[$Key]["B"]),$SerieDescription,$Data["Series"][$WeightSeries[$Key]]["Data"][$iKey]); } + + if ( $BorderWidth != 1 ) + { + $this->pChartObject->drawFilledRectangle($X-$CircleRadius-$BorderWidth,$Y-$CircleRadius-$BorderWidth,$X+$CircleRadius+$BorderWidth,$Y+$CircleRadius+$BorderWidth,$BorderColor); + $this->pChartObject->drawFilledRectangle($X-$CircleRadius,$Y-$CircleRadius,$X+$CircleRadius,$Y+$CircleRadius,$Color); + } + else + $this->pChartObject->drawFilledRectangle($X-$CircleRadius,$Y-$CircleRadius,$X+$CircleRadius,$Y+$CircleRadius,$Color); + } + elseif ( $Shape == BUBBLE_SHAPE_ROUND ) + { + if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".floor($CircleRadius),$this->pChartObject->toHTMLColor($Palette[$Key]["R"],$Palette[$Key]["G"],$Palette[$Key]["B"]),$SerieDescription,$Data["Series"][$WeightSeries[$Key]]["Data"][$iKey]); } + + if ( $BorderWidth != 1 ) + { + $this->pChartObject->drawFilledCircle($X,$Y,$CircleRadius+$BorderWidth,$BorderColor); + $this->pChartObject->drawFilledCircle($X,$Y,$CircleRadius,$Color); + } + else + $this->pChartObject->drawFilledCircle($X,$Y,$CircleRadius,$Color); + } + + $X = $X + $XStep; + } + elseif ( $Data["Orientation"] == SCALE_POS_TOPBOTTOM ) + { + if ( $XDivs == 0 ) { $XStep = 0; } else { $XStep = ($this->pChartObject->GraphAreaY2-$this->pChartObject->GraphAreaY1-$XMargin*2)/$XDivs; } + $X = floor($PosArray); $CircleRadius = floor(abs($PosArray - $WeightArray)/2); + + if ( $Shape == BUBBLE_SHAPE_SQUARE ) + { + if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("RECT",floor($X-$CircleRadius).",".floor($Y-$CircleRadius).",".floor($X+$CircleRadius).",".floor($Y+$CircleRadius),$this->pChartObject->toHTMLColor($Palette[$Key]["R"],$Palette[$Key]["G"],$Palette[$Key]["B"]),$SerieDescription,$Data["Series"][$WeightSeries[$Key]]["Data"][$iKey]); } + + if ( $BorderWidth != 1 ) + { + $this->pChartObject->drawFilledRectangle($X-$CircleRadius-$BorderWidth,$Y-$CircleRadius-$BorderWidth,$X+$CircleRadius+$BorderWidth,$Y+$CircleRadius+$BorderWidth,$BorderColor); + $this->pChartObject->drawFilledRectangle($X-$CircleRadius,$Y-$CircleRadius,$X+$CircleRadius,$Y+$CircleRadius,$Color); + } + else + $this->pChartObject->drawFilledRectangle($X-$CircleRadius,$Y-$CircleRadius,$X+$CircleRadius,$Y+$CircleRadius,$Color); + } + elseif ( $Shape == BUBBLE_SHAPE_ROUND ) + { + if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".floor($CircleRadius),$this->pChartObject->toHTMLColor($Palette[$Key]["R"],$Palette[$Key]["G"],$Palette[$Key]["B"]),$SerieDescription,$Data["Series"][$WeightSeries[$Key]]["Data"][$iKey]); } + + if ( $BorderWidth != 1 ) + { + $this->pChartObject->drawFilledCircle($X,$Y,$CircleRadius+$BorderWidth,$BorderColor); + $this->pChartObject->drawFilledCircle($X,$Y,$CircleRadius,$Color); + } + else + $this->pChartObject->drawFilledCircle($X,$Y,$CircleRadius,$Color); + } + + $Y = $Y + $XStep; + } + } + } + } + + function writeBubbleLabel($SerieName,$SerieWeightName,$Points,$Format="") + { + $OverrideTitle = isset($Format["OverrideTitle"]) ? $Format["OverrideTitle"] : NULL; + $DrawPoint = isset($Format["DrawPoint"]) ? $Format["DrawPoint"] : LABEL_POINT_BOX; + + if ( !is_array($Points) ) { $Point = $Points; $Points = ""; $Points[] = $Point; } + + $Data = $this->pDataObject->getData(); + $Palette = $this->pDataObject->getPalette(); + + if ( !isset($Data["Series"][$SerieName]) || !isset($Data["Series"][$SerieWeightName]) ) + return(0); + + list($XMargin,$XDivs) = $this->pChartObject->scaleGetXSettings(); + + $AxisID = $Data["Series"][$SerieName]["Axis"]; + $AxisMode = $Data["Axis"][$AxisID]["Display"]; + $AxisFormat = $Data["Axis"][$AxisID]["Format"]; + $AxisUnit = $Data["Axis"][$AxisID]["Unit"]; + $XStep = ($this->pChartObject->GraphAreaX2-$this->pChartObject->GraphAreaX1-$XMargin*2)/$XDivs; + + $X = $this->pChartObject->GraphAreaX1 + $XMargin; + $Y = $this->pChartObject->GraphAreaY1 + $XMargin; + + $Color = array("R"=>$Data["Series"][$SerieName]["Color"]["R"],"G"=>$Data["Series"][$SerieName]["Color"]["G"],"B"=>$Data["Series"][$SerieName]["Color"]["B"],"Alpha"=>$Data["Series"][$SerieName]["Color"]["Alpha"]); + + foreach($Points as $Key => $Point) + { + $Value = $Data["Series"][$SerieName]["Data"][$Point]; + $PosArray = $this->pChartObject->scaleComputeY($Value,array("AxisID"=>$AxisID)); + + if ( isset($Data["Abscissa"]) && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Point]) ) + $Abscissa = $Data["Series"][$Data["Abscissa"]]["Data"][$Point]." : "; + else + $Abscissa = ""; + + $Value = $this->pChartObject->scaleFormat($Value,$AxisMode,$AxisFormat,$AxisUnit); + $Weight = $Data["Series"][$SerieWeightName]["Data"][$Point]; + $Caption = $Abscissa.$Value." / ".$Weight; + + if ( isset($Data["Series"][$SerieName]["Description"]) ) + $Description = $Data["Series"][$SerieName]["Description"]; + else + $Description = "No description"; + + $Series = ""; + $Series[] = array("Format"=>$Color,"Caption"=>$Caption); + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + if ( $XDivs == 0 ) { $XStep = 0; } else { $XStep = ($this->pChartObject->GraphAreaX2-$this->pChartObject->GraphAreaX1-$XMargin*2)/$XDivs; } + + $X = floor($X + $Point * $XStep); + $Y = floor($PosArray); + } + else + { + if ( $XDivs == 0 ) { $YStep = 0; } else { $YStep = ($this->pChartObject->GraphAreaY2-$this->pChartObject->GraphAreaY1-$XMargin*2)/$XDivs; } + + $X = floor($PosArray); + $Y = floor($Y + $Point * $YStep); + } + + if ( $DrawPoint == LABEL_POINT_CIRCLE ) + $this->pChartObject->drawFilledCircle($X,$Y,3,array("R"=>255,"G"=>255,"B"=>255,"BorderR"=>0,"BorderG"=>0,"BorderB"=>0)); + elseif ( $DrawPoint == LABEL_POINT_BOX ) + $this->pChartObject->drawFilledRectangle($X-2,$Y-2,$X+2,$Y+2,array("R"=>255,"G"=>255,"B"=>255,"BorderR"=>0,"BorderG"=>0,"BorderB"=>0)); + + $this->pChartObject->drawLabelBox($X,$Y-3,$Description,$Series,$Format); + } + } + } ?> \ No newline at end of file diff --git a/www/libs/pChart2.1.3/class/pCache.class.php b/www/libs/pChart2.1.4/class/pCache.class.php similarity index 96% rename from www/libs/pChart2.1.3/class/pCache.class.php rename to www/libs/pChart2.1.4/class/pCache.class.php index 4e4a4504..722e51bc 100644 --- a/www/libs/pChart2.1.3/class/pCache.class.php +++ b/www/libs/pChart2.1.4/class/pCache.class.php @@ -1,280 +1,280 @@ -CacheFolder = $CacheFolder; - $this->CacheIndex = $CacheIndex; - $this->CacheDB = $CacheDB; - - if (!file_exists($this->CacheFolder."/".$this->CacheIndex)) { touch($this->CacheFolder."/".$this->CacheIndex); } - if (!file_exists($this->CacheFolder."/".$this->CacheDB)) { touch($this->CacheFolder."/".$this->CacheDB); } - } - - /* Flush the cache contents */ - function flush() - { - if (file_exists($this->CacheFolder."/".$this->CacheIndex)) { unlink($this->CacheFolder."/".$this->CacheIndex); touch($this->CacheFolder."/".$this->CacheIndex); } - if (file_exists($this->CacheFolder."/".$this->CacheDB)) { unlink($this->CacheFolder."/".$this->CacheDB); touch($this->CacheFolder."/".$this->CacheDB); } - } - - /* Return the MD5 of the data array to clearly identify the chart */ - function getHash($Data,$Marker="") - { return(md5($Marker.serialize($Data->Data))); } - - /* Write the generated picture to the cache */ - function writeToCache($ID,$pChartObject) - { - /* Compute the paths */ - $TemporaryFile = $this->CacheFolder."/tmp_".rand(0,1000).".png"; - $Database = $this->CacheFolder."/".$this->CacheDB; - $Index = $this->CacheFolder."/".$this->CacheIndex; - - /* Flush the picture to a temporary file */ - imagepng($pChartObject->Picture ,$TemporaryFile); - - /* Retrieve the files size */ - $PictureSize = filesize($TemporaryFile); - $DBSize = filesize($Database); - - /* Save the index */ - $Handle = fopen($Index,"a"); - fwrite($Handle, $ID.",".$DBSize.",".$PictureSize.",".time().",0 \r\n"); - fclose($Handle); - - /* Get the picture raw contents */ - $Handle = fopen($TemporaryFile,"r"); - $Raw = fread($Handle,$PictureSize); - fclose($Handle); - - /* Save the picture in the solid database file */ - $Handle = fopen($Database,"a"); - fwrite($Handle, $Raw); - fclose($Handle); - - /* Remove temporary file */ - unlink($TemporaryFile); - } - - /* Remove object older than the specified TS */ - function removeOlderThan($Expiry) - { $this->dbRemoval(array("Expiry"=>$Expiry)); } - - /* Remove an object from the cache */ - function remove($ID) - { $this->dbRemoval(array("Name"=>$ID)); } - - /* Remove with specified criterias */ - function dbRemoval($Settings) - { - $ID = isset($Settings["Name"]) ? $Settings["Name"] : NULL; - $Expiry = isset($Settings["Expiry"]) ? $Settings["Expiry"] : -(24*60*60); - $TS = time()-$Expiry; - - /* Compute the paths */ - $Database = $this->CacheFolder."/".$this->CacheDB; - $Index = $this->CacheFolder."/".$this->CacheIndex; - $DatabaseTemp = $this->CacheFolder."/".$this->CacheDB.".tmp"; - $IndexTemp = $this->CacheFolder."/".$this->CacheIndex.".tmp"; - - /* Single file removal */ - if ( $ID != NULL ) - { - /* Retrieve object informations */ - $Object = $this->isInCache($ID,TRUE); - - /* If it's not in the cache DB, go away */ - if ( !$Object ) { return(0); } - } - - /* Create the temporary files */ - if (!file_exists($DatabaseTemp)) { touch($DatabaseTemp); } - if (!file_exists($IndexTemp)) { touch($IndexTemp); } - - /* Open the file handles */ - $IndexHandle = @fopen($Index, "r"); - $IndexTempHandle = @fopen($IndexTemp, "w"); - $DBHandle = @fopen($Database, "r"); - $DBTempHandle = @fopen($DatabaseTemp, "w"); - - /* Remove the selected ID from the database */ - while (!feof($IndexHandle)) - { - $Entry = fgets($IndexHandle, 4096); - $Entry = str_replace("\r","",$Entry); - $Entry = str_replace("\n","",$Entry); - $Settings = preg_split("/,/",$Entry); - - if ( $Entry != "" ) - { - $PicID = $Settings[0]; - $DBPos = $Settings[1]; - $PicSize = $Settings[2]; - $GeneratedTS = $Settings[3]; - $Hits = $Settings[4]; - - if ( $Settings[0] != $ID && $GeneratedTS > $TS) - { - $CurrentPos = ftell($DBTempHandle); - fwrite($IndexTempHandle, $PicID.",".$CurrentPos.",".$PicSize.",".$GeneratedTS.",".$Hits."\r\n"); - - fseek($DBHandle,$DBPos); - $Picture = fread($DBHandle,$PicSize); - fwrite($DBTempHandle,$Picture); - } - } - } - - /* Close the handles */ - fclose($IndexHandle); - fclose($IndexTempHandle); - fclose($DBHandle); - fclose($DBTempHandle); - - /* Remove the prod files */ - unlink($Database); - unlink($Index); - - /* Swap the temp & prod DB */ - rename($DatabaseTemp,$Database); - rename($IndexTemp,$Index); - } - - function isInCache($ID,$Verbose=FALSE,$UpdateHitsCount=FALSE) - { - /* Compute the paths */ - $Index = $this->CacheFolder."/".$this->CacheIndex; - - /* Search the picture in the index file */ - $Handle = @fopen($Index, "r"); - while (!feof($Handle)) - { - $IndexPos = ftell($Handle); - $Entry = fgets($Handle, 4096); - if ( $Entry != "" ) - { - $Settings = preg_split("/,/",$Entry); - $PicID = $Settings[0]; - if ( $PicID == $ID ) - { - fclose($Handle); - - $DBPos = $Settings[1]; - $PicSize = $Settings[2]; - $GeneratedTS = $Settings[3]; - $Hits = intval($Settings[4]); - - if ( $UpdateHitsCount ) - { - $Hits++; - if ( strlen($Hits) < 7 ) { $Hits = $Hits.str_repeat(" ",7-strlen($Hits)); } - - $Handle = @fopen($Index, "r+"); - fseek($Handle,$IndexPos); - fwrite($Handle, $PicID.",".$DBPos.",".$PicSize.",".$GeneratedTS.",".$Hits."\r\n"); - fclose($Handle); - } - - if ($Verbose) - { return(array("DBPos"=>$DBPos,"PicSize"=>$PicSize,"GeneratedTS"=>$GeneratedTS,"Hits"=>$Hits)); } - else - { return(TRUE); } - } - } - } - fclose($Handle); - - /* Picture isn't in the cache */ - return(FALSE); - } - - /* Automatic output method based on the calling interface */ - function autoOutput($ID,$Destination="output.png") - { - if (php_sapi_name() == "cli") - $this->saveFromCache($ID,$Destination); - else - $this->strokeFromCache($ID); - } - - function strokeFromCache($ID) - { - /* Get the raw picture from the cache */ - $Picture = $this->getFromCache($ID); - - /* Do we have a hit? */ - if ( $Picture == NULL ) { return(FALSE); } - - header('Content-type: image/png'); - echo $Picture; - - return(TRUE); - } - - function saveFromCache($ID,$Destination) - { - /* Get the raw picture from the cache */ - $Picture = $this->getFromCache($ID); - - /* Do we have a hit? */ - if ( $Picture == NULL ) { return(FALSE); } - - /* Flush the picture to a file */ - $Handle = fopen($Destination,"w"); - fwrite($Handle,$Picture); - fclose($Handle); - - /* All went fine */ - return(TRUE); - } - - function getFromCache($ID) - { - /* Compute the path */ - $Database = $this->CacheFolder."/".$this->CacheDB; - - /* Lookup for the picture in the cache */ - $CacheInfo = $this->isInCache($ID,TRUE,TRUE); - - /* Not in the cache */ - if (!$CacheInfo) { return(NULL); } - - /* Get the database extended information */ - $DBPos = $CacheInfo["DBPos"]; - $PicSize = $CacheInfo["PicSize"]; - - /* Extract the picture from the solid cache file */ - $Handle = @fopen($Database, "r"); - fseek($Handle,$DBPos); - $Picture = fread($Handle,$PicSize); - fclose($Handle); - - /* Return back the raw picture data */ - return($Picture); - } - } +CacheFolder = $CacheFolder; + $this->CacheIndex = $CacheIndex; + $this->CacheDB = $CacheDB; + + if (!file_exists($this->CacheFolder."/".$this->CacheIndex)) { touch($this->CacheFolder."/".$this->CacheIndex); } + if (!file_exists($this->CacheFolder."/".$this->CacheDB)) { touch($this->CacheFolder."/".$this->CacheDB); } + } + + /* Flush the cache contents */ + function flush() + { + if (file_exists($this->CacheFolder."/".$this->CacheIndex)) { unlink($this->CacheFolder."/".$this->CacheIndex); touch($this->CacheFolder."/".$this->CacheIndex); } + if (file_exists($this->CacheFolder."/".$this->CacheDB)) { unlink($this->CacheFolder."/".$this->CacheDB); touch($this->CacheFolder."/".$this->CacheDB); } + } + + /* Return the MD5 of the data array to clearly identify the chart */ + function getHash($Data,$Marker="") + { return(md5($Marker.serialize($Data->Data))); } + + /* Write the generated picture to the cache */ + function writeToCache($ID,$pChartObject) + { + /* Compute the paths */ + $TemporaryFile = $this->CacheFolder."/tmp_".rand(0,1000).".png"; + $Database = $this->CacheFolder."/".$this->CacheDB; + $Index = $this->CacheFolder."/".$this->CacheIndex; + + /* Flush the picture to a temporary file */ + imagepng($pChartObject->Picture ,$TemporaryFile); + + /* Retrieve the files size */ + $PictureSize = filesize($TemporaryFile); + $DBSize = filesize($Database); + + /* Save the index */ + $Handle = fopen($Index,"a"); + fwrite($Handle, $ID.",".$DBSize.",".$PictureSize.",".time().",0 \r\n"); + fclose($Handle); + + /* Get the picture raw contents */ + $Handle = fopen($TemporaryFile,"r"); + $Raw = fread($Handle,$PictureSize); + fclose($Handle); + + /* Save the picture in the solid database file */ + $Handle = fopen($Database,"a"); + fwrite($Handle, $Raw); + fclose($Handle); + + /* Remove temporary file */ + unlink($TemporaryFile); + } + + /* Remove object older than the specified TS */ + function removeOlderThan($Expiry) + { $this->dbRemoval(array("Expiry"=>$Expiry)); } + + /* Remove an object from the cache */ + function remove($ID) + { $this->dbRemoval(array("Name"=>$ID)); } + + /* Remove with specified criterias */ + function dbRemoval($Settings) + { + $ID = isset($Settings["Name"]) ? $Settings["Name"] : NULL; + $Expiry = isset($Settings["Expiry"]) ? $Settings["Expiry"] : -(24*60*60); + $TS = time()-$Expiry; + + /* Compute the paths */ + $Database = $this->CacheFolder."/".$this->CacheDB; + $Index = $this->CacheFolder."/".$this->CacheIndex; + $DatabaseTemp = $this->CacheFolder."/".$this->CacheDB.".tmp"; + $IndexTemp = $this->CacheFolder."/".$this->CacheIndex.".tmp"; + + /* Single file removal */ + if ( $ID != NULL ) + { + /* Retrieve object informations */ + $Object = $this->isInCache($ID,TRUE); + + /* If it's not in the cache DB, go away */ + if ( !$Object ) { return(0); } + } + + /* Create the temporary files */ + if (!file_exists($DatabaseTemp)) { touch($DatabaseTemp); } + if (!file_exists($IndexTemp)) { touch($IndexTemp); } + + /* Open the file handles */ + $IndexHandle = @fopen($Index, "r"); + $IndexTempHandle = @fopen($IndexTemp, "w"); + $DBHandle = @fopen($Database, "r"); + $DBTempHandle = @fopen($DatabaseTemp, "w"); + + /* Remove the selected ID from the database */ + while (!feof($IndexHandle)) + { + $Entry = fgets($IndexHandle, 4096); + $Entry = str_replace("\r","",$Entry); + $Entry = str_replace("\n","",$Entry); + $Settings = preg_split("/,/",$Entry); + + if ( $Entry != "" ) + { + $PicID = $Settings[0]; + $DBPos = $Settings[1]; + $PicSize = $Settings[2]; + $GeneratedTS = $Settings[3]; + $Hits = $Settings[4]; + + if ( $Settings[0] != $ID && $GeneratedTS > $TS) + { + $CurrentPos = ftell($DBTempHandle); + fwrite($IndexTempHandle, $PicID.",".$CurrentPos.",".$PicSize.",".$GeneratedTS.",".$Hits."\r\n"); + + fseek($DBHandle,$DBPos); + $Picture = fread($DBHandle,$PicSize); + fwrite($DBTempHandle,$Picture); + } + } + } + + /* Close the handles */ + fclose($IndexHandle); + fclose($IndexTempHandle); + fclose($DBHandle); + fclose($DBTempHandle); + + /* Remove the prod files */ + unlink($Database); + unlink($Index); + + /* Swap the temp & prod DB */ + rename($DatabaseTemp,$Database); + rename($IndexTemp,$Index); + } + + function isInCache($ID,$Verbose=FALSE,$UpdateHitsCount=FALSE) + { + /* Compute the paths */ + $Index = $this->CacheFolder."/".$this->CacheIndex; + + /* Search the picture in the index file */ + $Handle = @fopen($Index, "r"); + while (!feof($Handle)) + { + $IndexPos = ftell($Handle); + $Entry = fgets($Handle, 4096); + if ( $Entry != "" ) + { + $Settings = preg_split("/,/",$Entry); + $PicID = $Settings[0]; + if ( $PicID == $ID ) + { + fclose($Handle); + + $DBPos = $Settings[1]; + $PicSize = $Settings[2]; + $GeneratedTS = $Settings[3]; + $Hits = intval($Settings[4]); + + if ( $UpdateHitsCount ) + { + $Hits++; + if ( strlen($Hits) < 7 ) { $Hits = $Hits.str_repeat(" ",7-strlen($Hits)); } + + $Handle = @fopen($Index, "r+"); + fseek($Handle,$IndexPos); + fwrite($Handle, $PicID.",".$DBPos.",".$PicSize.",".$GeneratedTS.",".$Hits."\r\n"); + fclose($Handle); + } + + if ($Verbose) + { return(array("DBPos"=>$DBPos,"PicSize"=>$PicSize,"GeneratedTS"=>$GeneratedTS,"Hits"=>$Hits)); } + else + { return(TRUE); } + } + } + } + fclose($Handle); + + /* Picture isn't in the cache */ + return(FALSE); + } + + /* Automatic output method based on the calling interface */ + function autoOutput($ID,$Destination="output.png") + { + if (php_sapi_name() == "cli") + $this->saveFromCache($ID,$Destination); + else + $this->strokeFromCache($ID); + } + + function strokeFromCache($ID) + { + /* Get the raw picture from the cache */ + $Picture = $this->getFromCache($ID); + + /* Do we have a hit? */ + if ( $Picture == NULL ) { return(FALSE); } + + header('Content-type: image/png'); + echo $Picture; + + return(TRUE); + } + + function saveFromCache($ID,$Destination) + { + /* Get the raw picture from the cache */ + $Picture = $this->getFromCache($ID); + + /* Do we have a hit? */ + if ( $Picture == NULL ) { return(FALSE); } + + /* Flush the picture to a file */ + $Handle = fopen($Destination,"w"); + fwrite($Handle,$Picture); + fclose($Handle); + + /* All went fine */ + return(TRUE); + } + + function getFromCache($ID) + { + /* Compute the path */ + $Database = $this->CacheFolder."/".$this->CacheDB; + + /* Lookup for the picture in the cache */ + $CacheInfo = $this->isInCache($ID,TRUE,TRUE); + + /* Not in the cache */ + if (!$CacheInfo) { return(NULL); } + + /* Get the database extended information */ + $DBPos = $CacheInfo["DBPos"]; + $PicSize = $CacheInfo["PicSize"]; + + /* Extract the picture from the solid cache file */ + $Handle = @fopen($Database, "r"); + fseek($Handle,$DBPos); + $Picture = fread($Handle,$PicSize); + fclose($Handle); + + /* Return back the raw picture data */ + return($Picture); + } + } ?> \ No newline at end of file diff --git a/www/libs/pChart2.1.3/class/pData.class.php b/www/libs/pChart2.1.4/class/pData.class.php similarity index 97% rename from www/libs/pChart2.1.3/class/pData.class.php rename to www/libs/pChart2.1.4/class/pData.class.php index 27505192..2dfff954 100644 --- a/www/libs/pChart2.1.3/class/pData.class.php +++ b/www/libs/pChart2.1.4/class/pData.class.php @@ -1,787 +1,788 @@ -array("R"=>188,"G"=>224,"B"=>46,"Alpha"=>100), - "1"=>array("R"=>224,"G"=>100,"B"=>46,"Alpha"=>100), - "2"=>array("R"=>224,"G"=>214,"B"=>46,"Alpha"=>100), - "3"=>array("R"=>46,"G"=>151,"B"=>224,"Alpha"=>100), - "4"=>array("R"=>176,"G"=>46,"B"=>224,"Alpha"=>100), - "5"=>array("R"=>224,"G"=>46,"B"=>117,"Alpha"=>100), - "6"=>array("R"=>92,"G"=>224,"B"=>46,"Alpha"=>100), - "7"=>array("R"=>224,"G"=>176,"B"=>46,"Alpha"=>100)); - - /* Class creator */ - function pData() - { - $this->Data = ""; - $this->Data["XAxisDisplay"] = AXIS_FORMAT_DEFAULT; - $this->Data["XAxisFormat"] = NULL; - $this->Data["XAxisName"] = NULL; - $this->Data["XAxisUnit"] = NULL; - $this->Data["Abscissa"] = NULL; - $this->Data["AbsicssaPosition"] = AXIS_POSITION_BOTTOM; - - $this->Data["Axis"][0]["Display"] = AXIS_FORMAT_DEFAULT; - $this->Data["Axis"][0]["Position"] = AXIS_POSITION_LEFT; - $this->Data["Axis"][0]["Identity"] = AXIS_Y; - } - - /* Add a single point or an array to the given serie */ - function addPoints($Values,$SerieName="Serie1") - { - if (!isset($this->Data["Series"][$SerieName])) - $this->initialise($SerieName); - - if ( is_array($Values) ) - { - foreach($Values as $Key => $Value) - { $this->Data["Series"][$SerieName]["Data"][] = $Value; } - } - else - $this->Data["Series"][$SerieName]["Data"][] = $Values; - - if ( $Values != VOID ) - { - $StrippedData = $this->stripVOID($this->Data["Series"][$SerieName]["Data"]); - if ( empty($StrippedData) ) { $this->Data["Series"][$SerieName]["Max"] = 0; $this->Data["Series"][$SerieName]["Min"] =0; return(0); } - $this->Data["Series"][$SerieName]["Max"] = max($StrippedData); - $this->Data["Series"][$SerieName]["Min"] = min($StrippedData); - } - } - - /* Strip VOID values */ - function stripVOID($Values) - { if (!is_array($Values)) { return(array()); } $Result = array(); foreach($Values as $Key => $Value) { if ( $Value != VOID ) { $Result[] = $Value; } } return($Result); } - - /* Return the number of values contained in a given serie */ - function getSerieCount($Serie) - { if (isset($this->Data["Series"][$Serie]["Data"])) { return(sizeof($this->Data["Series"][$Serie]["Data"])); } else { return(0); } } - - /* Remove a serie from the pData object */ - function removeSerie($Series) - { - if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } - foreach($Series as $Key => $Serie) { if (isset($this->Data["Series"][$Serie])) { unset($this->Data["Series"][$Serie]); } } - } - - /* Return a value from given serie & index */ - function getValueAt($Serie,$Index=0) - { if (isset($this->Data["Series"][$Serie]["Data"][$Index])) { return($this->Data["Series"][$Serie]["Data"][$Index]); } else { return(NULL); } } - - /* Return the values array */ - function getValues($Serie) - { if (isset($this->Data["Series"][$Serie]["Data"])) { return($this->Data["Series"][$Serie]["Data"]); } else { return(NULL); } } - - /* Reverse the values in the given serie */ - function reverseSerie($Series) - { - if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } - foreach($Series as $Key => $Serie) { if (isset($this->Data["Series"][$Serie]["Data"])) { $this->Data["Series"][$Serie]["Data"] = array_reverse($this->Data["Series"][$Serie]["Data"]); } } - } - - /* Return the sum of the serie values */ - function getSum($Serie) - { if (isset($this->Data["Series"][$Serie])) { return(array_sum($this->Data["Series"][$Serie]["Data"])); } else { return(NULL); } } - - /* Return the max value of a given serie */ - function getMax($Serie) - { if (isset($this->Data["Series"][$Serie]["Max"])) { return($this->Data["Series"][$Serie]["Max"]); } else { return(NULL); } } - - /* Return the min value of a given serie */ - function getMin($Serie) - { if (isset($this->Data["Series"][$Serie]["Min"])) { return($this->Data["Series"][$Serie]["Min"]); } else { return(NULL); } } - - /* Set the description of a given serie */ - function setSerieShape($Series,$Shape=SERIE_SHAPE_FILLEDCIRCLE) - { - if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } - foreach($Series as $Key => $Serie) { if (isset($this->Data["Series"][$Serie]) ) { $this->Data["Series"][$Serie]["Shape"] = $Shape; } } - } - - /* Set the description of a given serie */ - function setSerieDescription($Series,$Description="My serie") - { - if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } - foreach($Series as $Key => $Serie) { if (isset($this->Data["Series"][$Serie]) ) { $this->Data["Series"][$Serie]["Description"] = $Description; } } - } - - /* Set a serie as "drawable" while calling a rendering function */ - function setSerieDrawable($Series,$Drawable=TRUE) - { - if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } - foreach($Series as $Key => $Serie) { if (isset($this->Data["Series"][$Serie]) ) { $this->Data["Series"][$Serie]["isDrawable"] = $Drawable; } } - } - - /* Set the icon associated to a given serie */ - function setSeriePicture($Series,$Picture=NULL) - { - if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } - foreach($Series as $Key => $Serie) { if (isset($this->Data["Series"][$Serie]) ) { $this->Data["Series"][$Serie]["Picture"] = $Picture; } } - } - - /* Set the name of the X Axis */ - function setXAxisName($Name) - { $this->Data["XAxisName"] = $Name; } - - /* Set the display mode of the X Axis */ - function setXAxisDisplay($Mode,$Format=NULL) - { $this->Data["XAxisDisplay"] = $Mode; $this->Data["XAxisFormat"] = $Format; } - - /* Set the unit that will be displayed on the X axis */ - function setXAxisUnit($Unit) - { $this->Data["XAxisUnit"] = $Unit; } - - /* Set the serie that will be used as abscissa */ - function setAbscissa($Serie) - { if (isset($this->Data["Series"][$Serie])) { $this->Data["Abscissa"] = $Serie; } } - - function setAbsicssaPosition($Position = AXIS_POSITION_BOTTOM) - { $this->Data["AbsicssaPosition"] = $Position; } - - /* Set the name of the abscissa axis */ - function setAbscissaName($Name) - { $this->Data["AbscissaName"] = $Name; } - - /* Create a scatter group specifyin X and Y data series */ - function setScatterSerie($SerieX,$SerieY,$ID=0) - { if (isset($this->Data["Series"][$SerieX]) && isset($this->Data["Series"][$SerieY]) ) { $this->initScatterSerie($ID); $this->Data["ScatterSeries"][$ID]["X"] = $SerieX; $this->Data["ScatterSeries"][$ID]["Y"] = $SerieY; } } - - /* Set the shape of a given sctatter serie */ - function setScatterSerieShape($ID,$Shape=SERIE_SHAPE_FILLEDCIRCLE) - { if (isset($this->Data["ScatterSeries"][$ID]) ) { $this->Data["ScatterSeries"][$ID]["Shape"] = $Shape; } } - - /* Set the description of a given scatter serie */ - function setScatterSerieDescription($ID,$Description="My serie") - { if (isset($this->Data["ScatterSeries"][$ID]) ) { $this->Data["ScatterSeries"][$ID]["Description"] = $Description; } } - - /* Set the icon associated to a given scatter serie */ - function setScatterSeriePicture($ID,$Picture=NULL) - { if (isset($this->Data["ScatterSeries"][$ID]) ) { $this->Data["ScatterSeries"][$ID]["Picture"] = $Picture; } } - - /* Set a scatter serie as "drawable" while calling a rendering function */ - function setScatterSerieDrawable($ID ,$Drawable=TRUE) - { if (isset($this->Data["ScatterSeries"][$ID]) ) { $this->Data["ScatterSeries"][$ID]["isDrawable"] = $Drawable; } } - - /* Define if a scatter serie should be draw with ticks */ - function setScatterSerieTicks($ID,$Width=0) - { if ( isset($this->Data["ScatterSeries"][$ID]) ) { $this->Data["ScatterSeries"][$ID]["Ticks"] = $Width; } } - - /* Define if a scatter serie should be draw with a special weight */ - function setScatterSerieWeight($ID,$Weight=0) - { if ( isset($this->Data["ScatterSeries"][$ID]) ) { $this->Data["ScatterSeries"][$ID]["Weight"] = $Weight; } } - - /* Associate a color to a scatter serie */ - function setScatterSerieColor($ID,$Format) - { - $R = isset($Format["R"]) ? $Format["R"] : 0; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - - if ( isset($this->Data["ScatterSeries"][$ID]) ) - { - $this->Data["ScatterSeries"][$ID]["Color"]["R"] = $R; - $this->Data["ScatterSeries"][$ID]["Color"]["G"] = $G; - $this->Data["ScatterSeries"][$ID]["Color"]["B"] = $B; - $this->Data["ScatterSeries"][$ID]["Color"]["Alpha"] = $Alpha; - } - } - - /* Compute the series limits for an individual and global point of view */ - function limits() - { - $GlobalMin = ABSOLUTE_MAX; - $GlobalMax = ABSOLUTE_MIN; - - foreach($this->Data["Series"] as $Key => $Value) - { - if ( $this->Data["Abscissa"] != $Key && $this->Data["Series"][$Key]["isDrawable"] == TRUE) - { - if ( $GlobalMin > $this->Data["Series"][$Key]["Min"] ) { $GlobalMin = $this->Data["Series"][$Key]["Min"]; } - if ( $GlobalMax < $this->Data["Series"][$Key]["Max"] ) { $GlobalMax = $this->Data["Series"][$Key]["Max"]; } - } - } - $this->Data["Min"] = $GlobalMin; - $this->Data["Max"] = $GlobalMax; - - return(array($GlobalMin,$GlobalMax)); - } - - /* Mark all series as drawable */ - function drawAll() - { foreach($this->Data["Series"] as $Key => $Value) { if ( $this->Data["Abscissa"] != $Key ) { $this->Data["Series"][$Key]["isDrawable"]=TRUE; } } } - - /* Return the average value of the given serie */ - function getSerieAverage($Serie) - { - if ( isset($this->Data["Series"][$Serie]) ) - { - $SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]); - return(array_sum($SerieData)/sizeof($SerieData)); - } - else - return(NULL); - } - - /* Return the geometric mean of the given serie */ - function getGeometricMean($Serie) - { - if ( isset($this->Data["Series"][$Serie]) ) - { - $SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]); - $Seriesum = 1; foreach($SerieData as $Key => $Value) { $Seriesum = $Seriesum * $Value; } - return(pow($Seriesum,1/sizeof($SerieData))); - } - else - return(NULL); - } - - /* Return the harmonic mean of the given serie */ - function getHarmonicMean($Serie) - { - if ( isset($this->Data["Series"][$Serie]) ) - { - $SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]); - $Seriesum = 0; foreach($SerieData as $Key => $Value) { $Seriesum = $Seriesum + 1/$Value; } - return(sizeof($SerieData)/$Seriesum); - } - else - return(NULL); - } - - /* Return the standard deviation of the given serie */ - function getStandardDeviation($Serie) - { - if ( isset($this->Data["Series"][$Serie]) ) - { - $Average = $this->getSerieAverage($Serie); - $SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]); - - $DeviationSum = 0; - foreach($SerieData as $Key => $Value) - $DeviationSum = $DeviationSum + ($Value-$Average)*($Value-$Average); - - $Deviation = sqrt($DeviationSum/count($SerieData)); - - return($Deviation); - } - else - return(NULL); - } - - /* Return the Coefficient of variation of the given serie */ - function getCoefficientOfVariation($Serie) - { - if ( isset($this->Data["Series"][$Serie]) ) - { - $Average = $this->getSerieAverage($Serie); - $StandardDeviation = $this->getStandardDeviation($Serie); - - if ( $StandardDeviation != 0 ) - return($StandardDeviation/$Average); - else - return(NULL); - } - else - return(NULL); - } - - /* Return the median value of the given serie */ - function getSerieMedian($Serie) - { - if ( isset($this->Data["Series"][$Serie]) ) - { - $SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]); - sort($SerieData); - $SerieCenter = floor(sizeof($SerieData)/2); - - if ( isset($SerieData[$SerieCenter]) ) - return($SerieData[$SerieCenter]); - else - return(NULL); - } - else - return(NULL); - } - - /* Return the x th percentil of the given serie */ - function getSeriePercentile($Serie="Serie1",$Percentil=95) - { - if (!isset($this->Data["Series"][$Serie]["Data"])) { return(NULL); } - - $Values = count($this->Data["Series"][$Serie]["Data"])-1; - if ( $Values < 0 ) { $Values = 0; } - - $PercentilID = floor(($Values/100)*$Percentil+.5); - $SortedValues = $this->Data["Series"][$Serie]["Data"]; - sort($SortedValues); - - if ( is_numeric($SortedValues[$PercentilID]) ) - return($SortedValues[$PercentilID]); - else - return(NULL); - } - - /* Add random values to a given serie */ - function addRandomValues($SerieName="Serie1",$Options="") - { - $Values = isset($Options["Values"]) ? $Options["Values"] : 20; - $Min = isset($Options["Min"]) ? $Options["Min"] : 0; - $Max = isset($Options["Max"]) ? $Options["Max"] : 100; - $withFloat = isset($Options["withFloat"]) ? $Options["withFloat"] : FALSE; - - for ($i=0;$i<=$Values;$i++) - { - if ( $withFloat ) { $Value = rand($Min*100,$Max*100)/100; } else { $Value = rand($Min,$Max); } - $this->addPoints($Value,$SerieName); - } - } - - /* Test if we have valid data */ - function containsData() - { - if (!isset($this->Data["Series"])) { return(FALSE); } - - $Result = FALSE; - foreach($this->Data["Series"] as $Key => $Value) - { if ( $this->Data["Abscissa"] != $Key && $this->Data["Series"][$Key]["isDrawable"]==TRUE) { $Result=TRUE; } } - return($Result); - } - - /* Set the display mode of an Axis */ - function setAxisDisplay($AxisID,$Mode=AXIS_FORMAT_DEFAULT,$Format=NULL) - { - if ( isset($this->Data["Axis"][$AxisID] ) ) - { - $this->Data["Axis"][$AxisID]["Display"] = $Mode; - if ( $Format != NULL ) { $this->Data["Axis"][$AxisID]["Format"] = $Format; } - } - } - - /* Set the position of an Axis */ - function setAxisPosition($AxisID,$Position=AXIS_POSITION_LEFT) - { if ( isset($this->Data["Axis"][$AxisID] ) ) { $this->Data["Axis"][$AxisID]["Position"] = $Position; } } - - /* Associate an unit to an axis */ - function setAxisUnit($AxisID,$Unit) - { if ( isset($this->Data["Axis"][$AxisID] ) ) { $this->Data["Axis"][$AxisID]["Unit"] = $Unit; } } - - /* Associate a name to an axis */ - function setAxisName($AxisID,$Name) - { if ( isset($this->Data["Axis"][$AxisID] ) ) { $this->Data["Axis"][$AxisID]["Name"] = $Name; } } - - /* Associate a color to an axis */ - function setAxisColor($AxisID,$Format) - { - $R = isset($Format["R"]) ? $Format["R"] : 0; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - - if ( isset($this->Data["Axis"][$AxisID] ) ) - { - $this->Data["Axis"][$AxisID]["Color"]["R"] = $R; - $this->Data["Axis"][$AxisID]["Color"]["G"] = $G; - $this->Data["Axis"][$AxisID]["Color"]["B"] = $B; - $this->Data["Axis"][$AxisID]["Color"]["Alpha"] = $Alpha; - } - } - - - /* Design an axis as X or Y member */ - function setAxisXY($AxisID,$Identity=AXIS_Y) - { if ( isset($this->Data["Axis"][$AxisID] ) ) { $this->Data["Axis"][$AxisID]["Identity"] = $Identity; } } - - /* Associate one data serie with one axis */ - function setSerieOnAxis($Series,$AxisID) - { - if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } - foreach($Series as $Key => $Serie) - { - $PreviousAxis = $this->Data["Series"][$Serie]["Axis"]; - - /* Create missing axis */ - if ( !isset($this->Data["Axis"][$AxisID] ) ) - { $this->Data["Axis"][$AxisID]["Position"] = AXIS_POSITION_LEFT; $this->Data["Axis"][$AxisID]["Identity"] = AXIS_Y;} - - $this->Data["Series"][$Serie]["Axis"] = $AxisID; - - /* Cleanup unused axis */ - $Found = FALSE; - foreach($this->Data["Series"] as $SerieName => $Values) { if ( $Values["Axis"] == $PreviousAxis ) { $Found = TRUE; } } - if (!$Found) { unset($this->Data["Axis"][$PreviousAxis]); } - } - } - - /* Define if a serie should be draw with ticks */ - function setSerieTicks($Series,$Width=0) - { - if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } - foreach($Series as $Key => $Serie) { if ( isset($this->Data["Series"][$Serie]) ) { $this->Data["Series"][$Serie]["Ticks"] = $Width; } } - } - - /* Define if a serie should be draw with a special weight */ - function setSerieWeight($Series,$Weight=0) - { - if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } - foreach($Series as $Key => $Serie) { if ( isset($this->Data["Series"][$Serie]) ) { $this->Data["Series"][$Serie]["Weight"] = $Weight; } } - } - - /* Returns the palette of the given serie */ - function getSeriePalette($Serie) - { - if ( !isset($this->Data["Series"][$Serie]) ) { return(NULL); } - - $Result = ""; - $Result["R"] = $this->Data["Series"][$Serie]["Color"]["R"]; - $Result["G"] = $this->Data["Series"][$Serie]["Color"]["G"]; - $Result["B"] = $this->Data["Series"][$Serie]["Color"]["B"]; - $Result["Alpha"] = $this->Data["Series"][$Serie]["Color"]["Alpha"]; - - return($Result); - } - - /* Set the color of one serie */ - function setPalette($Series,$Format=NULL) - { - if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } - - foreach($Series as $Key => $Serie) - { - $R = isset($Format["R"]) ? $Format["R"] : 0; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - - if ( isset($this->Data["Series"][$Serie]) ) - { - $OldR = $this->Data["Series"][$Serie]["Color"]["R"]; $OldG = $this->Data["Series"][$Serie]["Color"]["G"]; $OldB = $this->Data["Series"][$Serie]["Color"]["B"]; - $this->Data["Series"][$Serie]["Color"]["R"] = $R; - $this->Data["Series"][$Serie]["Color"]["G"] = $G; - $this->Data["Series"][$Serie]["Color"]["B"] = $B; - $this->Data["Series"][$Serie]["Color"]["Alpha"] = $Alpha; - - /* Do reverse processing on the internal palette array */ - foreach ($this->Palette as $Key => $Value) - { if ($Value["R"] == $OldR && $Value["G"] == $OldG && $Value["B"] == $OldB) { $this->Palette[$Key]["R"] = $R; $this->Palette[$Key]["G"] = $G; $this->Palette[$Key]["B"] = $B; $this->Palette[$Key]["Alpha"] = $Alpha;} } - } - } - } - - /* Load a palette file */ - function loadPalette($FileName,$Overwrite=FALSE) - { - if ( !file_exists($FileName) ) { return(-1); } - if ( $Overwrite ) { $this->Palette = ""; } - - $fileHandle = @fopen($FileName, "r"); - if (!$fileHandle) { return(-1); } - while (!feof($fileHandle)) - { - $buffer = fgets($fileHandle, 4096); - if ( preg_match("/,/",$buffer) ) - { - list($R,$G,$B,$Alpha) = preg_split("/,/",$buffer); - if ( $this->Palette == "" ) { $ID = 0; } else { $ID = count($this->Palette); } - $this->Palette[$ID] = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); - } - } - fclose($fileHandle); - - /* Apply changes to current series */ - $ID = 0; - if ( isset($this->Data["Series"])) - { - foreach($this->Data["Series"] as $Key => $Value) - { - if ( !isset($this->Palette[$ID]) ) - $this->Data["Series"][$Key]["Color"] = array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>0); - else - $this->Data["Series"][$Key]["Color"] = $this->Palette[$ID]; - $ID++; - } - } - } - - /* Initialise a given scatter serie */ - function initScatterSerie($ID) - { - if ( isset($this->Data["ScatterSeries"][$ID]) ) { return(0); } - - $this->Data["ScatterSeries"][$ID]["Description"] = "Scatter ".$ID; - $this->Data["ScatterSeries"][$ID]["isDrawable"] = TRUE; - $this->Data["ScatterSeries"][$ID]["Picture"] = NULL; - $this->Data["ScatterSeries"][$ID]["Ticks"] = 0; - $this->Data["ScatterSeries"][$ID]["Weight"] = 0; - - if ( isset($this->Palette[$ID]) ) - $this->Data["ScatterSeries"][$ID]["Color"] = $this->Palette[$ID]; - else - { - $this->Data["ScatterSeries"][$ID]["Color"]["R"] = rand(0,255); - $this->Data["ScatterSeries"][$ID]["Color"]["G"] = rand(0,255); - $this->Data["ScatterSeries"][$ID]["Color"]["B"] = rand(0,255); - $this->Data["ScatterSeries"][$ID]["Color"]["Alpha"] = 100; - } - } - - /* Initialise a given serie */ - function initialise($Serie) - { - if ( isset($this->Data["Series"]) ) { $ID = count($this->Data["Series"]); } else { $ID = 0; } - - $this->Data["Series"][$Serie]["Description"] = $Serie; - $this->Data["Series"][$Serie]["isDrawable"] = TRUE; - $this->Data["Series"][$Serie]["Picture"] = NULL; - $this->Data["Series"][$Serie]["Max"] = NULL; - $this->Data["Series"][$Serie]["Min"] = NULL; - $this->Data["Series"][$Serie]["Axis"] = 0; - $this->Data["Series"][$Serie]["Ticks"] = 0; - $this->Data["Series"][$Serie]["Weight"] = 0; - $this->Data["Series"][$Serie]["Shape"] = SERIE_SHAPE_FILLEDCIRCLE; - - if ( isset($this->Palette[$ID]) ) - $this->Data["Series"][$Serie]["Color"] = $this->Palette[$ID]; - else - { - $this->Data["Series"][$Serie]["Color"]["R"] = rand(0,255); - $this->Data["Series"][$Serie]["Color"]["G"] = rand(0,255); - $this->Data["Series"][$Serie]["Color"]["B"] = rand(0,255); - $this->Data["Series"][$Serie]["Color"]["Alpha"] = 100; - } - } - - function normalize($NormalizationFactor=100,$UnitChange=NULL,$Round=1) - { - $Abscissa = $this->Data["Abscissa"]; - - $SelectedSeries = ""; - $MaxVal = 0; - foreach($this->Data["Axis"] as $AxisID => $Axis) - { - if ( $UnitChange != NULL ) { $this->Data["Axis"][$AxisID]["Unit"] = $UnitChange; } - - foreach($this->Data["Series"] as $SerieName => $Serie) - { - if ($Serie["Axis"] == $AxisID && $Serie["isDrawable"] == TRUE && $SerieName != $Abscissa) - { - $SelectedSeries[$SerieName] = $SerieName; - - if ( count($Serie["Data"] ) > $MaxVal ) { $MaxVal = count($Serie["Data"]); } - } - } - } - - for($i=0;$i<=$MaxVal-1;$i++) - { - $Factor = 0; - foreach ($SelectedSeries as $Key => $SerieName ) - { - $Value = $this->Data["Series"][$SerieName]["Data"][$i]; - if ( $Value != VOID ) - $Factor = $Factor + abs($Value); - } - - if ( $Factor != 0 ) - { - $Factor = $NormalizationFactor / $Factor; - - foreach ($SelectedSeries as $Key => $SerieName ) - { - $Value = $this->Data["Series"][$SerieName]["Data"][$i]; - - if ( $Value != VOID && $Factor != $NormalizationFactor ) - $this->Data["Series"][$SerieName]["Data"][$i] = round(abs($Value)*$Factor,$Round); - elseif ( $Value == VOID || $Value == 0 ) - $this->Data["Series"][$SerieName]["Data"][$i] = VOID; - elseif ( $Factor == $NormalizationFactor ) - $this->Data["Series"][$SerieName]["Data"][$i] = $NormalizationFactor; - } - } - } - - foreach ($SelectedSeries as $Key => $SerieName ) - { - $this->Data["Series"][$SerieName]["Max"] = max($this->stripVOID($this->Data["Series"][$SerieName]["Data"])); - $this->Data["Series"][$SerieName]["Min"] = min($this->stripVOID($this->Data["Series"][$SerieName]["Data"])); - } - } - - /* Load data from a CSV (or similar) data source */ - function importFromCSV($FileName,$Options="") - { - $Delimiter = isset($Options["Delimiter"]) ? $Options["Delimiter"] : ","; - $GotHeader = isset($Options["GotHeader"]) ? $Options["GotHeader"] : FALSE; - $SkipColumns = isset($Options["SkipColumns"]) ? $Options["SkipColumns"] : array(-1); - $DefaultSerieName = isset($Options["DefaultSerieName"]) ? $Options["DefaultSerieName"] : "Serie"; - - $Handle = @fopen($FileName,"r"); - if ($Handle) - { - $HeaderParsed = FALSE; $SerieNames = ""; - while (!feof($Handle)) - { - $Buffer = fgets($Handle, 4096); - $Buffer = str_replace(chr(10),"",$Buffer); - $Buffer = str_replace(chr(13),"",$Buffer); - $Values = preg_split("/".$Delimiter."/",$Buffer); - - if ( $Buffer != "" ) - { - if ( $GotHeader && !$HeaderParsed ) - { - foreach($Values as $Key => $Name) { if ( !in_array($Key,$SkipColumns) ) { $SerieNames[$Key] = $Name; } } - $HeaderParsed = TRUE; - } - else - { - if ($SerieNames == "" ) { foreach($Values as $Key => $Name) { if ( !in_array($Key,$SkipColumns) ) { $SerieNames[$Key] = $DefaultSerieName.$Key; } } } - foreach($Values as $Key => $Value) { if ( !in_array($Key,$SkipColumns) ) { $this->addPoints($Value,$SerieNames[$Key]); } } - } - } - } - fclose($Handle); - } - } - - /* Create a dataset based on a formula */ - function createFunctionSerie($SerieName,$Formula="",$Options="") - { - $MinX = isset($Options["MinX"]) ? $Options["MinX"] : -10; - $MaxX = isset($Options["MaxX"]) ? $Options["MaxX"] : 10; - $XStep = isset($Options["XStep"]) ? $Options["XStep"] : 1; - $AutoDescription = isset($Options["AutoDescription"]) ? $Options["AutoDescription"] : FALSE; - $RecordAbscissa = isset($Options["RecordAbscissa"]) ? $Options["RecordAbscissa"] : FALSE; - $AbscissaSerie = isset($Options["AbscissaSerie"]) ? $Options["AbscissaSerie"] : "Abscissa"; - - if ( $Formula == "" ) { return(0); } - - $Result = ""; $Abscissa = ""; - for($i=$MinX; $i<=$MaxX; $i=$i+$XStep) - { - $Expression = "\$return = '!'.(".str_replace("z",$i,$Formula).");"; - if ( @eval($Expression) === FALSE ) { $return = VOID; } - if ( $return == "!" ) { $return = VOID; } else { $return = $this->right($return,strlen($return)-1); } - if ( $return == "NAN" ) { $return = VOID; } - if ( $return == "INF" ) { $return = VOID; } - if ( $return == "-INF" ) { $return = VOID; } - - $Abscissa[] = $i; - $Result[] = $return; - } - - $this->addPoints($Result,$SerieName); - if ( $AutoDescription ) { $this->setSerieDescription($SerieName,$Formula); } - if ( $RecordAbscissa ) { $this->addPoints($Abscissa,$AbscissaSerie); } - } - - function negateValues($Series) - { - if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } - foreach($Series as $Key => $SerieName) - { - if (isset($this->Data["Series"][$SerieName])) - { - $Data = ""; - foreach($this->Data["Series"][$SerieName]["Data"] as $Key => $Value) - { if ( $Value == VOID ) { $Data[] = VOID; } else { $Data[] = -$Value; } } - $this->Data["Series"][$SerieName]["Data"] = $Data; - - $this->Data["Series"][$SerieName]["Max"] = max($this->stripVOID($this->Data["Series"][$SerieName]["Data"])); - $this->Data["Series"][$SerieName]["Min"] = min($this->stripVOID($this->Data["Series"][$SerieName]["Data"])); - } - } - } - - /* Return the data & configuration of the series */ - function getData() - { return($this->Data); } - - /* Save a palette element */ - function savePalette($ID,$Color) - { $this->Palette[$ID] = $Color; } - - /* Return the palette of the series */ - function getPalette() - { return($this->Palette); } - - /* Called by the scaling algorithm to save the config */ - function saveAxisConfig($Axis) { $this->Data["Axis"]=$Axis; } - - /* Save the Y Margin if set */ - function saveYMargin($Value) { $this->Data["YMargin"]=$Value; } - - /* Save extended configuration to the pData object */ - function saveExtendedData($Tag,$Values) { $this->Data["Extended"][$Tag]=$Values; } - - /* Called by the scaling algorithm to save the orientation of the scale */ - function saveOrientation($Orientation) { $this->Data["Orientation"]=$Orientation; } - - /* Convert a string to a single elements array */ - function convertToArray($Value) - { $Values = ""; $Values[] = $Value; return($Values); } - - /* Class string wrapper */ - function __toString() - { return("pData object."); } - - function left($value,$NbChar) { return substr($value,0,$NbChar); } - function right($value,$NbChar) { return substr($value,strlen($value)-$NbChar,$NbChar); } - function mid($value,$Depart,$NbChar) { return substr($value,$Depart-1,$NbChar); } - } +array("R"=>188,"G"=>224,"B"=>46,"Alpha"=>100), + "1"=>array("R"=>224,"G"=>100,"B"=>46,"Alpha"=>100), + "2"=>array("R"=>224,"G"=>214,"B"=>46,"Alpha"=>100), + "3"=>array("R"=>46,"G"=>151,"B"=>224,"Alpha"=>100), + "4"=>array("R"=>176,"G"=>46,"B"=>224,"Alpha"=>100), + "5"=>array("R"=>224,"G"=>46,"B"=>117,"Alpha"=>100), + "6"=>array("R"=>92,"G"=>224,"B"=>46,"Alpha"=>100), + "7"=>array("R"=>224,"G"=>176,"B"=>46,"Alpha"=>100)); + + /* Class creator */ + function pData() + { + $this->Data = ""; + $this->Data["XAxisDisplay"] = AXIS_FORMAT_DEFAULT; + $this->Data["XAxisFormat"] = NULL; + $this->Data["XAxisName"] = NULL; + $this->Data["XAxisUnit"] = NULL; + $this->Data["Abscissa"] = NULL; + $this->Data["AbsicssaPosition"] = AXIS_POSITION_BOTTOM; + + $this->Data["Axis"][0]["Display"] = AXIS_FORMAT_DEFAULT; + $this->Data["Axis"][0]["Position"] = AXIS_POSITION_LEFT; + $this->Data["Axis"][0]["Identity"] = AXIS_Y; + } + + /* Add a single point or an array to the given serie */ + function addPoints($Values,$SerieName="Serie1") + { + if (!isset($this->Data["Series"][$SerieName])) + $this->initialise($SerieName); + + if ( is_array($Values) ) + { + foreach($Values as $Key => $Value) + { $this->Data["Series"][$SerieName]["Data"][] = $Value; } + } + else + $this->Data["Series"][$SerieName]["Data"][] = $Values; + + if ( $Values != VOID ) + { + $StrippedData = $this->stripVOID($this->Data["Series"][$SerieName]["Data"]); + if ( empty($StrippedData) ) { $this->Data["Series"][$SerieName]["Max"] = 0; $this->Data["Series"][$SerieName]["Min"] =0; return(0); } + $this->Data["Series"][$SerieName]["Max"] = max($StrippedData); + $this->Data["Series"][$SerieName]["Min"] = min($StrippedData); + } + } + + /* Strip VOID values */ + function stripVOID($Values) + { if (!is_array($Values)) { return(array()); } $Result = array(); foreach($Values as $Key => $Value) { if ( $Value != VOID ) { $Result[] = $Value; } } return($Result); } + + /* Return the number of values contained in a given serie */ + function getSerieCount($Serie) + { if (isset($this->Data["Series"][$Serie]["Data"])) { return(sizeof($this->Data["Series"][$Serie]["Data"])); } else { return(0); } } + + /* Remove a serie from the pData object */ + function removeSerie($Series) + { + if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } + foreach($Series as $Key => $Serie) { if (isset($this->Data["Series"][$Serie])) { unset($this->Data["Series"][$Serie]); } } + } + + /* Return a value from given serie & index */ + function getValueAt($Serie,$Index=0) + { if (isset($this->Data["Series"][$Serie]["Data"][$Index])) { return($this->Data["Series"][$Serie]["Data"][$Index]); } else { return(NULL); } } + + /* Return the values array */ + function getValues($Serie) + { if (isset($this->Data["Series"][$Serie]["Data"])) { return($this->Data["Series"][$Serie]["Data"]); } else { return(NULL); } } + + /* Reverse the values in the given serie */ + function reverseSerie($Series) + { + if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } + foreach($Series as $Key => $Serie) { if (isset($this->Data["Series"][$Serie]["Data"])) { $this->Data["Series"][$Serie]["Data"] = array_reverse($this->Data["Series"][$Serie]["Data"]); } } + } + + /* Return the sum of the serie values */ + function getSum($Serie) + { if (isset($this->Data["Series"][$Serie])) { return(array_sum($this->Data["Series"][$Serie]["Data"])); } else { return(NULL); } } + + /* Return the max value of a given serie */ + function getMax($Serie) + { if (isset($this->Data["Series"][$Serie]["Max"])) { return($this->Data["Series"][$Serie]["Max"]); } else { return(NULL); } } + + /* Return the min value of a given serie */ + function getMin($Serie) + { if (isset($this->Data["Series"][$Serie]["Min"])) { return($this->Data["Series"][$Serie]["Min"]); } else { return(NULL); } } + + /* Set the description of a given serie */ + function setSerieShape($Series,$Shape=SERIE_SHAPE_FILLEDCIRCLE) + { + if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } + foreach($Series as $Key => $Serie) { if (isset($this->Data["Series"][$Serie]) ) { $this->Data["Series"][$Serie]["Shape"] = $Shape; } } + } + + /* Set the description of a given serie */ + function setSerieDescription($Series,$Description="My serie") + { + if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } + foreach($Series as $Key => $Serie) { if (isset($this->Data["Series"][$Serie]) ) { $this->Data["Series"][$Serie]["Description"] = $Description; } } + } + + /* Set a serie as "drawable" while calling a rendering function */ + function setSerieDrawable($Series,$Drawable=TRUE) + { + if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } + foreach($Series as $Key => $Serie) { if (isset($this->Data["Series"][$Serie]) ) { $this->Data["Series"][$Serie]["isDrawable"] = $Drawable; } } + } + + /* Set the icon associated to a given serie */ + function setSeriePicture($Series,$Picture=NULL) + { + if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } + foreach($Series as $Key => $Serie) { if (isset($this->Data["Series"][$Serie]) ) { $this->Data["Series"][$Serie]["Picture"] = $Picture; } } + } + + /* Set the name of the X Axis */ + function setXAxisName($Name) + { $this->Data["XAxisName"] = $Name; } + + /* Set the display mode of the X Axis */ + function setXAxisDisplay($Mode,$Format=NULL) + { $this->Data["XAxisDisplay"] = $Mode; $this->Data["XAxisFormat"] = $Format; } + + /* Set the unit that will be displayed on the X axis */ + function setXAxisUnit($Unit) + { $this->Data["XAxisUnit"] = $Unit; } + + /* Set the serie that will be used as abscissa */ + function setAbscissa($Serie) + { if (isset($this->Data["Series"][$Serie])) { $this->Data["Abscissa"] = $Serie; } } + + function setAbsicssaPosition($Position = AXIS_POSITION_BOTTOM) + { $this->Data["AbsicssaPosition"] = $Position; } + + /* Set the name of the abscissa axis */ + function setAbscissaName($Name) + { $this->Data["AbscissaName"] = $Name; } + + /* Create a scatter group specifyin X and Y data series */ + function setScatterSerie($SerieX,$SerieY,$ID=0) + { if (isset($this->Data["Series"][$SerieX]) && isset($this->Data["Series"][$SerieY]) ) { $this->initScatterSerie($ID); $this->Data["ScatterSeries"][$ID]["X"] = $SerieX; $this->Data["ScatterSeries"][$ID]["Y"] = $SerieY; } } + + /* Set the shape of a given sctatter serie */ + function setScatterSerieShape($ID,$Shape=SERIE_SHAPE_FILLEDCIRCLE) + { if (isset($this->Data["ScatterSeries"][$ID]) ) { $this->Data["ScatterSeries"][$ID]["Shape"] = $Shape; } } + + /* Set the description of a given scatter serie */ + function setScatterSerieDescription($ID,$Description="My serie") + { if (isset($this->Data["ScatterSeries"][$ID]) ) { $this->Data["ScatterSeries"][$ID]["Description"] = $Description; } } + + /* Set the icon associated to a given scatter serie */ + function setScatterSeriePicture($ID,$Picture=NULL) + { if (isset($this->Data["ScatterSeries"][$ID]) ) { $this->Data["ScatterSeries"][$ID]["Picture"] = $Picture; } } + + /* Set a scatter serie as "drawable" while calling a rendering function */ + function setScatterSerieDrawable($ID ,$Drawable=TRUE) + { if (isset($this->Data["ScatterSeries"][$ID]) ) { $this->Data["ScatterSeries"][$ID]["isDrawable"] = $Drawable; } } + + /* Define if a scatter serie should be draw with ticks */ + function setScatterSerieTicks($ID,$Width=0) + { if ( isset($this->Data["ScatterSeries"][$ID]) ) { $this->Data["ScatterSeries"][$ID]["Ticks"] = $Width; } } + + /* Define if a scatter serie should be draw with a special weight */ + function setScatterSerieWeight($ID,$Weight=0) + { if ( isset($this->Data["ScatterSeries"][$ID]) ) { $this->Data["ScatterSeries"][$ID]["Weight"] = $Weight; } } + + /* Associate a color to a scatter serie */ + function setScatterSerieColor($ID,$Format) + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + + if ( isset($this->Data["ScatterSeries"][$ID]) ) + { + $this->Data["ScatterSeries"][$ID]["Color"]["R"] = $R; + $this->Data["ScatterSeries"][$ID]["Color"]["G"] = $G; + $this->Data["ScatterSeries"][$ID]["Color"]["B"] = $B; + $this->Data["ScatterSeries"][$ID]["Color"]["Alpha"] = $Alpha; + } + } + + /* Compute the series limits for an individual and global point of view */ + function limits() + { + $GlobalMin = ABSOLUTE_MAX; + $GlobalMax = ABSOLUTE_MIN; + + foreach($this->Data["Series"] as $Key => $Value) + { + if ( $this->Data["Abscissa"] != $Key && $this->Data["Series"][$Key]["isDrawable"] == TRUE) + { + if ( $GlobalMin > $this->Data["Series"][$Key]["Min"] ) { $GlobalMin = $this->Data["Series"][$Key]["Min"]; } + if ( $GlobalMax < $this->Data["Series"][$Key]["Max"] ) { $GlobalMax = $this->Data["Series"][$Key]["Max"]; } + } + } + $this->Data["Min"] = $GlobalMin; + $this->Data["Max"] = $GlobalMax; + + return(array($GlobalMin,$GlobalMax)); + } + + /* Mark all series as drawable */ + function drawAll() + { foreach($this->Data["Series"] as $Key => $Value) { if ( $this->Data["Abscissa"] != $Key ) { $this->Data["Series"][$Key]["isDrawable"]=TRUE; } } } + + /* Return the average value of the given serie */ + function getSerieAverage($Serie) + { + if ( isset($this->Data["Series"][$Serie]) ) + { + $SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]); + return(array_sum($SerieData)/sizeof($SerieData)); + } + else + return(NULL); + } + + /* Return the geometric mean of the given serie */ + function getGeometricMean($Serie) + { + if ( isset($this->Data["Series"][$Serie]) ) + { + $SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]); + $Seriesum = 1; foreach($SerieData as $Key => $Value) { $Seriesum = $Seriesum * $Value; } + return(pow($Seriesum,1/sizeof($SerieData))); + } + else + return(NULL); + } + + /* Return the harmonic mean of the given serie */ + function getHarmonicMean($Serie) + { + if ( isset($this->Data["Series"][$Serie]) ) + { + $SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]); + $Seriesum = 0; foreach($SerieData as $Key => $Value) { $Seriesum = $Seriesum + 1/$Value; } + return(sizeof($SerieData)/$Seriesum); + } + else + return(NULL); + } + + /* Return the standard deviation of the given serie */ + function getStandardDeviation($Serie) + { + if ( isset($this->Data["Series"][$Serie]) ) + { + $Average = $this->getSerieAverage($Serie); + $SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]); + + $DeviationSum = 0; + foreach($SerieData as $Key => $Value) + $DeviationSum = $DeviationSum + ($Value-$Average)*($Value-$Average); + + $Deviation = sqrt($DeviationSum/count($SerieData)); + + return($Deviation); + } + else + return(NULL); + } + + /* Return the Coefficient of variation of the given serie */ + function getCoefficientOfVariation($Serie) + { + if ( isset($this->Data["Series"][$Serie]) ) + { + $Average = $this->getSerieAverage($Serie); + $StandardDeviation = $this->getStandardDeviation($Serie); + + if ( $StandardDeviation != 0 ) + return($StandardDeviation/$Average); + else + return(NULL); + } + else + return(NULL); + } + + /* Return the median value of the given serie */ + function getSerieMedian($Serie) + { + if ( isset($this->Data["Series"][$Serie]) ) + { + $SerieData = $this->stripVOID($this->Data["Series"][$Serie]["Data"]); + sort($SerieData); + $SerieCenter = floor(sizeof($SerieData)/2); + + if ( isset($SerieData[$SerieCenter]) ) + return($SerieData[$SerieCenter]); + else + return(NULL); + } + else + return(NULL); + } + + /* Return the x th percentil of the given serie */ + function getSeriePercentile($Serie="Serie1",$Percentil=95) + { + if (!isset($this->Data["Series"][$Serie]["Data"])) { return(NULL); } + + $Values = count($this->Data["Series"][$Serie]["Data"])-1; + if ( $Values < 0 ) { $Values = 0; } + + $PercentilID = floor(($Values/100)*$Percentil+.5); + $SortedValues = $this->Data["Series"][$Serie]["Data"]; + sort($SortedValues); + + if ( is_numeric($SortedValues[$PercentilID]) ) + return($SortedValues[$PercentilID]); + else + return(NULL); + } + + /* Add random values to a given serie */ + function addRandomValues($SerieName="Serie1",$Options="") + { + $Values = isset($Options["Values"]) ? $Options["Values"] : 20; + $Min = isset($Options["Min"]) ? $Options["Min"] : 0; + $Max = isset($Options["Max"]) ? $Options["Max"] : 100; + $withFloat = isset($Options["withFloat"]) ? $Options["withFloat"] : FALSE; + + for ($i=0;$i<=$Values;$i++) + { + if ( $withFloat ) { $Value = rand($Min*100,$Max*100)/100; } else { $Value = rand($Min,$Max); } + $this->addPoints($Value,$SerieName); + } + } + + /* Test if we have valid data */ + function containsData() + { + if (!isset($this->Data["Series"])) { return(FALSE); } + + $Result = FALSE; + foreach($this->Data["Series"] as $Key => $Value) + { if ( $this->Data["Abscissa"] != $Key && $this->Data["Series"][$Key]["isDrawable"]==TRUE) { $Result=TRUE; } } + return($Result); + } + + /* Set the display mode of an Axis */ + function setAxisDisplay($AxisID,$Mode=AXIS_FORMAT_DEFAULT,$Format=NULL) + { + if ( isset($this->Data["Axis"][$AxisID] ) ) + { + $this->Data["Axis"][$AxisID]["Display"] = $Mode; + if ( $Format != NULL ) { $this->Data["Axis"][$AxisID]["Format"] = $Format; } + } + } + + /* Set the position of an Axis */ + function setAxisPosition($AxisID,$Position=AXIS_POSITION_LEFT) + { if ( isset($this->Data["Axis"][$AxisID] ) ) { $this->Data["Axis"][$AxisID]["Position"] = $Position; } } + + /* Associate an unit to an axis */ + function setAxisUnit($AxisID,$Unit) + { if ( isset($this->Data["Axis"][$AxisID] ) ) { $this->Data["Axis"][$AxisID]["Unit"] = $Unit; } } + + /* Associate a name to an axis */ + function setAxisName($AxisID,$Name) + { if ( isset($this->Data["Axis"][$AxisID] ) ) { $this->Data["Axis"][$AxisID]["Name"] = $Name; } } + + /* Associate a color to an axis */ + function setAxisColor($AxisID,$Format) + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + + if ( isset($this->Data["Axis"][$AxisID] ) ) + { + $this->Data["Axis"][$AxisID]["Color"]["R"] = $R; + $this->Data["Axis"][$AxisID]["Color"]["G"] = $G; + $this->Data["Axis"][$AxisID]["Color"]["B"] = $B; + $this->Data["Axis"][$AxisID]["Color"]["Alpha"] = $Alpha; + } + } + + + /* Design an axis as X or Y member */ + function setAxisXY($AxisID,$Identity=AXIS_Y) + { if ( isset($this->Data["Axis"][$AxisID] ) ) { $this->Data["Axis"][$AxisID]["Identity"] = $Identity; } } + + /* Associate one data serie with one axis */ + function setSerieOnAxis($Series,$AxisID) + { + if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } + foreach($Series as $Key => $Serie) + { + $PreviousAxis = $this->Data["Series"][$Serie]["Axis"]; + + /* Create missing axis */ + if ( !isset($this->Data["Axis"][$AxisID] ) ) + { $this->Data["Axis"][$AxisID]["Position"] = AXIS_POSITION_LEFT; $this->Data["Axis"][$AxisID]["Identity"] = AXIS_Y;} + + $this->Data["Series"][$Serie]["Axis"] = $AxisID; + + /* Cleanup unused axis */ + $Found = FALSE; + foreach($this->Data["Series"] as $SerieName => $Values) { if ( $Values["Axis"] == $PreviousAxis ) { $Found = TRUE; } } + if (!$Found) { unset($this->Data["Axis"][$PreviousAxis]); } + } + } + + /* Define if a serie should be draw with ticks */ + function setSerieTicks($Series,$Width=0) + { + if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } + foreach($Series as $Key => $Serie) { if ( isset($this->Data["Series"][$Serie]) ) { $this->Data["Series"][$Serie]["Ticks"] = $Width; } } + } + + /* Define if a serie should be draw with a special weight */ + function setSerieWeight($Series,$Weight=0) + { + if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } + foreach($Series as $Key => $Serie) { if ( isset($this->Data["Series"][$Serie]) ) { $this->Data["Series"][$Serie]["Weight"] = $Weight; } } + } + + /* Returns the palette of the given serie */ + function getSeriePalette($Serie) + { + if ( !isset($this->Data["Series"][$Serie]) ) { return(NULL); } + + $Result = ""; + $Result["R"] = $this->Data["Series"][$Serie]["Color"]["R"]; + $Result["G"] = $this->Data["Series"][$Serie]["Color"]["G"]; + $Result["B"] = $this->Data["Series"][$Serie]["Color"]["B"]; + $Result["Alpha"] = $this->Data["Series"][$Serie]["Color"]["Alpha"]; + + return($Result); + } + + /* Set the color of one serie */ + function setPalette($Series,$Format=NULL) + { + if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } + + foreach($Series as $Key => $Serie) + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + + if ( isset($this->Data["Series"][$Serie]) ) + { + $OldR = $this->Data["Series"][$Serie]["Color"]["R"]; $OldG = $this->Data["Series"][$Serie]["Color"]["G"]; $OldB = $this->Data["Series"][$Serie]["Color"]["B"]; + $this->Data["Series"][$Serie]["Color"]["R"] = $R; + $this->Data["Series"][$Serie]["Color"]["G"] = $G; + $this->Data["Series"][$Serie]["Color"]["B"] = $B; + $this->Data["Series"][$Serie]["Color"]["Alpha"] = $Alpha; + + /* Do reverse processing on the internal palette array */ + foreach ($this->Palette as $Key => $Value) + { if ($Value["R"] == $OldR && $Value["G"] == $OldG && $Value["B"] == $OldB) { $this->Palette[$Key]["R"] = $R; $this->Palette[$Key]["G"] = $G; $this->Palette[$Key]["B"] = $B; $this->Palette[$Key]["Alpha"] = $Alpha;} } + } + } + } + + /* Load a palette file */ + function loadPalette($FileName,$Overwrite=FALSE) + { + if ( !file_exists($FileName) ) { return(-1); } + if ( $Overwrite ) { $this->Palette = ""; } + + $fileHandle = @fopen($FileName, "r"); + if (!$fileHandle) { return(-1); } + while (!feof($fileHandle)) + { + $buffer = fgets($fileHandle, 4096); + if ( preg_match("/,/",$buffer) ) + { + list($R,$G,$B,$Alpha) = preg_split("/,/",$buffer); + if ( $this->Palette == "" ) { $ID = 0; } else { $ID = count($this->Palette); } + $this->Palette[$ID] = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); + } + } + fclose($fileHandle); + + /* Apply changes to current series */ + $ID = 0; + if ( isset($this->Data["Series"])) + { + foreach($this->Data["Series"] as $Key => $Value) + { + if ( !isset($this->Palette[$ID]) ) + $this->Data["Series"][$Key]["Color"] = array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>0); + else + $this->Data["Series"][$Key]["Color"] = $this->Palette[$ID]; + $ID++; + } + } + } + + /* Initialise a given scatter serie */ + function initScatterSerie($ID) + { + if ( isset($this->Data["ScatterSeries"][$ID]) ) { return(0); } + + $this->Data["ScatterSeries"][$ID]["Description"] = "Scatter ".$ID; + $this->Data["ScatterSeries"][$ID]["isDrawable"] = TRUE; + $this->Data["ScatterSeries"][$ID]["Picture"] = NULL; + $this->Data["ScatterSeries"][$ID]["Ticks"] = 0; + $this->Data["ScatterSeries"][$ID]["Weight"] = 0; + + if ( isset($this->Palette[$ID]) ) + $this->Data["ScatterSeries"][$ID]["Color"] = $this->Palette[$ID]; + else + { + $this->Data["ScatterSeries"][$ID]["Color"]["R"] = rand(0,255); + $this->Data["ScatterSeries"][$ID]["Color"]["G"] = rand(0,255); + $this->Data["ScatterSeries"][$ID]["Color"]["B"] = rand(0,255); + $this->Data["ScatterSeries"][$ID]["Color"]["Alpha"] = 100; + } + } + + /* Initialise a given serie */ + function initialise($Serie) + { + if ( isset($this->Data["Series"]) ) { $ID = count($this->Data["Series"]); } else { $ID = 0; } + + $this->Data["Series"][$Serie]["Description"] = $Serie; + $this->Data["Series"][$Serie]["isDrawable"] = TRUE; + $this->Data["Series"][$Serie]["Picture"] = NULL; + $this->Data["Series"][$Serie]["Max"] = NULL; + $this->Data["Series"][$Serie]["Min"] = NULL; + $this->Data["Series"][$Serie]["Axis"] = 0; + $this->Data["Series"][$Serie]["Ticks"] = 0; + $this->Data["Series"][$Serie]["Weight"] = 0; + $this->Data["Series"][$Serie]["Shape"] = SERIE_SHAPE_FILLEDCIRCLE; + + if ( isset($this->Palette[$ID]) ) + $this->Data["Series"][$Serie]["Color"] = $this->Palette[$ID]; + else + { + $this->Data["Series"][$Serie]["Color"]["R"] = rand(0,255); + $this->Data["Series"][$Serie]["Color"]["G"] = rand(0,255); + $this->Data["Series"][$Serie]["Color"]["B"] = rand(0,255); + $this->Data["Series"][$Serie]["Color"]["Alpha"] = 100; + } + } + + function normalize($NormalizationFactor=100,$UnitChange=NULL,$Round=1) + { + $Abscissa = $this->Data["Abscissa"]; + + $SelectedSeries = ""; + $MaxVal = 0; + foreach($this->Data["Axis"] as $AxisID => $Axis) + { + if ( $UnitChange != NULL ) { $this->Data["Axis"][$AxisID]["Unit"] = $UnitChange; } + + foreach($this->Data["Series"] as $SerieName => $Serie) + { + if ($Serie["Axis"] == $AxisID && $Serie["isDrawable"] == TRUE && $SerieName != $Abscissa) + { + $SelectedSeries[$SerieName] = $SerieName; + + if ( count($Serie["Data"] ) > $MaxVal ) { $MaxVal = count($Serie["Data"]); } + } + } + } + + for($i=0;$i<=$MaxVal-1;$i++) + { + $Factor = 0; + foreach ($SelectedSeries as $Key => $SerieName ) + { + $Value = $this->Data["Series"][$SerieName]["Data"][$i]; + if ( $Value != VOID ) + $Factor = $Factor + abs($Value); + } + + if ( $Factor != 0 ) + { + $Factor = $NormalizationFactor / $Factor; + + foreach ($SelectedSeries as $Key => $SerieName ) + { + $Value = $this->Data["Series"][$SerieName]["Data"][$i]; + + if ( $Value != VOID && $Factor != $NormalizationFactor ) + $this->Data["Series"][$SerieName]["Data"][$i] = round(abs($Value)*$Factor,$Round); + elseif ( $Value == VOID || $Value == 0 ) + $this->Data["Series"][$SerieName]["Data"][$i] = VOID; + elseif ( $Factor == $NormalizationFactor ) + $this->Data["Series"][$SerieName]["Data"][$i] = $NormalizationFactor; + } + } + } + + foreach ($SelectedSeries as $Key => $SerieName ) + { + $this->Data["Series"][$SerieName]["Max"] = max($this->stripVOID($this->Data["Series"][$SerieName]["Data"])); + $this->Data["Series"][$SerieName]["Min"] = min($this->stripVOID($this->Data["Series"][$SerieName]["Data"])); + } + } + + /* Load data from a CSV (or similar) data source */ + function importFromCSV($FileName,$Options="") + { + $Delimiter = isset($Options["Delimiter"]) ? $Options["Delimiter"] : ","; + $GotHeader = isset($Options["GotHeader"]) ? $Options["GotHeader"] : FALSE; + $SkipColumns = isset($Options["SkipColumns"]) ? $Options["SkipColumns"] : array(-1); + $DefaultSerieName = isset($Options["DefaultSerieName"]) ? $Options["DefaultSerieName"] : "Serie"; + + $Handle = @fopen($FileName,"r"); + if ($Handle) + { + $HeaderParsed = FALSE; $SerieNames = ""; + while (!feof($Handle)) + { + $Buffer = fgets($Handle, 4096); + $Buffer = str_replace(chr(10),"",$Buffer); + $Buffer = str_replace(chr(13),"",$Buffer); + $Values = preg_split("/".$Delimiter."/",$Buffer); + + if ( $Buffer != "" ) + { + if ( $GotHeader && !$HeaderParsed ) + { + foreach($Values as $Key => $Name) { if ( !in_array($Key,$SkipColumns) ) { $SerieNames[$Key] = $Name; } } + $HeaderParsed = TRUE; + } + else + { + if ($SerieNames == "" ) { foreach($Values as $Key => $Name) { if ( !in_array($Key,$SkipColumns) ) { $SerieNames[$Key] = $DefaultSerieName.$Key; } } } + foreach($Values as $Key => $Value) { if ( !in_array($Key,$SkipColumns) ) { $this->addPoints($Value,$SerieNames[$Key]); } } + } + } + } + fclose($Handle); + } + } + + /* Create a dataset based on a formula */ + function createFunctionSerie($SerieName,$Formula="",$Options="") + { + $MinX = isset($Options["MinX"]) ? $Options["MinX"] : -10; + $MaxX = isset($Options["MaxX"]) ? $Options["MaxX"] : 10; + $XStep = isset($Options["XStep"]) ? $Options["XStep"] : 1; + $AutoDescription = isset($Options["AutoDescription"]) ? $Options["AutoDescription"] : FALSE; + $RecordAbscissa = isset($Options["RecordAbscissa"]) ? $Options["RecordAbscissa"] : FALSE; + $AbscissaSerie = isset($Options["AbscissaSerie"]) ? $Options["AbscissaSerie"] : "Abscissa"; + + if ( $Formula == "" ) { return(0); } + + $Result = ""; $Abscissa = ""; + for($i=$MinX; $i<=$MaxX; $i=$i+$XStep) + { + $Expression = "\$return = '!'.(".str_replace("z",$i,$Formula).");"; + if ( @eval($Expression) === FALSE ) { $return = VOID; } + if ( $return == "!" ) { $return = VOID; } else { $return = $this->right($return,strlen($return)-1); } + if ( $return == "NAN" ) { $return = VOID; } + if ( $return == "INF" ) { $return = VOID; } + if ( $return == "-INF" ) { $return = VOID; } + + $Abscissa[] = $i; + $Result[] = $return; + } + + $this->addPoints($Result,$SerieName); + if ( $AutoDescription ) { $this->setSerieDescription($SerieName,$Formula); } + if ( $RecordAbscissa ) { $this->addPoints($Abscissa,$AbscissaSerie); } + } + + function negateValues($Series) + { + if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); } + foreach($Series as $Key => $SerieName) + { + if (isset($this->Data["Series"][$SerieName])) + { + $Data = ""; + foreach($this->Data["Series"][$SerieName]["Data"] as $Key => $Value) + { if ( $Value == VOID ) { $Data[] = VOID; } else { $Data[] = -$Value; } } + $this->Data["Series"][$SerieName]["Data"] = $Data; + + $this->Data["Series"][$SerieName]["Max"] = max($this->stripVOID($this->Data["Series"][$SerieName]["Data"])); + $this->Data["Series"][$SerieName]["Min"] = min($this->stripVOID($this->Data["Series"][$SerieName]["Data"])); + } + } + } + + /* Return the data & configuration of the series */ + function getData() + { return($this->Data); } + + /* Save a palette element */ + function savePalette($ID,$Color) + { $this->Palette[$ID] = $Color; } + + /* Return the palette of the series */ + function getPalette() + { return($this->Palette); } + + /* Called by the scaling algorithm to save the config */ + function saveAxisConfig($Axis) { $this->Data["Axis"]=$Axis; } + + /* Save the Y Margin if set */ + function saveYMargin($Value) { $this->Data["YMargin"]=$Value; } + + /* Save extended configuration to the pData object */ + function saveExtendedData($Tag,$Values) { $this->Data["Extended"][$Tag]=$Values; } + + /* Called by the scaling algorithm to save the orientation of the scale */ + function saveOrientation($Orientation) { $this->Data["Orientation"]=$Orientation; } + + /* Convert a string to a single elements array */ + function convertToArray($Value) + { $Values = ""; $Values[] = $Value; return($Values); } + + /* Class string wrapper */ + function __toString() + { return("pData object."); } + + function left($value,$NbChar) { return substr($value,0,$NbChar); } + function right($value,$NbChar) { return substr($value,strlen($value)-$NbChar,$NbChar); } + function mid($value,$Depart,$NbChar) { return substr($value,$Depart-1,$NbChar); } + } ?> \ No newline at end of file diff --git a/www/libs/pChart2.1.3/class/pDraw.class.php b/www/libs/pChart2.1.4/class/pDraw.class.php similarity index 96% rename from www/libs/pChart2.1.3/class/pDraw.class.php rename to www/libs/pChart2.1.4/class/pDraw.class.php index a5a41cb8..2e75aeb7 100644 --- a/www/libs/pChart2.1.3/class/pDraw.class.php +++ b/www/libs/pChart2.1.4/class/pDraw.class.php @@ -1,6193 +1,6216 @@ -DataSet->getData(); - - foreach($Data["Series"] as $SerieName => $Serie) - { if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) { $Results++; } } - - return($Results); - } - - /* Fix box coordinates */ - function fixBoxCoordinates($Xa,$Ya,$Xb,$Yb) - { - $X1 = min($Xa,$Xb); $Y1 = min($Ya,$Yb); - $X2 = max($Xa,$Xb); $Y2 = max($Ya,$Yb); - - return(array($X1,$Y1,$X2,$Y2)); - } - - /* Draw a polygon */ - function drawPolygon($Points,$Format="") - { - $R = isset($Format["R"]) ? $Format["R"] : 0; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $NoFill = isset($Format["NoFill"]) ? $Format["NoFill"] : FALSE; - $NoBorder = isset($Format["NoBorder"]) ? $Format["NoBorder"] : FALSE; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B; - $BorderAlpha = isset($Format["Alpha"]) ? $Format["Alpha"] : $Alpha / 2; - $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; - $SkipX = isset($Format["SkipX"]) ? $Format["SkipX"] : OUT_OF_SIGHT; - $SkipY = isset($Format["SkipY"]) ? $Format["SkipY"] : OUT_OF_SIGHT; - - /* Calling the ImageFilledPolygon() function over the $Points array will round it */ - $Backup = $Points; - - if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; } - - if ( $SkipX != OUT_OF_SIGHT ) { $SkipX = floor($SkipX); } - if ( $SkipY != OUT_OF_SIGHT ) { $SkipY = floor($SkipY); } - - $RestoreShadow = $this->Shadow; - if ( !$NoFill ) - { - if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) - { - $this->Shadow = FALSE; - for($i=0;$i<=count($Points)-1;$i=$i+2) - { $Shadow[] = $Points[$i] + $this->ShadowX; $Shadow[] = $Points[$i+1] + $this->ShadowY; } - $this->drawPolygon($Shadow,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa,"NoBorder"=>TRUE)); - } - - $FillColor = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); - - if ( count($Points) >= 6 ) - { ImageFilledPolygon($this->Picture,$Points,count($Points)/2,$FillColor); } - } - - if ( !$NoBorder ) - { - $Points = $Backup; - - if ( $NoFill ) - $BorderSettings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); - else - $BorderSettings = array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha); - - for($i=0;$i<=count($Points)-1;$i=$i+2) - { - if ( isset($Points[$i+2]) ) - { - if ( !($Points[$i] == $Points[$i+2] && $Points[$i] == $SkipX ) && !($Points[$i+1] == $Points[$i+3] && $Points[$i+1] == $SkipY ) ) - $this->drawLine($Points[$i],$Points[$i+1],$Points[$i+2],$Points[$i+3],$BorderSettings); - } - else - { - if ( !($Points[$i] == $Points[0] && $Points[$i] == $SkipX ) && !($Points[$i+1] == $Points[1] && $Points[$i+1] == $SkipY ) ) - $this->drawLine($Points[$i],$Points[$i+1],$Points[0],$Points[1],$BorderSettings); - } - } - } - - $this->Shadow = $RestoreShadow; - } - - /* Apply AALias correction to the rounded box boundaries */ - function offsetCorrection($Value,$Mode) - { - $Value = round($Value,1); - - if ( $Value == 0 && $Mode == 1 ) { return(.9); } - if ( $Value == 0 ) { return(0); } - - if ( $Mode == 1) - { if ( $Value == 1 ) { return(.9); }; if ( $Value == .1 ) { return(.9); }; if ( $Value == .2 ) { return(.8); }; if ( $Value == .3 ) { return(.8); }; if ( $Value == .4 ) { return(.7); }; if ( $Value == .5 ) { return(.5); }; if ( $Value == .6 ) { return(.8); }; if ( $Value == .7 ) { return(.7); }; if ( $Value == .8 ) { return(.6); }; if ( $Value == .9 ) { return(.9); }; } - - if ( $Mode == 2) - { if ( $Value == 1 ) { return(.9); }; if ( $Value == .1 ) { return(.1); }; if ( $Value == .2 ) { return(.2); }; if ( $Value == .3 ) { return(.3); }; if ( $Value == .4 ) { return(.4); }; if ( $Value == .5 ) { return(.5); }; if ( $Value == .6 ) { return(.8); }; if ( $Value == .7 ) { return(.7); }; if ( $Value == .8 ) { return(.8); }; if ( $Value == .9 ) { return(.9); }; } - - if ( $Mode == 3) - { if ( $Value == 1 ) { return(.1); }; if ( $Value == .1 ) { return(.1); }; if ( $Value == .2 ) { return(.2); }; if ( $Value == .3 ) { return(.3); }; if ( $Value == .4 ) { return(.4); }; if ( $Value == .5 ) { return(.9); }; if ( $Value == .6 ) { return(.6); }; if ( $Value == .7 ) { return(.7); }; if ( $Value == .8 ) { return(.4); }; if ( $Value == .9 ) { return(.5); }; } - - if ( $Mode == 4) - { if ( $Value == 1 ) { return(-1); }; if ( $Value == .1 ) { return(.1); }; if ( $Value == .2 ) { return(.2); }; if ( $Value == .3 ) { return(.3); }; if ( $Value == .4 ) { return(.1); }; if ( $Value == .5 ) { return(-.1); }; if ( $Value == .6 ) { return(.8); }; if ( $Value == .7 ) { return(.1); }; if ( $Value == .8 ) { return(.1); }; if ( $Value == .9 ) { return(.1); }; } - } - - /* Draw a rectangle with rounded corners */ - function drawRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,$Format="") - { - $R = isset($Format["R"]) ? $Format["R"] : 0; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - - list($X1,$Y1,$X2,$Y2) = $this->fixBoxCoordinates($X1,$Y1,$X2,$Y2); - - if ( $X2 - $X1 < $Radius ) { $Radius = floor((($X2-$X1))/2); } - if ( $Y2 - $Y1 < $Radius ) { $Radius = floor((($Y2-$Y1))/2); } - - $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"NoBorder"=>TRUE); - - if ( $Radius <= 0 ) { $this->drawRectangle($X1,$Y1,$X2,$Y2,$Color); return(0); } - - if ( $this->Antialias ) - { - $this->drawLine($X1+$Radius,$Y1,$X2-$Radius,$Y1,$Color); - $this->drawLine($X2,$Y1+$Radius,$X2,$Y2-$Radius,$Color); - $this->drawLine($X2-$Radius,$Y2,$X1+$Radius,$Y2,$Color); - $this->drawLine($X1,$Y1+$Radius,$X1,$Y2-$Radius,$Color); - } - else - { - $Color = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); - imageline($this->Picture,$X1+$Radius,$Y1,$X2-$Radius,$Y1,$Color); - imageline($this->Picture,$X2,$Y1+$Radius,$X2,$Y2-$Radius,$Color); - imageline($this->Picture,$X2-$Radius,$Y2,$X1+$Radius,$Y2,$Color); - imageline($this->Picture,$X1,$Y1+$Radius,$X1,$Y2-$Radius,$Color); - } - - $Step = 360 / (2 * PI * $Radius); - for($i=0;$i<=90;$i=$i+$Step) - { - $X = cos(($i+180)*PI/180) * $Radius + $X1 + $Radius; - $Y = sin(($i+180)*PI/180) * $Radius + $Y1 + $Radius; - $this->drawAntialiasPixel($X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); - - $X = cos(($i+90)*PI/180) * $Radius + $X1 + $Radius; - $Y = sin(($i+90)*PI/180) * $Radius + $Y2 - $Radius; - $this->drawAntialiasPixel($X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); - - $X = cos($i*PI/180) * $Radius + $X2 - $Radius; - $Y = sin($i*PI/180) * $Radius + $Y2 - $Radius; - $this->drawAntialiasPixel($X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); - - $X = cos(($i+270)*PI/180) * $Radius + $X2 - $Radius; - $Y = sin(($i+270)*PI/180) * $Radius + $Y1 + $Radius; - $this->drawAntialiasPixel($X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); - } - } - - /* Draw a rectangle with rounded corners */ - function drawRoundedFilledRectangle($X1,$Y1,$X2,$Y2,$Radius,$Format="") - { - $R = isset($Format["R"]) ? $Format["R"] : 0; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; - - /* Temporary fix for AA issue */ - $Y1 = floor($Y1); $Y2 = floor($Y2); $X1 = floor($X1); $X2 = floor($X2); - - if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; } - if ( $BorderR == -1 ) { $BorderR = $R; $BorderG = $G; $BorderB = $B; } - - list($X1,$Y1,$X2,$Y2) = $this->fixBoxCoordinates($X1,$Y1,$X2,$Y2); - - if ( $X2 - $X1 < $Radius*2 ) { $Radius = floor((($X2-$X1))/4); } - if ( $Y2 - $Y1 < $Radius*2 ) { $Radius = floor((($Y2-$Y1))/4); } - - $RestoreShadow = $this->Shadow; - if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) - { - $this->Shadow = FALSE; - $this->drawRoundedFilledRectangle($X1+$this->ShadowX,$Y1+$this->ShadowY,$X2+$this->ShadowX,$Y2+$this->ShadowY,$Radius,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa)); - } - - $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"NoBorder"=>TRUE); - - if ( $Radius <= 0 ) { $this->drawFilledRectangle($X1,$Y1,$X2,$Y2,$Color); return(0); } - - $YTop = $Y1+$Radius; - $YBottom = $Y2-$Radius; - - $Step = 360 / (2 * PI * $Radius); - $Positions = ""; $Radius--; $MinY = ""; $MaxY = ""; - for($i=0;$i<=90;$i=$i+$Step) - { - $Xp1 = cos(($i+180)*PI/180) * $Radius + $X1 + $Radius; - $Xp2 = cos(((90-$i)+270)*PI/180) * $Radius + $X2 - $Radius; - $Yp = floor(sin(($i+180)*PI/180) * $Radius + $YTop); - if ( $MinY == "" || $Yp > $MinY ) { $MinY = $Yp; } - - if ( $Xp1 <= floor($X1) ) { $Xp1++; } - if ( $Xp2 >= floor($X2) ) { $Xp2--; } - $Xp1++; - - if ( !isset($Positions[$Yp]) ) - { $Positions[$Yp]["X1"] = $Xp1; $Positions[$Yp]["X2"] = $Xp2; } - else - { $Positions[$Yp]["X1"] = ($Positions[$Yp]["X1"]+$Xp1)/2; $Positions[$Yp]["X2"] = ($Positions[$Yp]["X2"]+$Xp2)/2; } - - $Xp1 = cos(($i+90)*PI/180) * $Radius + $X1 + $Radius; - $Xp2 = cos((90-$i)*PI/180) * $Radius + $X2 - $Radius; - $Yp = floor(sin(($i+90)*PI/180) * $Radius + $YBottom); - if ( $MaxY == "" || $Yp < $MaxY ) { $MaxY = $Yp; } - - if ( $Xp1 <= floor($X1) ) { $Xp1++; } - if ( $Xp2 >= floor($X2) ) { $Xp2--; } - $Xp1++; - - if ( !isset($Positions[$Yp]) ) - { $Positions[$Yp]["X1"] = $Xp1; $Positions[$Yp]["X2"] = $Xp2; } - else - { $Positions[$Yp]["X1"] = ($Positions[$Yp]["X1"]+$Xp1)/2; $Positions[$Yp]["X2"] = ($Positions[$Yp]["X2"]+$Xp2)/2; } - } - - $ManualColor = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); - foreach($Positions as $Yp => $Bounds) - { - $X1 = $Bounds["X1"]; $X1Dec = $this->getFirstDecimal($X1); if ( $X1Dec != 0 ) { $X1 = floor($X1)+1; } - $X2 = $Bounds["X2"]; $X2Dec = $this->getFirstDecimal($X2); if ( $X2Dec != 0 ) { $X2 = floor($X2)-1; } - imageline($this->Picture,$X1,$Yp,$X2,$Yp,$ManualColor); - } - $this->drawFilledRectangle($X1,$MinY+1,floor($X2),$MaxY-1,$Color); - - $Radius++; - $this->drawRoundedRectangle($X1,$Y1,$X2+1,$Y2-1,$Radius,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha)); - - $this->Shadow = $RestoreShadow; - } - - /* Draw a rectangle with rounded corners */ - function drawRoundedFilledRectangle_deprecated($X1,$Y1,$X2,$Y2,$Radius,$Format="") - { - $R = isset($Format["R"]) ? $Format["R"] : 0; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; - - if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; } - if ( $BorderR == -1 ) { $BorderR = $R; $BorderG = $G; $BorderB = $B; } - - list($X1,$Y1,$X2,$Y2) = $this->fixBoxCoordinates($X1,$Y1,$X2,$Y2); - - if ( $X2 - $X1 < $Radius ) { $Radius = floor((($X2-$X1)+2)/2); } - if ( $Y2 - $Y1 < $Radius ) { $Radius = floor((($Y2-$Y1)+2)/2); } - - $RestoreShadow = $this->Shadow; - if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) - { - $this->Shadow = FALSE; - $this->drawRoundedFilledRectangle($X1+$this->ShadowX,$Y1+$this->ShadowY,$X2+$this->ShadowX,$Y2+$this->ShadowY,$Radius,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa)); - } - - if ( $this->getFirstDecimal($X2) >= 5 ) { $XOffset2 = 1; } else { $XOffset2 = 0; } - if ( $this->getFirstDecimal($X1) <= 5 ) { $XOffset1 = 1; } else { $XOffset1 = 0; } - - if ( !$this->Antialias ) { $XOffset1 = 1; $XOffset2 = 1; } - - $YTop = floor($Y1+$Radius); - $YBottom = floor($Y2-$Radius); - - $this->drawFilledRectangle($X1-$XOffset1,$YTop,$X2+$XOffset2,$YBottom,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"NoBorder"=>TRUE)); - - $Step = 360 / (2 * PI * $Radius); - $Color = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); - $Color2 = $this->allocateColor($this->Picture,255,0,0,$Alpha); - $Drawn = ""; - - if ( $Alpha < 100 ) { $Drawn[$YTop] = FALSE; } - if ( $Alpha < 100 ) { $Drawn[$YBottom] = TRUE; } - - for($i=0;$i<=90;$i=$i+$Step) - { - $Xp1 = cos(($i+180)*PI/180) * $Radius + $X1 + $Radius; - $Xp2 = cos(((90-$i)+270)*PI/180) * $Radius + $X2 - $Radius; - $Yp = sin(($i+180)*PI/180) * $Radius + $YTop; - - if ( $this->getFirstDecimal($Xp1) > 5 ) { $XOffset1 = 1; } else { $XOffset1 = 0; } - if ( $this->getFirstDecimal($Xp2) > 5 ) { $XOffset2 = 1; } else { $XOffset2 = 0; } - if ( $this->getFirstDecimal($Yp) > 5 ) { $YOffset = 1; } else { $YOffset = 0; } - - if ( !isset($Drawn[$Yp+$YOffset]) || $Alpha == 100 ) - imageline($this->Picture,$Xp1+$XOffset1,$Yp+$YOffset,$Xp2+$XOffset2,$Yp+$YOffset,$Color); - - $Drawn[$Yp+$YOffset] = $Xp2; - - $Xp1 = cos(($i+90)*PI/180) * $Radius + $X1 + $Radius; - $Xp2 = cos((90-$i)*PI/180) * $Radius + $X2 - $Radius; - $Yp = sin(($i+90)*PI/180) * $Radius + $YBottom; - - if ( $this->getFirstDecimal($Xp1) > 7 ) { $XOffset1 = 1; } else { $XOffset1 = 0; } - if ( $this->getFirstDecimal($Xp2) > 7 ) { $XOffset2 = 1; } else { $XOffset2 = 0; } - if ( $this->getFirstDecimal($Yp) > 5 ) { $YOffset = 1; } else { $YOffset = 0; } - - if ( !isset($Drawn[$Yp+$YOffset]) || $Alpha == 100 ) - imageline($this->Picture,$Xp1+$XOffset1,$Yp+$YOffset,$Xp2+$XOffset2,$Yp+$YOffset,$Color); - - $Drawn[$Yp+$YOffset] = $Xp2; - } - - $this->drawRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha)); - - $this->Shadow = $RestoreShadow; - } - - /* Draw a rectangle */ - function drawRectangle($X1,$Y1,$X2,$Y2,$Format="") - { - $R = isset($Format["R"]) ? $Format["R"] : 0; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL; - $NoAngle = isset($Format["NoAngle"]) ? $Format["NoAngle"] : FALSE; - - if ($X1 > $X2) { list($X1, $X2) = array($X2, $X1); } - if ($Y1 > $Y2) { list($Y1, $Y2) = array($Y2, $Y1); } - - if ( $this->Antialias ) - { - if ( $NoAngle ) - { - $this->drawLine($X1+1,$Y1,$X2-1,$Y1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); - $this->drawLine($X2,$Y1+1,$X2,$Y2-1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); - $this->drawLine($X2-1,$Y2,$X1+1,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); - $this->drawLine($X1,$Y1+1,$X1,$Y2-1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); - } - else - { - $this->drawLine($X1+1,$Y1,$X2-1,$Y1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); - $this->drawLine($X2,$Y1,$X2,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); - $this->drawLine($X2-1,$Y2,$X1+1,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); - $this->drawLine($X1,$Y1,$X1,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); - } - } - else - { - $Color = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); - imagerectangle($this->Picture,$X1,$Y1,$X2,$Y2,$Color); - } - } - - /* Draw a filled rectangle */ - function drawFilledRectangle($X1,$Y1,$X2,$Y2,$Format="") - { - $R = isset($Format["R"]) ? $Format["R"] : 0; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1; - $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha; - $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; - $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL; - $NoAngle = isset($Format["NoAngle"]) ? $Format["NoAngle"] : NULL; - $Dash = isset($Format["Dash"]) ? $Format["Dash"] : FALSE; - $DashStep = isset($Format["DashStep"]) ? $Format["DashStep"] : 4; - $DashR = isset($Format["DashR"]) ? $Format["DashR"] : 0; - $DashG = isset($Format["DashG"]) ? $Format["DashG"] : 0; - $DashB = isset($Format["DashB"]) ? $Format["DashB"] : 0; - $NoBorder = isset($Format["NoBorder"]) ? $Format["NoBorder"] : FALSE; - - if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; } - - if ($X1 > $X2) { list($X1, $X2) = array($X2, $X1); } - if ($Y1 > $Y2) { list($Y1, $Y2) = array($Y2, $Y1); } - - $RestoreShadow = $this->Shadow; - if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) - { - $this->Shadow = FALSE; - $this->drawFilledRectangle($X1+$this->ShadowX,$Y1+$this->ShadowY,$X2+$this->ShadowX,$Y2+$this->ShadowY,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa,"Ticks"=>$Ticks,"NoAngle"=>$NoAngle)); - } - - $Color = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); - if ( $NoAngle ) - { - imagefilledrectangle($this->Picture,ceil($X1)+1,ceil($Y1),floor($X2)-1,floor($Y2),$Color); - imageline($this->Picture,ceil($X1),ceil($Y1)+1,ceil($X1),floor($Y2)-1,$Color); - imageline($this->Picture,floor($X2),ceil($Y1)+1,floor($X2),floor($Y2)-1,$Color); - } - else - imagefilledrectangle($this->Picture,ceil($X1),ceil($Y1),floor($X2),floor($Y2),$Color); - - if ( $Dash ) - { - if ( $BorderR != -1 ) { $iX1=$X1+1; $iY1=$Y1+1; $iX2=$X2-1; $iY2=$Y2-1; } else { $iX1=$X1; $iY1=$Y1; $iX2=$X2; $iY2=$Y2; } - - $Color = $this->allocateColor($this->Picture,$DashR,$DashG,$DashB,$Alpha); - $Y=$iY1-$DashStep; - for($X=$iX1; $X<=$iX2+($iY2-$iY1); $X=$X+$DashStep) - { - $Y=$Y+$DashStep; - if ( $X > $iX2 ) { $Xa = $X-($X-$iX2); $Ya = $iY1+($X-$iX2); } else { $Xa = $X; $Ya = $iY1; } - if ( $Y > $iY2 ) { $Xb = $iX1+($Y-$iY2); $Yb = $Y-($Y-$iY2); } else { $Xb = $iX1; $Yb = $Y; } - imageline($this->Picture,$Xa,$Ya,$Xb,$Yb,$Color); - } - } - - if ( $this->Antialias && !$NoBorder ) - { - if ( $X1 < ceil($X1) ) - { - $AlphaA = $Alpha * (ceil($X1) - $X1); - $Color = $this->allocateColor($this->Picture,$R,$G,$B,$AlphaA); - imageline($this->Picture,ceil($X1)-1,ceil($Y1),ceil($X1)-1,floor($Y2),$Color); - } - - if ( $Y1 < ceil($Y1) ) - { - $AlphaA = $Alpha * (ceil($Y1) - $Y1); - $Color = $this->allocateColor($this->Picture,$R,$G,$B,$AlphaA); - imageline($this->Picture,ceil($X1),ceil($Y1)-1,floor($X2),ceil($Y1)-1,$Color); - } - - if ( $X2 > floor($X2) ) - { - $AlphaA = $Alpha * (.5-($X2 - floor($X2))); - $Color = $this->allocateColor($this->Picture,$R,$G,$B,$AlphaA); - imageline($this->Picture,floor($X2)+1,ceil($Y1),floor($X2)+1,floor($Y2),$Color); - } - - if ( $Y2 > floor($Y2) ) - { - $AlphaA = $Alpha * (.5-($Y2 - floor($Y2))); - $Color = $this->allocateColor($this->Picture,$R,$G,$B,$AlphaA); - imageline($this->Picture,ceil($X1),floor($Y2)+1,floor($X2),floor($Y2)+1,$Color); - } - } - - if ( $BorderR != -1 ) - $this->drawRectangle($X1,$Y1,$X2,$Y2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$Ticks,"NoAngle"=>$NoAngle)); - - $this->Shadow = $RestoreShadow; - } - - /* Draw a rectangular marker of the specified size */ - function drawRectangleMarker($X,$Y,$Format="") - { - $Size = isset($Format["Size"]) ? $Format["Size"] : 4; - - $HalfSize = floor($Size/2); - $this->drawFilledRectangle($X-$HalfSize,$Y-$HalfSize,$X+$HalfSize,$Y+$HalfSize,$Format); - } - - /* Drawn a spline based on the bezier function */ - function drawSpline($Coordinates,$Format="") - { - $R = isset($Format["R"]) ? $Format["R"] : 0; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $Force = isset($Format["Force"]) ? $Format["Force"] : 30; - $Forces = isset($Format["Forces"]) ? $Format["Forces"] : NULL; - $ShowC = isset($Format["ShowControl"]) ? $Format["ShowControl"] : FALSE; - $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL; - $PathOnly = isset($Format["PathOnly"]) ? $Format["PathOnly"] : FALSE; - $Weight = isset($Format["Weight"]) ? $Format["Weight"] : NULL; - - $Cpt = NULL; $Mode = NULL; $Result = ""; - for($i=1;$i<=count($Coordinates)-1;$i++) - { - $X1 = $Coordinates[$i-1][0]; $Y1 = $Coordinates[$i-1][1]; - $X2 = $Coordinates[$i][0]; $Y2 = $Coordinates[$i][1]; - - if ( $Forces != NULL ) { $Force = $Forces[$i]; } - - /* First segment */ - if ( $i == 1 ) - { $Xv1 = $X1; $Yv1 = $Y1; } - else - { - $Angle1 = $this->getAngle($XLast,$YLast,$X1,$Y1); - $Angle2 = $this->getAngle($X1,$Y1,$X2,$Y2); - $XOff = cos($Angle2 * PI / 180) * $Force + $X1; - $YOff = sin($Angle2 * PI / 180) * $Force + $Y1; - - $Xv1 = cos($Angle1 * PI / 180) * $Force + $XOff; - $Yv1 = sin($Angle1 * PI / 180) * $Force + $YOff; - } - - /* Last segment */ - if ( $i == count($Coordinates)-1 ) - { $Xv2 = $X2; $Yv2 = $Y2; } - else - { - $Angle1 = $this->getAngle($X2,$Y2,$Coordinates[$i+1][0],$Coordinates[$i+1][1]); - $Angle2 = $this->getAngle($X1,$Y1,$X2,$Y2); - $XOff = cos(($Angle2+180) * PI / 180) * $Force + $X2; - $YOff = sin(($Angle2+180) * PI / 180) * $Force + $Y2; - - $Xv2 = cos(($Angle1+180) * PI / 180) * $Force + $XOff; - $Yv2 = sin(($Angle1+180) * PI / 180) * $Force + $YOff; - } - - $Path = $this->drawBezier($X1,$Y1,$X2,$Y2,$Xv1,$Yv1,$Xv2,$Yv2,$Format); - if ($PathOnly) { $Result[] = $Path; } - - $XLast = $X1; $YLast = $Y1; - } - - return($Result); - } - - /* Draw a bezier curve with two controls points */ - function drawBezier($X1,$Y1,$X2,$Y2,$Xv1,$Yv1,$Xv2,$Yv2,$Format="") - { - $R = isset($Format["R"]) ? $Format["R"] : 0; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $ShowC = isset($Format["ShowControl"]) ? $Format["ShowControl"] : FALSE; - $Segments = isset($Format["Segments"]) ? $Format["Segments"] : NULL; - $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL; - $NoDraw = isset($Format["NoDraw"]) ? $Format["NoDraw"] : FALSE; - $PathOnly = isset($Format["PathOnly"]) ? $Format["PathOnly"] : FALSE; - $Weight = isset($Format["Weight"]) ? $Format["Weight"] : NULL; - $DrawArrow = isset($Format["DrawArrow"]) ? $Format["DrawArrow"] : FALSE; - $ArrowSize = isset($Format["ArrowSize"]) ? $Format["ArrowSize"] : 10; - $ArrowRatio = isset($Format["ArrowRatio"]) ? $Format["ArrowRatio"] : .5; - $ArrowTwoHeads = isset($Format["ArrowTwoHeads"]) ? $Format["ArrowTwoHeads"] : FALSE; - - if ( $Segments == NULL ) - { - $Length = $this->getLength($X1,$Y1,$X2,$Y2); - $Precision = ($Length*125)/1000; - } - else - $Precision = $Segments; - - $P[0]["X"] = $X1; $P[0]["Y"] = $Y1; - $P[1]["X"] = $Xv1; $P[1]["Y"] = $Yv1; - $P[2]["X"] = $Xv2; $P[2]["Y"] = $Yv2; - $P[3]["X"] = $X2; $P[3]["Y"] = $Y2; - - /* Compute the bezier points */ - $Q = ""; $ID = 0; $Path = ""; - for($i=0;$i<=$Precision;$i=$i+1) - { - $u = $i / $Precision; - - $C = ""; - $C[0] = (1 - $u) * (1 - $u) * (1 - $u); - $C[1] = ($u * 3) * (1 - $u) * (1 - $u); - $C[2] = 3 * $u * $u * (1 - $u); - $C[3] = $u * $u * $u; - - for($j=0;$j<=3;$j++) - { - if ( !isset($Q[$ID]) ) { $Q[$ID] = ""; } - if ( !isset($Q[$ID]["X"]) ) { $Q[$ID]["X"] = 0; } - if ( !isset($Q[$ID]["Y"]) ) { $Q[$ID]["Y"] = 0; } - - $Q[$ID]["X"] = $Q[$ID]["X"] + $P[$j]["X"] * $C[$j]; - $Q[$ID]["Y"] = $Q[$ID]["Y"] + $P[$j]["Y"] * $C[$j]; - } - $ID++; - } - $Q[$ID]["X"] = $X2; $Q[$ID]["Y"] = $Y2; - - if ( !$NoDraw ) - { - /* Display the control points */ - if ( $ShowC && !$PathOnly ) - { - $Xv1 = floor($Xv1); $Yv1 = floor($Yv1); $Xv2 = floor($Xv2); $Yv2 = floor($Yv2); - - $this->drawLine($X1,$Y1,$X2,$Y2,array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>30)); - - $MyMarkerSettings = array("R"=>255,"G"=>0,"B"=>0,"BorderR"=>255,"BorderB"=>255,"BorderG"=>255,"Size"=>4); - $this->drawRectangleMarker($Xv1,$Yv1,$MyMarkerSettings); - $this->drawText($Xv1+4,$Yv1,"v1"); - $MyMarkerSettings = array("R"=>0,"G"=>0,"B"=>255,"BorderR"=>255,"BorderB"=>255,"BorderG"=>255,"Size"=>4); - $this->drawRectangleMarker($Xv2,$Yv2,$MyMarkerSettings); - $this->drawText($Xv2+4,$Yv2,"v2"); - } - - /* Draw the bezier */ - $LastX = NULL; $LastY = NULL; $Cpt = NULL; $Mode = NULL; $ArrowS = NULL; - foreach ($Q as $Key => $Point) - { - $X = $Point["X"]; $Y = $Point["Y"]; - - /* Get the first segment */ - if ( $ArrowS == NULL && $LastX != NULL && $LastY != NULL ) - { $ArrowS["X2"] = $LastX; $ArrowS["Y2"] = $LastY; $ArrowS["X1"] = $X; $ArrowS["Y1"] = $Y; } - - if ( $LastX != NULL && $LastY != NULL && !$PathOnly) - list($Cpt,$Mode) = $this->drawLine($LastX,$LastY,$X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Cpt"=>$Cpt,"Mode"=>$Mode,"Weight"=>$Weight)); - - /* Get the last segment */ - $ArrowE["X1"] = $LastX; $ArrowE["Y1"] = $LastY; $ArrowE["X2"] = $X; $ArrowE["Y2"] = $Y; - - $LastX = $X; $LastY = $Y; - } - - if ( $DrawArrow && !$PathOnly ) - { - $ArrowSettings = array("FillR"=>$R,"FillG"=>$G,"FillB"=>$B,"Alpha"=>$Alpha,"Size"=>$ArrowSize,"Ratio"=>$ArrowRatio); - if ( $ArrowTwoHeads ) - $this->drawArrow($ArrowS["X1"],$ArrowS["Y1"],$ArrowS["X2"],$ArrowS["Y2"],$ArrowSettings); - - $this->drawArrow($ArrowE["X1"],$ArrowE["Y1"],$ArrowE["X2"],$ArrowE["Y2"],$ArrowSettings); - } - } - return($Q); - } - - /* Draw a line between two points */ - function drawLine($X1,$Y1,$X2,$Y2,$Format="") - { - $R = isset($Format["R"]) ? $Format["R"] : 0; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL; - $Cpt = isset($Format["Cpt"]) ? $Format["Cpt"] : 1; - $Mode = isset($Format["Mode"]) ? $Format["Mode"] : 1; - $Weight = isset($Format["Weight"]) ? $Format["Weight"] : NULL; - $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : NULL; - - if ( $this->Antialias == FALSE && $Ticks == NULL ) - { - if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) - { - $ShadowColor = $this->allocateColor($this->Picture,$this->ShadowR,$this->ShadowG,$this->ShadowB,$this->Shadowa); - imageline($this->Picture,$X1+$this->ShadowX,$Y1+$this->ShadowY,$X2+$this->ShadowX,$Y2+$this->ShadowY,$ShadowColor); - } - - $Color = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); - imageline($this->Picture,$X1,$Y1,$X2,$Y2,$Color); - return(0); - } - - $Distance = sqrt(($X2-$X1)*($X2-$X1)+($Y2-$Y1)*($Y2-$Y1)); - if ( $Distance == 0 ) { return(-1); } - - /* Derivative algorithm for overweighted lines, re-route to polygons primitives */ - if ( $Weight != NULL ) - { - $Angle = $this->getAngle($X1,$Y1,$X2,$Y2); - $PolySettings = array ("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderAlpha"=>$Alpha); - - if ( $Ticks == NULL ) - { - $Points = ""; - $Points[] = cos(deg2rad($Angle-90)) * $Weight + $X1; $Points[] = sin(deg2rad($Angle-90)) * $Weight + $Y1; - $Points[] = cos(deg2rad($Angle+90)) * $Weight + $X1; $Points[] = sin(deg2rad($Angle+90)) * $Weight + $Y1; - $Points[] = cos(deg2rad($Angle+90)) * $Weight + $X2; $Points[] = sin(deg2rad($Angle+90)) * $Weight + $Y2; - $Points[] = cos(deg2rad($Angle-90)) * $Weight + $X2; $Points[] = sin(deg2rad($Angle-90)) * $Weight + $Y2; - - $this->drawPolygon($Points,$PolySettings); - } - else - { - for($i=0;$i<=$Distance;$i=$i+$Ticks*2) - { - $Xa = (($X2-$X1)/$Distance) * $i + $X1; $Ya = (($Y2-$Y1)/$Distance) * $i + $Y1; - $Xb = (($X2-$X1)/$Distance) * ($i+$Ticks) + $X1; $Yb = (($Y2-$Y1)/$Distance) * ($i+$Ticks) + $Y1; - - $Points = ""; - $Points[] = cos(deg2rad($Angle-90)) * $Weight + $Xa; $Points[] = sin(deg2rad($Angle-90)) * $Weight + $Ya; - $Points[] = cos(deg2rad($Angle+90)) * $Weight + $Xa; $Points[] = sin(deg2rad($Angle+90)) * $Weight + $Ya; - $Points[] = cos(deg2rad($Angle+90)) * $Weight + $Xb; $Points[] = sin(deg2rad($Angle+90)) * $Weight + $Yb; - $Points[] = cos(deg2rad($Angle-90)) * $Weight + $Xb; $Points[] = sin(deg2rad($Angle-90)) * $Weight + $Yb; - - $this->drawPolygon($Points,$PolySettings); - } - } - - return(1); - } - - $XStep = ($X2-$X1) / $Distance; - $YStep = ($Y2-$Y1) / $Distance; - - for($i=0;$i<=$Distance;$i++) - { - $X = $i * $XStep + $X1; - $Y = $i * $YStep + $Y1; - - $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); - - if ( $Threshold != NULL ) - { - foreach($Threshold as $Key => $Parameters) - { - if ( $Y <= $Parameters["MinX"] && $Y >= $Parameters["MaxX"]) - { - if ( isset($Parameters["R"]) ) { $RT = $Parameters["R"]; } else { $RT = 0; } - if ( isset($Parameters["G"]) ) { $GT = $Parameters["G"]; } else { $GT = 0; } - if ( isset($Parameters["B"]) ) { $BT = $Parameters["B"]; } else { $BT = 0; } - if ( isset($Parameters["Alpha"]) ) { $AlphaT = $Parameters["Alpha"]; } else { $AlphaT = 0; } - $Color = array("R"=>$RT,"G"=>$GT,"B"=>$BT,"Alpha"=>$AlphaT); - } - } - } - - if ( $Ticks != NULL ) - { - if ( $Cpt % $Ticks == 0 ) - { $Cpt = 0; if ( $Mode == 1 ) { $Mode = 0; } else { $Mode = 1; } } - - if ( $Mode == 1 ) - $this->drawAntialiasPixel($X,$Y,$Color); - - $Cpt++; - } - else - $this->drawAntialiasPixel($X,$Y,$Color); - } - - return(array($Cpt,$Mode)); - } - - /* Draw a circle */ - function drawCircle($Xc,$Yc,$Height,$Width,$Format="") - { - $R = isset($Format["R"]) ? $Format["R"] : 0; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL; - - $Height = abs($Height); - $Width = abs($Width); - - if ( $Height == 0 ) { $Height = 1; } - if ( $Width == 0 ) { $Width = 1; } - $Xc = floor($Xc); $Yc = floor($Yc); - - $RestoreShadow = $this->Shadow; - if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) - { - $this->Shadow = FALSE; - $this->drawCircle($Xc+$this->ShadowX,$Yc+$this->ShadowY,$Height,$Width,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa,"Ticks"=>$Ticks)); - } - - if ( $Width == 0 ) { $Width = $Height; } - if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } - if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } - if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } - - $Step = 360 / (2 * PI * max($Width,$Height)); - $Mode = 1; $Cpt = 1; - for($i=0;$i<=360;$i=$i+$Step) - { - $X = cos($i*PI/180) * $Height + $Xc; - $Y = sin($i*PI/180) * $Width + $Yc; - - if ( $Ticks != NULL ) - { - if ( $Cpt % $Ticks == 0 ) - { $Cpt = 0; if ( $Mode == 1 ) { $Mode = 0; } else { $Mode = 1; } } - - if ( $Mode == 1 ) - $this->drawAntialiasPixel($X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); - - $Cpt++; - } - else - $this->drawAntialiasPixel($X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); - - } - $this->Shadow = $RestoreShadow; - } - - /* Draw a filled circle */ - function drawFilledCircle($X,$Y,$Radius,$Format="") - { - $R = isset($Format["R"]) ? $Format["R"] : 0; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1; - $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha; - $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL; - $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; - - if ( $Radius == 0 ) { $Radius = 1; } - if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; } - $X = floor($X); $Y = floor($Y); - - $Radius = abs($Radius); - - $RestoreShadow = $this->Shadow; - if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) - { - $this->Shadow = FALSE; - $this->drawFilledCircle($X+$this->ShadowX,$Y+$this->ShadowY,$Radius,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa,"Ticks"=>$Ticks)); - } - - $this->Mask = ""; - $Color = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); - for ($i=0; $i<=$Radius*2; $i++) - { - $Slice = sqrt($Radius * $Radius - ($Radius - $i) * ($Radius - $i)); - $XPos = floor($Slice); - $YPos = $Y + $i - $Radius; - $AAlias = $Slice - floor($Slice); - - $this->Mask[$X-$XPos][$YPos] = TRUE; - $this->Mask[$X+$XPos][$YPos] = TRUE; - imageline($this->Picture,$X-$XPos,$YPos,$X+$XPos,$YPos,$Color); - } - if ( $this->Antialias ) - $this->drawCircle($X,$Y,$Radius,$Radius,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); - - $this->Mask = ""; - - if ( $BorderR != -1 ) - $this->drawCircle($X,$Y,$Radius,$Radius,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$Ticks)); - - $this->Shadow = $RestoreShadow; - } - - /* Write text */ - function drawText($X,$Y,$Text,$Format="") - { - $R = isset($Format["R"]) ? $Format["R"] : $this->FontColorR; - $G = isset($Format["G"]) ? $Format["G"] : $this->FontColorG; - $B = isset($Format["B"]) ? $Format["B"] : $this->FontColorB; - $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0; - $Align = isset($Format["Align"]) ? $Format["Align"] : TEXT_ALIGN_BOTTOMLEFT; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : $this->FontColorA; - $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->FontName; - $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize; - $ShowOrigine = isset($Format["ShowOrigine"]) ? $Format["ShowOrigine"] : FALSE; - $TOffset = isset($Format["TOffset"]) ? $Format["TOffset"] : 2; - $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : FALSE; - $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : TRUE; - $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 6; - $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : FALSE; - $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 6; - $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 255; - $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 255; - $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 255; - $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 50; - $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : ""; - $BoxBorderR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0; - $BoxBorderG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0; - $BoxBorderB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0; - $BoxBorderAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 50; - $NoShadow = isset($Format["NoShadow"]) ? $Format["NoShadow"] : FALSE; - - $Shadow = $this->Shadow; - if ( $NoShadow ) { $this->Shadow = FALSE; } - - if ( $BoxSurrounding != "" ) { $BoxBorderR = $BoxR - $BoxSurrounding; $BoxBorderG = $BoxG - $BoxSurrounding; $BoxBorderB = $BoxB - $BoxSurrounding; $BoxBorderAlpha = $BoxAlpha; } - - if ( $ShowOrigine ) - { - $MyMarkerSettings = array("R"=>255,"G"=>0,"B"=>0,"BorderR"=>255,"BorderB"=>255,"BorderG"=>255,"Size"=>4); - $this->drawRectangleMarker($X,$Y,$MyMarkerSettings); - } - - $TxtPos = $this->getTextBox($X,$Y,$FontName,$FontSize,$Angle,$Text); - - if ( $DrawBox && ($Angle == 0 || $Angle == 90 || $Angle == 180 || $Angle == 270)) - { - $T[0]["X"]=0;$T[0]["Y"]=0;$T[1]["X"]=0;$T[1]["Y"]=0;$T[2]["X"]=0;$T[2]["Y"]=0;$T[3]["X"]=0;$T[3]["Y"]=0; - if ( $Angle == 0 ) { $T[0]["X"]=-$TOffset;$T[0]["Y"]=$TOffset;$T[1]["X"]=$TOffset;$T[1]["Y"]=$TOffset;$T[2]["X"]=$TOffset;$T[2]["Y"]=-$TOffset;$T[3]["X"]=-$TOffset;$T[3]["Y"]=-$TOffset; } - - $X1 = min($TxtPos[0]["X"],$TxtPos[1]["X"],$TxtPos[2]["X"],$TxtPos[3]["X"]) - $BorderOffset + 3; - $Y1 = min($TxtPos[0]["Y"],$TxtPos[1]["Y"],$TxtPos[2]["Y"],$TxtPos[3]["Y"]) - $BorderOffset; - $X2 = max($TxtPos[0]["X"],$TxtPos[1]["X"],$TxtPos[2]["X"],$TxtPos[3]["X"]) + $BorderOffset + 3; - $Y2 = max($TxtPos[0]["Y"],$TxtPos[1]["Y"],$TxtPos[2]["Y"],$TxtPos[3]["Y"]) + $BorderOffset - 3; - - $X1 = $X1 - $TxtPos[$Align]["X"] + $X + $T[0]["X"]; - $Y1 = $Y1 - $TxtPos[$Align]["Y"] + $Y + $T[0]["Y"]; - $X2 = $X2 - $TxtPos[$Align]["X"] + $X + $T[0]["X"]; - $Y2 = $Y2 - $TxtPos[$Align]["Y"] + $Y + $T[0]["Y"]; - - $Settings = array("R"=>$BoxR,"G"=>$BoxG,"B"=>$BoxB,"Alpha"=>$BoxAlpha,"BorderR"=>$BoxBorderR,"BorderG"=>$BoxBorderG,"BorderB"=>$BoxBorderB,"BorderAlpha"=>$BoxBorderAlpha); - - if ( $BoxRounded ) - { $this->drawRoundedFilledRectangle($X1,$Y1,$X2,$Y2,$RoundedRadius,$Settings); } - else - { $this->drawFilledRectangle($X1,$Y1,$X2,$Y2,$Settings); } - } - - $X = $X - $TxtPos[$Align]["X"] + $X; - $Y = $Y - $TxtPos[$Align]["Y"] + $Y; - - if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) - { - $C_ShadowColor = $this->allocateColor($this->Picture,$this->ShadowR,$this->ShadowG,$this->ShadowB,$this->Shadowa); - imagettftext($this->Picture,$FontSize,$Angle,$X+$this->ShadowX,$Y+$this->ShadowY,$C_ShadowColor,$FontName,$Text); - } - - $C_TextColor = $this->AllocateColor($this->Picture,$R,$G,$B,$Alpha); - imagettftext($this->Picture,$FontSize,$Angle,$X,$Y,$C_TextColor,$FontName,$Text); - - $this->Shadow = $Shadow; - - return($TxtPos); - } - - /* Draw a gradient within a defined area */ - function drawGradientArea($X1,$Y1,$X2,$Y2,$Direction,$Format="") - { - $StartR = isset($Format["StartR"]) ? $Format["StartR"] : 90; - $StartG = isset($Format["StartG"]) ? $Format["StartG"] : 90; - $StartB = isset($Format["StartB"]) ? $Format["StartB"] : 90; - $EndR = isset($Format["EndR"]) ? $Format["EndR"] : 0; - $EndG = isset($Format["EndG"]) ? $Format["EndG"] : 0; - $EndB = isset($Format["EndB"]) ? $Format["EndB"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $Levels = isset($Format["Levels"]) ? $Format["Levels"] : NULL; - - $Shadow = $this->Shadow; - $this->Shadow = FALSE; - - if ( $StartR == $EndR && $StartG == $EndG && $StartB == $EndB ) - { - $this->drawFilledRectangle($X1,$Y1,$X2,$Y2,array("R"=>$StartR,"G"=>$StartG,"B"=>$StartB,"Alpha"=>$Alpha)); - return(0); - } - - if ( $Levels != NULL ) - { $EndR=$StartR+$Levels; $EndG=$StartG+$Levels; $EndB=$StartB+$Levels; } - - if ($X1 > $X2) { list($X1, $X2) = array($X2, $X1); } - if ($Y1 > $Y2) { list($Y1, $Y2) = array($Y2, $Y1); } - - if ( $Direction == DIRECTION_VERTICAL ) { $Width = abs($Y2-$Y1); } - if ( $Direction == DIRECTION_HORIZONTAL ) { $Width = abs($X2-$X1); } - - $Step = max(abs($EndR-$StartR),abs($EndG-$StartG),abs($EndB-$StartB)); - $StepSize = $Width/$Step; - $RStep = ($EndR-$StartR)/$Step; - $GStep = ($EndG-$StartG)/$Step; - $BStep = ($EndB-$StartB)/$Step; - - $R=$StartR;$G=$StartG;$B=$StartB; - switch($Direction) - { - case DIRECTION_VERTICAL: - $StartY = $Y1; $EndY = floor($Y2)+1; $LastY2 = $StartY; - for($i=0;$i<=$Step;$i++) - { - $Y2 = floor($StartY + ($i * $StepSize)); - - if ($Y2 > $EndY) { $Y2 = $EndY; } - if (($Y1 != $Y2 && $Y1 < $Y2) || $Y2 == $EndY) - { - $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); - $this->drawFilledRectangle($X1,$Y1,$X2,$Y2,$Color); - $LastY2 = max($LastY2,$Y2); - $Y1 = $Y2+1; - } - $R = $R + $RStep; $G = $G + $GStep; $B = $B + $BStep; - } - if ( $LastY2 < $EndY && isset($Color)) { for ($i=$LastY2+1;$i<=$EndY;$i++) { $this->drawLine($X1,$i,$X2,$i,$Color); } } - break; - - case DIRECTION_HORIZONTAL: - $StartX = $X1; $EndX = $X2; - for($i=0;$i<=$Step;$i++) - { - $X2 = floor($StartX + ($i * $StepSize)); - - if ($X2 > $EndX) { $X2 = $EndX; } - if (($X1 != $X2 && $X1 < $X2) || $X2 == $EndX) - { - $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); - $this->drawFilledRectangle($X1,$Y1,$X2,$Y2,$Color); - $X1 = $X2+1; - } - $R = $R + $RStep; $G = $G + $GStep; $B = $B + $BStep; - } - if ( $X2 < $EndX && isset($Color)) { $this->drawFilledRectangle($X2,$Y1,$EndX,$Y2,$Color); } - break; - } - - $this->Shadow = $Shadow; - - } - - /* Draw an aliased pixel */ - function drawAntialiasPixel($X,$Y,$Format="") - { - $R = isset($Format["R"]) ? $Format["R"] : 0; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - - if ( $X < 0 || $Y < 0 || $X >= $this->XSize || $Y >= $this->YSize ) - return(-1); - - if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } - if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } - if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } - - if ( !$this->Antialias ) - { - if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) - { - $ShadowColor = $this->allocateColor($this->Picture,$this->ShadowR,$this->ShadowG,$this->ShadowB,$this->Shadowa); - imagesetpixel($this->Picture,$X+$this->ShadowX,$Y+$this->ShadowY,$ShadowColor); - } - - $PlotColor = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); - imagesetpixel($this->Picture,$X,$Y,$PlotColor); - - return(0); - } - - $Plot = ""; - $Xi = floor($X); - $Yi = floor($Y); - - if ( $Xi == $X && $Yi == $Y) - { - if ( $Alpha == 100 ) - $this->drawAlphaPixel($X,$Y,100,$R,$G,$B); - else - $this->drawAlphaPixel($X,$Y,$Alpha,$R,$G,$B); - } - else - { - $Alpha1 = (((1 - ($X - floor($X))) * (1 - ($Y - floor($Y))) * 100) / 100) * $Alpha; - if ( $Alpha1 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi,$Yi,$Alpha1,$R,$G,$B); } - - $Alpha2 = ((($X - floor($X)) * (1 - ($Y - floor($Y))) * 100) / 100) * $Alpha; - if ( $Alpha2 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi+1,$Yi,$Alpha2,$R,$G,$B); } - - $Alpha3 = (((1 - ($X - floor($X))) * ($Y - floor($Y)) * 100) / 100) * $Alpha; - if ( $Alpha3 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi,$Yi+1,$Alpha3,$R,$G,$B); } - - $Alpha4 = ((($X - floor($X)) * ($Y - floor($Y)) * 100) / 100) * $Alpha; - if ( $Alpha4 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi+1,$Yi+1,$Alpha4,$R,$G,$B); } - } - } - - /* Draw a semi-transparent pixel */ - function drawAlphaPixel($X,$Y,$Alpha,$R,$G,$B) - { - if ( isset($this->Mask[$X])) { if ( isset($this->Mask[$X][$Y]) ) { return(0); } } - - if ( $X < 0 || $Y < 0 || $X >= $this->XSize || $Y >= $this->YSize ) - return(-1); - - if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } - if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } - if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } - - if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) - { - $AlphaFactor = floor(($Alpha / 100) * $this->Shadowa); - $ShadowColor = $this->allocateColor($this->Picture,$this->ShadowR,$this->ShadowG,$this->ShadowB,$AlphaFactor); - imagesetpixel($this->Picture,$X+$this->ShadowX,$Y+$this->ShadowY,$ShadowColor); - } - - $C_Aliased = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); - imagesetpixel($this->Picture,$X,$Y,$C_Aliased); - } - - /* Convert apha to base 10 */ - function convertAlpha($AlphaValue) - { return((127/100)*(100-$AlphaValue)); } - - /* Allocate a color with transparency */ - function allocateColor($Picture,$R,$G,$B,$Alpha=100) - { - if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } - if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } - if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } - if ( $Alpha < 0 ) { $Alpha = 0; } - if ( $Alpha > 100) { $Alpha = 100; } - - $Alpha = $this->convertAlpha($Alpha); - return(imagecolorallocatealpha($Picture,$R,$G,$B,$Alpha)); - } - - /* Load a PNG file and draw it over the chart */ - function drawFromPNG($X,$Y,$FileName) - { $this->drawFromPicture(1,$FileName,$X,$Y); } - - /* Load a GIF file and draw it over the chart */ - function drawFromGIF($X,$Y,$FileName) - { $this->drawFromPicture(2,$FileName,$X,$Y); } - - /* Load a JPEG file and draw it over the chart */ - function drawFromJPG($X,$Y,$FileName) - { $this->drawFromPicture(3,$FileName,$X,$Y); } - - function getPicInfo($FileName) - { - $Infos = getimagesize($FileName); - $Width = $Infos[0]; - $Height = $Infos[1]; - $Type = $Infos["mime"]; - - if ( $Type == "image/png") { $Type = 1; } - if ( $Type == "image/gif") { $Type = 2; } - if ( $Type == "image/jpeg ") { $Type = 3; } - - return(array($Width,$Height,$Type)); - } - - /* Generic loader function for external pictures */ - function drawFromPicture($PicType,$FileName,$X,$Y) - { - if ( file_exists($FileName)) - { - list($Width,$Height) = $this->getPicInfo($FileName); - - if ( $PicType == 1 ) - { $Raster = imagecreatefrompng($FileName); } - elseif ( $PicType == 2 ) - { $Raster = imagecreatefromgif($FileName); } - elseif ( $PicType == 3 ) - { $Raster = imagecreatefromjpeg($FileName); } - else - { return(0); } - - - $RestoreShadow = $this->Shadow; - if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) - { - $this->Shadow = FALSE; - if ( $PicType == 3 ) - $this->drawFilledRectangle($X+$this->ShadowX,$Y+$this->ShadowY,$X+$Width+$this->ShadowX,$Y+$Height+$this->ShadowY,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa)); - else - { - $TranparentID = imagecolortransparent($Raster); - for ($Xc=0;$Xc<=$Width-1;$Xc++) - { - for ($Yc=0;$Yc<=$Height-1;$Yc++) - { - $RGBa = imagecolorat($Raster,$Xc,$Yc); - $Values = imagecolorsforindex($Raster,$RGBa); - if ( $Values["alpha"] < 120 ) - { - $AlphaFactor = floor(($this->Shadowa / 100) * ((100 / 127) * (127-$Values["alpha"]))); - $this->drawAlphaPixel($X+$Xc+$this->ShadowX,$Y+$Yc+$this->ShadowY,$AlphaFactor,$this->ShadowR,$this->ShadowG,$this->ShadowB); - } - } - } - } - } - $this->Shadow = $RestoreShadow; - - imagecopy($this->Picture,$Raster,$X,$Y,0,0,$Width,$Height); - imagedestroy($Raster); - } - } - - /* Draw an arrow */ - function drawArrow($X1,$Y1,$X2,$Y2,$Format="") - { - $FillR = isset($Format["FillR"]) ? $Format["FillR"] : 0; - $FillG = isset($Format["FillG"]) ? $Format["FillG"] : 0; - $FillB = isset($Format["FillB"]) ? $Format["FillB"] : 0; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $FillR; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $FillG; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $FillB; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $Size = isset($Format["Size"]) ? $Format["Size"] : 10; - $Ratio = isset($Format["Ratio"]) ? $Format["Ratio"] : .5; - $TwoHeads = isset($Format["TwoHeads"]) ? $Format["TwoHeads"] : FALSE; - $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : FALSE; - - /* Calculate the line angle */ - $Angle = $this->getAngle($X1,$Y1,$X2,$Y2); - - /* Override Shadow support, this will be managed internally */ - $RestoreShadow = $this->Shadow; - if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) - { - $this->Shadow = FALSE; - $this->drawArrow($X1+$this->ShadowX,$Y1+$this->ShadowY,$X2+$this->ShadowX,$Y2+$this->ShadowY,array("FillR"=>$this->ShadowR,"FillG"=>$this->ShadowG,"FillB"=>$this->ShadowB,"Alpha"=>$this->Shadowa,"Size"=>$Size,"Ratio"=>$Ratio,"TwoHeads"=>$TwoHeads,"Ticks"=>$Ticks)); - } - - /* Draw the 1st Head */ - $TailX = cos(($Angle-180)*PI/180)*$Size+$X2; - $TailY = sin(($Angle-180)*PI/180)*$Size+$Y2; - - $Points = ""; - $Points[] = $X2; $Points[] = $Y2; - $Points[] = cos(($Angle-90)*PI/180)*$Size*$Ratio+$TailX; $Points[] = sin(($Angle-90)*PI/180)*$Size*$Ratio+$TailY; - $Points[] = cos(($Angle-270)*PI/180)*$Size*$Ratio+$TailX; $Points[] = sin(($Angle-270)*PI/180)*$Size*$Ratio+$TailY; - $Points[] = $X2; $Points[] = $Y2; - - /* Visual correction */ - if ($Angle == 180 || $Angle == 360 ) { $Points[4] = $Points[2]; } - if ($Angle == 90 || $Angle == 270 ) { $Points[5] = $Points[3]; } - - $ArrowColor = $this->allocateColor($this->Picture,$FillR,$FillG,$FillB,$Alpha); - ImageFilledPolygon($this->Picture,$Points,4,$ArrowColor); - - $this->drawLine($Points[0],$Points[1],$Points[2],$Points[3],array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha)); - $this->drawLine($Points[2],$Points[3],$Points[4],$Points[5],array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha)); - $this->drawLine($Points[0],$Points[1],$Points[4],$Points[5],array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha)); - - /* Draw the second head */ - if ( $TwoHeads ) - { - $Angle = $this->getAngle($X2,$Y2,$X1,$Y1); - - $TailX2 = cos(($Angle-180)*PI/180)*$Size+$X1; - $TailY2 = sin(($Angle-180)*PI/180)*$Size+$Y1; - - $Points = ""; - $Points[] = $X1; $Points[] = $Y1; - $Points[] = cos(($Angle-90)*PI/180)*$Size*$Ratio+$TailX2; $Points[] = sin(($Angle-90)*PI/180)*$Size*$Ratio+$TailY2; - $Points[] = cos(($Angle-270)*PI/180)*$Size*$Ratio+$TailX2; $Points[] = sin(($Angle-270)*PI/180)*$Size*$Ratio+$TailY2; - $Points[] = $X1; $Points[] = $Y1; - - /* Visual correction */ - if ($Angle == 180 || $Angle == 360 ) { $Points[4] = $Points[2]; } - if ($Angle == 90 || $Angle == 270 ) { $Points[5] = $Points[3]; } - - $ArrowColor = $this->allocateColor($this->Picture,$FillR,$FillG,$FillB,$Alpha); - ImageFilledPolygon($this->Picture,$Points,4,$ArrowColor); - - $this->drawLine($Points[0],$Points[1],$Points[2],$Points[3],array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha)); - $this->drawLine($Points[2],$Points[3],$Points[4],$Points[5],array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha)); - $this->drawLine($Points[0],$Points[1],$Points[4],$Points[5],array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha)); - - $this->drawLine($TailX,$TailY,$TailX2,$TailY2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); - } - else - $this->drawLine($X1,$Y1,$TailX,$TailY,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); - - /* Re-enable shadows */ - $this->Shadow = $RestoreShadow; - } - - /* Draw a label with associated arrow */ - function drawArrowLabel($X1,$Y1,$Text,$Format="") - { - $FillR = isset($Format["FillR"]) ? $Format["FillR"] : 0; - $FillG = isset($Format["FillG"]) ? $Format["FillG"] : 0; - $FillB = isset($Format["FillB"]) ? $Format["FillB"] : 0; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $FillR; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $FillG; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $FillB; - $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->FontName; - $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $Length = isset($Format["Length"]) ? $Format["Length"] : 50; - $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 315; - $Size = isset($Format["Size"]) ? $Format["Size"] : 10; - $Position = isset($Format["Position"]) ? $Format["Position"] : POSITION_TOP; - $RoundPos = isset($Format["RoundPos"]) ? $Format["RoundPos"] : FALSE; - $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL; - - $Angle = $Angle % 360; - - $X2 = sin(($Angle+180)*PI/180)*$Length+$X1; - $Y2 = cos(($Angle+180)*PI/180)*$Length+$Y1; - - if ( $RoundPos && $Angle > 0 && $Angle < 180 ) { $Y2 = ceil($Y2); } - if ( $RoundPos && $Angle > 180 ) { $Y2 = floor($Y2); } - - $this->drawArrow($X2,$Y2,$X1,$Y1,$Format); - - $Size = imagettfbbox($FontSize,0,$FontName,$Text); - $TxtWidth = max(abs($Size[2]-$Size[0]),abs($Size[0]-$Size[6])); - $TxtHeight = max(abs($Size[1]-$Size[7]),abs($Size[3]-$Size[1])); - - if ( $Angle > 0 && $Angle < 180 ) - { - $this->drawLine($X2,$Y2,$X2-$TxtWidth,$Y2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); - if ( $Position == POSITION_TOP ) - $this->drawText($X2,$Y2-2,$Text,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha,"Align"=>TEXT_ALIGN_BOTTOMRIGHT)); - else - $this->drawText($X2,$Y2+4,$Text,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha,"Align"=>TEXT_ALIGN_TOPRIGHT)); - } - else - { - $this->drawLine($X2,$Y2,$X2+$TxtWidth,$Y2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); - if ( $Position == POSITION_TOP ) - $this->drawText($X2,$Y2-2,$Text,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha)); - else - $this->drawText($X2,$Y2+4,$Text,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha,"Align"=>TEXT_ALIGN_TOPLEFT)); - } - } - - /* Draw a progress bar filled with specified % */ - function drawProgress($X,$Y,$Percent,$Format="") - { - if ( $Percent > 100 ) { $Percent = 100; } - if ( $Percent < 0 ) { $Percent = 0; } - - $Width = isset($Format["Width"]) ? $Format["Width"] : 200; - $Height = isset($Format["Height"]) ? $Format["Height"] : 20; - $Orientation = isset($Format["Orientation"]) ? $Format["Orientation"] : ORIENTATION_HORIZONTAL; - $ShowLabel = isset($Format["ShowLabel"]) ? $Format["ShowLabel"] : FALSE; - $LabelPos = isset($Format["LabelPos"]) ? $Format["LabelPos"] : LABEL_POS_INSIDE; - $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 10; - $R = isset($Format["R"]) ? $Format["R"] : 130; - $G = isset($Format["G"]) ? $Format["G"] : 130; - $B = isset($Format["B"]) ? $Format["B"] : 130; - $RFade = isset($Format["RFade"]) ? $Format["RFade"] : -1; - $GFade = isset($Format["GFade"]) ? $Format["GFade"] : -1; - $BFade = isset($Format["BFade"]) ? $Format["BFade"] : -1; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B; - $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 0; - $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 0; - $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 0; - $BoxBackR = isset($Format["BoxBackR"]) ? $Format["BoxBackR"] : 255; - $BoxBackG = isset($Format["BoxBackG"]) ? $Format["BoxBackG"] : 255; - $BoxBackB = isset($Format["BoxBackB"]) ? $Format["BoxBackB"] : 255; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; - $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : NULL; - $NoAngle = isset($Format["NoAngle"]) ? $Format["NoAngle"] : FALSE; - - if ( $RFade != -1 && $GFade != -1 && $BFade != -1 ) - { - $RFade = (($RFade-$R)/100)*$Percent+$R; - $GFade = (($GFade-$G)/100)*$Percent+$G; - $BFade = (($BFade-$B)/100)*$Percent+$B; - } - - if ( $Surrounding != NULL ) { $BorderR = $R + $Surrounding; $BorderG = $G + $Surrounding; $BorderB = $B + $Surrounding; } - if ( $BoxSurrounding != NULL ) { $BoxBorderR = $BoxBackR + $Surrounding; $BoxBorderG = $BoxBackG + $Surrounding; $BoxBorderB = $BoxBackB + $Surrounding; } - - if ( $Orientation == ORIENTATION_VERTICAL ) - { - $InnerHeight = (($Height-2)/100)*$Percent; - $this->drawFilledRectangle($X,$Y,$X+$Width,$Y-$Height,array("R"=>$BoxBackR,"G"=>$BoxBackG,"B"=>$BoxBackB,"BorderR"=>$BoxBorderR,"BorderG"=>$BoxBorderG,"BorderB"=>$BoxBorderB,"NoAngle"=>$NoAngle)); - - $RestoreShadow = $this->Shadow; $this->Shadow = FALSE; - if ( $RFade != -1 && $GFade != -1 && $BFade != -1 ) - { - $GradientOptions = array("StartR"=>$RFade,"StartG"=>$GFade,"StartB"=>$BFade,"EndR"=>$R,"EndG"=>$G,"EndB"=>$B); - $this->drawGradientArea($X+1,$Y-1,$X+$Width-1,$Y-$InnerHeight,DIRECTION_VERTICAL,$GradientOptions); - - if ( $Surrounding ) - $this->drawRectangle($X+1,$Y-1,$X+$Width-1,$Y-$InnerHeight,array("R"=>255,"G"=>255,"B"=>255,"Alpha"=>$Surrounding)); - } - else - $this->drawFilledRectangle($X+1,$Y-1,$X+$Width-1,$Y-$InnerHeight,array("R"=>$R,"G"=>$G,"B"=>$B,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB)); - - $this->Shadow = $RestoreShadow; - - if ( $ShowLabel && $LabelPos == LABEL_POS_BOTTOM ) { $this->drawText($X+($Width/2),$Y+$Margin,$Percent."%",array("Align"=>TEXT_ALIGN_TOPMIDDLE)); } - if ( $ShowLabel && $LabelPos == LABEL_POS_TOP ) { $this->drawText($X+($Width/2),$Y-$Height-$Margin,$Percent."%",array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); } - if ( $ShowLabel && $LabelPos == LABEL_POS_INSIDE ) { $this->drawText($X+($Width/2),$Y-$InnerHeight-$Margin,$Percent."%",array("Align"=>TEXT_ALIGN_MIDDLELEFT,"Angle"=>90)); } - if ( $ShowLabel && $LabelPos == LABEL_POS_CENTER ) { $this->drawText($X+($Width/2),$Y-($Height/2),$Percent."%",array("Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"Angle"=>90)); } - } - else - { - if ( $Percent == 100 ) - $InnerWidth = $Width-1; - else - $InnerWidth = (($Width-2)/100)*$Percent; - - $this->drawFilledRectangle($X,$Y,$X+$Width,$Y+$Height,array("R"=>$BoxBackR,"G"=>$BoxBackG,"B"=>$BoxBackB,"BorderR"=>$BoxBorderR,"BorderG"=>$BoxBorderG,"BorderB"=>$BoxBorderB,"NoAngle"=>$NoAngle)); - - $RestoreShadow = $this->Shadow; $this->Shadow = FALSE; - if ( $RFade != -1 && $GFade != -1 && $BFade != -1 ) - { - $GradientOptions = array("StartR"=>$R,"StartG"=>$G,"StartB"=>$B,"EndR"=>$RFade,"EndG"=>$GFade,"EndB"=>$BFade); - $this->drawGradientArea($X+1,$Y+1,$X+$InnerWidth,$Y+$Height-1,DIRECTION_HORIZONTAL,$GradientOptions); - - if ( $Surrounding ) - $this->drawRectangle($X+1,$Y+1,$X+$InnerWidth,$Y+$Height-1,array("R"=>255,"G"=>255,"B"=>255,"Alpha"=>$Surrounding)); - } - else - $this->drawFilledRectangle($X+1,$Y+1,$X+$InnerWidth,$Y+$Height-1,array("R"=>$R,"G"=>$G,"B"=>$B,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB)); - - $this->Shadow = $RestoreShadow; - - if ( $ShowLabel && $LabelPos == LABEL_POS_LEFT ) { $this->drawText($X-$Margin,$Y+($Height/2),$Percent."%",array("Align"=>TEXT_ALIGN_MIDDLERIGHT)); } - if ( $ShowLabel && $LabelPos == LABEL_POS_RIGHT ) { $this->drawText($X+$Width+$Margin,$Y+($Height/2),$Percent."%",array("Align"=>TEXT_ALIGN_MIDDLELEFT)); } - if ( $ShowLabel && $LabelPos == LABEL_POS_CENTER ) { $this->drawText($X+($Width/2),$Y+($Height/2),$Percent."%",array("Align"=>TEXT_ALIGN_MIDDLEMIDDLE)); } - if ( $ShowLabel && $LabelPos == LABEL_POS_INSIDE ) { $this->drawText($X+$InnerWidth+$Margin,$Y+($Height/2),$Percent."%",array("Align"=>TEXT_ALIGN_MIDDLELEFT)); } - } - } - - /* Get the legend box size */ - function getLegendSize($Format="") - { - $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->FontName; - $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize; - $BoxSize = isset($Format["BoxSize"]) ? $Format["BoxSize"] : 5; - $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5; - $Style = isset($Format["Style"]) ? $Format["Style"] : LEGEND_ROUND; - $Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL; - $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 5; - $BoxHeight = isset($Format["BoxHeight"]) ? $Format["BoxHeight"] : 5; - $IconAreaWidth = isset($Format["IconAreaWidth"]) ? $Format["IconAreaWidth"] : $BoxWidth; - $IconAreaHeight = isset($Format["IconAreaHeight"]) ? $Format["IconAreaHeight"] : $BoxHeight; - $XSpacing = isset($Format["XSpacing"]) ? $Format["XSpacing"] : 5; - - $Data = $this->DataSet->getData(); - - foreach($Data["Series"] as $SerieName => $Serie) - { - if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] && isset($Serie["Picture"])) - { - list($PicWidth,$PicHeight) = $this->getPicInfo($Serie["Picture"]); - if ( $IconAreaWidth < $PicWidth ) { $IconAreaWidth = $PicWidth; } - if ( $IconAreaHeight < $PicHeight ) { $IconAreaHeight = $PicHeight; } - } - } - - $YStep = max($this->FontSize,$IconAreaHeight) + 5; - $XStep = $IconAreaWidth + 5; - $XStep = $XSpacing; - - $X=100; $Y=100; - - $Boundaries = ""; $Boundaries["L"] = $X; $Boundaries["T"] = $Y; $Boundaries["R"] = 0; $Boundaries["B"] = 0; $vY = $Y; $vX = $X; - foreach($Data["Series"] as $SerieName => $Serie) - { - if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) - { - if ( $Mode == LEGEND_VERTICAL ) - { - $BoxArray = $this->getTextBox($vX+$IconAreaWidth+4,$vY+$IconAreaHeight/2,$FontName,$FontSize,0,$Serie["Description"]); - - if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$IconAreaHeight/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$IconAreaHeight/2; } - if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; } - if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$IconAreaHeight/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$IconAreaHeight/2; } - - $Lines = preg_split("/\n/",$Serie["Description"]); - $vY = $vY + max($this->FontSize*count($Lines),$IconAreaHeight) + 5; - } - elseif ( $Mode == LEGEND_HORIZONTAL ) - { - $Lines = preg_split("/\n/",$Serie["Description"]); - $Width = ""; - foreach($Lines as $Key => $Value) - { - $BoxArray = $this->getTextBox($vX+$IconAreaWidth+6,$Y+$IconAreaHeight/2+(($this->FontSize+3)*$Key),$FontName,$FontSize,0,$Value); - - if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$IconAreaHeight/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$IconAreaHeight/2; } - if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; } - if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$IconAreaHeight/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$IconAreaHeight/2; } - - $Width[] = $BoxArray[1]["X"]; - } - - $vX=max($Width)+$XStep; - } - } - } - $vY=$vY-$YStep; $vX=$vX-$XStep; - - $TopOffset = $Y - $Boundaries["T"]; - if ( $Boundaries["B"]-($vY+$IconAreaHeight) < $TopOffset ) { $Boundaries["B"] = $vY+$IconAreaHeight+$TopOffset; } - - $Width = ($Boundaries["R"]+$Margin) - ($Boundaries["L"]-$Margin); - $Height = ($Boundaries["B"]+$Margin) - ($Boundaries["T"]-$Margin); - - return(array("Width"=>$Width,"Height"=>$Height)); - } - - /* Draw the legend of the active series */ - function drawLegend($X,$Y,$Format="") - { - $Family = isset($Format["Family"]) ? $Format["Family"] : LEGEND_FAMILY_BOX; - $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->FontName; - $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize; - $FontR = isset($Format["FontR"]) ? $Format["FontR"] : $this->FontColorR; - $FontG = isset($Format["FontG"]) ? $Format["FontG"] : $this->FontColorG; - $FontB = isset($Format["FontB"]) ? $Format["FontB"] : $this->FontColorB; - $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 5; - $BoxHeight = isset($Format["BoxHeight"]) ? $Format["BoxHeight"] : 5; - $IconAreaWidth = isset($Format["IconAreaWidth"]) ? $Format["IconAreaWidth"] : $BoxWidth; - $IconAreaHeight = isset($Format["IconAreaHeight"]) ? $Format["IconAreaHeight"] : $BoxHeight; - $XSpacing = isset($Format["XSpacing"]) ? $Format["XSpacing"] : 5; - $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5; - $R = isset($Format["R"]) ? $Format["R"] : 200; - $G = isset($Format["G"]) ? $Format["G"] : 200; - $B = isset($Format["B"]) ? $Format["B"] : 200; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255; - $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; - $Style = isset($Format["Style"]) ? $Format["Style"] : LEGEND_ROUND; - $Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL; - - if ( $Surrounding != NULL ) { $BorderR = $R + $Surrounding; $BorderG = $G + $Surrounding; $BorderB = $B + $Surrounding; } - - $Data = $this->DataSet->getData(); - - foreach($Data["Series"] as $SerieName => $Serie) - { - if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] && isset($Serie["Picture"])) - { - list($PicWidth,$PicHeight) = $this->getPicInfo($Serie["Picture"]); - if ( $IconAreaWidth < $PicWidth ) { $IconAreaWidth = $PicWidth; } - if ( $IconAreaHeight < $PicHeight ) { $IconAreaHeight = $PicHeight; } - } - } - - $YStep = max($this->FontSize,$IconAreaHeight) + 5; - $XStep = $IconAreaWidth + 5; - $XStep = $XSpacing; - - $Boundaries = ""; $Boundaries["L"] = $X; $Boundaries["T"] = $Y; $Boundaries["R"] = 0; $Boundaries["B"] = 0; $vY = $Y; $vX = $X; - foreach($Data["Series"] as $SerieName => $Serie) - { - if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) - { - if ( $Mode == LEGEND_VERTICAL ) - { - $BoxArray = $this->getTextBox($vX+$IconAreaWidth+4,$vY+$IconAreaHeight/2,$FontName,$FontSize,0,$Serie["Description"]); - - if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$IconAreaHeight/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$IconAreaHeight/2; } - if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; } - if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$IconAreaHeight/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$IconAreaHeight/2; } - - $Lines = preg_split("/\n/",$Serie["Description"]); - $vY = $vY + max($this->FontSize*count($Lines),$IconAreaHeight) + 5; - } - elseif ( $Mode == LEGEND_HORIZONTAL ) - { - $Lines = preg_split("/\n/",$Serie["Description"]); - $Width = ""; - foreach($Lines as $Key => $Value) - { - $BoxArray = $this->getTextBox($vX+$IconAreaWidth+6,$Y+$IconAreaHeight/2+(($this->FontSize+3)*$Key),$FontName,$FontSize,0,$Value); - - if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$IconAreaHeight/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$IconAreaHeight/2; } - if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; } - if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$IconAreaHeight/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$IconAreaHeight/2; } - - $Width[] = $BoxArray[1]["X"]; - } - - $vX=max($Width)+$XStep; - } - } - } - $vY=$vY-$YStep; $vX=$vX-$XStep; - - $TopOffset = $Y - $Boundaries["T"]; - if ( $Boundaries["B"]-($vY+$IconAreaHeight) < $TopOffset ) { $Boundaries["B"] = $vY+$IconAreaHeight+$TopOffset; } - - if ( $Style == LEGEND_ROUND ) - $this->drawRoundedFilledRectangle($Boundaries["L"]-$Margin,$Boundaries["T"]-$Margin,$Boundaries["R"]+$Margin,$Boundaries["B"]+$Margin,$Margin,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB)); - elseif ( $Style == LEGEND_BOX ) - $this->drawFilledRectangle($Boundaries["L"]-$Margin,$Boundaries["T"]-$Margin,$Boundaries["R"]+$Margin,$Boundaries["B"]+$Margin,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB)); - - $RestoreShadow = $this->Shadow; $this->Shadow = FALSE; - foreach($Data["Series"] as $SerieName => $Serie) - { - if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) - { - $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; - $Ticks = $Serie["Ticks"]; $Weight = $Serie["Weight"]; - - if ( isset($Serie["Picture"]) ) - { - $Picture = $Serie["Picture"]; - list($PicWidth,$PicHeight) = $this->getPicInfo($Picture); - $PicX = $X+$IconAreaWidth/2; $PicY = $Y+$IconAreaHeight/2; - - $this->drawFromPNG($PicX-$PicWidth/2,$PicY-$PicHeight/2,$Picture); - } - else - { - if ( $Family == LEGEND_FAMILY_BOX ) - { - if ( $BoxWidth != $IconAreaWidth ) { $XOffset = floor(($IconAreaWidth-$BoxWidth)/2); } else { $XOffset = 0; } - if ( $BoxHeight != $IconAreaHeight ) { $YOffset = floor(($IconAreaHeight-$BoxHeight)/2); } else { $YOffset = 0; } - - $this->drawFilledRectangle($X+1+$XOffset,$Y+1+$YOffset,$X+$BoxWidth+$XOffset+1,$Y+$BoxHeight+1+$YOffset,array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20)); - $this->drawFilledRectangle($X+$XOffset,$Y+$YOffset,$X+$BoxWidth+$XOffset,$Y+$BoxHeight+$YOffset,array("R"=>$R,"G"=>$G,"B"=>$B,"Surrounding"=>20)); - } - elseif ( $Family == LEGEND_FAMILY_CIRCLE ) - { - $this->drawFilledCircle($X+1+$IconAreaWidth/2,$Y+1+$IconAreaHeight/2,min($IconAreaHeight/2,$IconAreaWidth/2),array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20)); - $this->drawFilledCircle($X+$IconAreaWidth/2,$Y+$IconAreaHeight/2,min($IconAreaHeight/2,$IconAreaWidth/2),array("R"=>$R,"G"=>$G,"B"=>$B,"Surrounding"=>20)); - } - elseif ( $Family == LEGEND_FAMILY_LINE ) - { - $this->drawLine($X+1,$Y+1+$IconAreaHeight/2,$X+1+$IconAreaWidth,$Y+1+$IconAreaHeight/2,array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20,"Ticks"=>$Ticks,"Weight"=>$Weight)); - $this->drawLine($X,$Y+$IconAreaHeight/2,$X+$IconAreaWidth,$Y+$IconAreaHeight/2,array("R"=>$R,"G"=>$G,"B"=>$B,"Ticks"=>$Ticks,"Weight"=>$Weight)); - } - } - - if ( $Mode == LEGEND_VERTICAL ) - { - $Lines = preg_split("/\n/",$Serie["Description"]); - foreach($Lines as $Key => $Value) - $this->drawText($X+$IconAreaWidth+4,$Y+$IconAreaHeight/2+(($this->FontSize+3)*$Key),$Value,array("R"=>$FontR,"G"=>$FontG,"B"=>$FontB,"Align"=>TEXT_ALIGN_MIDDLELEFT,"FontSize"=>$FontSize,"FontName"=>$FontName)); - - $Y=$Y+max($this->FontSize*count($Lines),$IconAreaHeight) + 5; - } - elseif ( $Mode == LEGEND_HORIZONTAL ) - { - $Lines = preg_split("/\n/",$Serie["Description"]); - $Width = ""; - foreach($Lines as $Key => $Value) - { - $BoxArray = $this->drawText($X+$IconAreaWidth+4,$Y+$IconAreaHeight/2+(($this->FontSize+3)*$Key),$Value,array("R"=>$FontR,"G"=>$FontG,"B"=>$FontB,"Align"=>TEXT_ALIGN_MIDDLELEFT,"FontSize"=>$FontSize,"FontName"=>$FontName)); - $Width[] = $BoxArray[1]["X"]; - } - $X=max($Width)+2+$XStep; - } - } - } - - - $this->Shadow = $RestoreShadow; - } - - function drawScale($Format="") - { - $Pos = isset($Format["Pos"]) ? $Format["Pos"] : SCALE_POS_LEFTRIGHT; - $Floating = isset($Format["Floating"]) ? $Format["Floating"] : FALSE; - $Mode = isset($Format["Mode"]) ? $Format["Mode"] : SCALE_MODE_FLOATING; - $RemoveXAxis = isset($Format["RemoveXAxis"]) ? $Format["RemoveXAxis"] : FALSE; - $MinDivHeight = isset($Format["MinDivHeight"]) ? $Format["MinDivHeight"] : 20; - $Factors = isset($Format["Factors"]) ? $Format["Factors"] : array(1,2,5); - $ManualScale = isset($Format["ManualScale"]) ? $Format["ManualScale"] : array("0"=>array("Min"=>-100,"Max"=>100)); - $XMargin = isset($Format["XMargin"]) ? $Format["XMargin"] : AUTO; - $YMargin = isset($Format["YMargin"]) ? $Format["YMargin"] : 0; - $ScaleSpacing = isset($Format["ScaleSpacing"]) ? $Format["ScaleSpacing"] : 15; - $InnerTickWidth = isset($Format["InnerTickWidth"]) ? $Format["InnerTickWidth"] : 2; - $OuterTickWidth = isset($Format["OuterTickWidth"]) ? $Format["OuterTickWidth"] : 2; - $DrawXLines = isset($Format["DrawXLines"]) ? $Format["DrawXLines"] : TRUE; - $DrawYLines = isset($Format["DrawYLines"]) ? $Format["DrawYLines"] : ALL; - $GridTicks = isset($Format["GridTicks"]) ? $Format["GridTicks"] : 4; - $GridR = isset($Format["GridR"]) ? $Format["GridR"] : 255; - $GridG = isset($Format["GridG"]) ? $Format["GridG"] : 255; - $GridB = isset($Format["GridB"]) ? $Format["GridB"] : 255; - $GridAlpha = isset($Format["GridAlpha"]) ? $Format["GridAlpha"] : 40; - $AxisRo = isset($Format["AxisR"]) ? $Format["AxisR"] : 0; - $AxisGo = isset($Format["AxisG"]) ? $Format["AxisG"] : 0; - $AxisBo = isset($Format["AxisB"]) ? $Format["AxisB"] : 0; - $AxisAlpha = isset($Format["AxisAlpha"]) ? $Format["AxisAlpha"] : 100; - $TickRo = isset($Format["TickR"]) ? $Format["TickR"] : 0; - $TickGo = isset($Format["TickG"]) ? $Format["TickG"] : 0; - $TickBo = isset($Format["TickB"]) ? $Format["TickB"] : 0; - $TickAlpha = isset($Format["TickAlpha"]) ? $Format["TickAlpha"] : 100; - $DrawSubTicks = isset($Format["DrawSubTicks"]) ? $Format["DrawSubTicks"] : FALSE; - $InnerSubTickWidth = isset($Format["InnerSubTickWidth"]) ? $Format["InnerSubTickWidth"] : 0; - $OuterSubTickWidth = isset($Format["OuterSubTickWidth"]) ? $Format["OuterSubTickWidth"] : 2; - $SubTickR = isset($Format["SubTickR"]) ? $Format["SubTickR"] : 255; - $SubTickG = isset($Format["SubTickG"]) ? $Format["SubTickG"] : 0; - $SubTickB = isset($Format["SubTickB"]) ? $Format["SubTickB"] : 0; - $SubTickAlpha = isset($Format["SubTickAlpha"]) ? $Format["SubTickAlpha"] : 100; - $AutoAxisLabels = isset($Format["AutoAxisLabels"]) ? $Format["AutoAxisLabels"] : TRUE; - $XReleasePercent = isset($Format["XReleasePercent"]) ? $Format["XReleasePercent"] : 1; - $DrawArrows = isset($Format["DrawArrows"]) ? $Format["DrawArrows"] : FALSE; - $ArrowSize = isset($Format["ArrowSize"]) ? $Format["ArrowSize"] : 8; - $CycleBackground = isset($Format["CycleBackground"]) ? $Format["CycleBackground"] : FALSE; - $BackgroundR1 = isset($Format["BackgroundR1"]) ? $Format["BackgroundR1"] : 255; - $BackgroundG1 = isset($Format["BackgroundG1"]) ? $Format["BackgroundG1"] : 255; - $BackgroundB1 = isset($Format["BackgroundB1"]) ? $Format["BackgroundB1"] : 255; - $BackgroundAlpha1 = isset($Format["BackgroundAlpha1"]) ? $Format["BackgroundAlpha1"] : 20; - $BackgroundR2 = isset($Format["BackgroundR2"]) ? $Format["BackgroundR2"] : 230; - $BackgroundG2 = isset($Format["BackgroundG2"]) ? $Format["BackgroundG2"] : 230; - $BackgroundB2 = isset($Format["BackgroundB2"]) ? $Format["BackgroundB2"] : 230; - $BackgroundAlpha2 = isset($Format["BackgroundAlpha2"]) ? $Format["BackgroundAlpha2"] : 20; - $LabelingMethod = isset($Format["LabelingMethod"]) ? $Format["LabelingMethod"] : LABELING_ALL; - $LabelSkip = isset($Format["LabelSkip"]) ? $Format["LabelSkip"] : 0; - $LabelRotation = isset($Format["LabelRotation"]) ? $Format["LabelRotation"] : 0; - $SkippedAxisTicks = isset($Format["SkippedAxisTicks"]) ? $Format["SkippedAxisTicks"] : $GridTicks+2; - $SkippedAxisR = isset($Format["SkippedAxisR"]) ? $Format["SkippedAxisR"] : $GridR; - $SkippedAxisG = isset($Format["SkippedAxisG"]) ? $Format["SkippedAxisG"] : $GridG; - $SkippedAxisB = isset($Format["SkippedAxisB"]) ? $Format["SkippedAxisB"] : $GridB; - $SkippedAxisAlpha = isset($Format["SkippedAxisAlpha"]) ? $Format["SkippedAxisAlpha"] : $GridAlpha-30; - $SkippedTickR = isset($Format["SkippedTickR"]) ? $Format["SkippedTickR"] : $TickRo; - $SkippedTickG = isset($Format["SkippedTickG"]) ? $Format["SkippedTickG"] : $TickGo; - $SkippedTickB = isset($Format["SkippedTicksB"]) ? $Format["SkippedTickB"] : $TickBo; - $SkippedTickAlpha = isset($Format["SkippedTickAlpha"]) ? $Format["SkippedTickAlpha"] : $TickAlpha-80; - $SkippedInnerTickWidth = isset($Format["SkippedInnerTickWidth"]) ? $Format["SkippedInnerTickWidth"] : 0; - $SkippedOuterTickWidth = isset($Format["SkippedOuterTickWidth"]) ? $Format["SkippedOuterTickWidth"] : 2; - - /* Floating scale require X & Y margins to be set manually */ - if ( $Floating && ( $XMargin == AUTO || $YMargin == 0 ) ) { $Floating = FALSE; } - - /* Skip a NOTICE event in case of an empty array */ - if ( $DrawYLines == NONE || $DrawYLines == FALSE ) { $DrawYLines = array("zarma"=>"31"); } - - /* Define the color for the skipped elements */ - $SkippedAxisColor = array("R"=>$SkippedAxisR,"G"=>$SkippedAxisG,"B"=>$SkippedAxisB,"Alpha"=>$SkippedAxisAlpha,"Ticks"=>$SkippedAxisTicks); - $SkippedTickColor = array("R"=>$SkippedTickR,"G"=>$SkippedTickG,"B"=>$SkippedTickB,"Alpha"=>$SkippedTickAlpha); - - $Data = $this->DataSet->getData(); - if ( isset($Data["Abscissa"]) ) { $Abscissa = $Data["Abscissa"]; } else { $Abscissa = NULL; } - - /* Unset the abscissa axis, needed if we display multiple charts on the same picture */ - if ( $Abscissa != NULL ) - { - foreach($Data["Axis"] as $AxisID => $Parameters) - { if ($Parameters["Identity"] == AXIS_X) { unset($Data["Axis"][$AxisID]); } } - } - - /* Build the scale settings */ - $GotXAxis = FALSE; - foreach($Data["Axis"] as $AxisID => $AxisParameter) - { - if ( $AxisParameter["Identity"] == AXIS_X ) { $GotXAxis = TRUE; } - - if ( $Pos == SCALE_POS_LEFTRIGHT && $AxisParameter["Identity"] == AXIS_Y) - { $Height = $this->GraphAreaY2-$this->GraphAreaY1 - $YMargin*2; } - elseif ( $Pos == SCALE_POS_LEFTRIGHT && $AxisParameter["Identity"] == AXIS_X) - { $Height = $this->GraphAreaX2-$this->GraphAreaX1; } - elseif ( $Pos == SCALE_POS_TOPBOTTOM && $AxisParameter["Identity"] == AXIS_Y) - { $Height = $this->GraphAreaX2-$this->GraphAreaX1 - $YMargin*2;; } - else - { $Height = $this->GraphAreaY2-$this->GraphAreaY1; } - - $AxisMin = ABSOLUTE_MAX; $AxisMax = OUT_OF_SIGHT; - if ( $Mode == SCALE_MODE_FLOATING || $Mode == SCALE_MODE_START0 ) - { - foreach($Data["Series"] as $SerieID => $SerieParameter) - { - if ( $SerieParameter["Axis"] == $AxisID && $Data["Series"][$SerieID]["isDrawable"] && $Data["Abscissa"] != $SerieID) - { - $AxisMax = max($AxisMax,$Data["Series"][$SerieID]["Max"]); - $AxisMin = min($AxisMin,$Data["Series"][$SerieID]["Min"]); - } - } - $AutoMargin = (($AxisMax-$AxisMin)/100)*$XReleasePercent; - - $Data["Axis"][$AxisID]["Min"] = $AxisMin-$AutoMargin; $Data["Axis"][$AxisID]["Max"] = $AxisMax+$AutoMargin; - if ( $Mode == SCALE_MODE_START0 ) { $Data["Axis"][$AxisID]["Min"] = 0; } - } - elseif ( $Mode == SCALE_MODE_MANUAL ) - { - if ( isset($ManualScale[$AxisID]["Min"]) && isset($ManualScale[$AxisID]["Max"]) ) - { - $Data["Axis"][$AxisID]["Min"] = $ManualScale[$AxisID]["Min"]; - $Data["Axis"][$AxisID]["Max"] = $ManualScale[$AxisID]["Max"]; - } - else - { echo "Manual scale boundaries not set."; exit(); } - } - elseif ( $Mode == SCALE_MODE_ADDALL || $Mode == SCALE_MODE_ADDALL_START0 ) - { - $Series = ""; - foreach($Data["Series"] as $SerieID => $SerieParameter) - { if ( $SerieParameter["Axis"] == $AxisID && $SerieParameter["isDrawable"] && $Data["Abscissa"] != $SerieID ) { $Series[$SerieID] = count($Data["Series"][$SerieID]["Data"]); } } - - for ($ID=0;$ID<=max($Series)-1;$ID++) - { - $PointMin = 0; $PointMax = 0; - foreach($Series as $SerieID => $ValuesCount ) - { - if (isset($Data["Series"][$SerieID]["Data"][$ID]) && $Data["Series"][$SerieID]["Data"][$ID] != NULL ) - { - $Value = $Data["Series"][$SerieID]["Data"][$ID]; - if ( $Value > 0 ) { $PointMax = $PointMax + $Value; } else { $PointMin = $PointMin + $Value; } - } - } - $AxisMax = max($AxisMax,$PointMax); - $AxisMin = min($AxisMin,$PointMin); - } - $AutoMargin = (($AxisMax-$AxisMin)/100)*$XReleasePercent; - $Data["Axis"][$AxisID]["Min"] = $AxisMin-$AutoMargin; $Data["Axis"][$AxisID]["Max"] = $AxisMax+$AutoMargin; - } - $MaxDivs = floor($Height/$MinDivHeight); - - if ( $Mode == SCALE_MODE_ADDALL_START0 ) { $Data["Axis"][$AxisID]["Min"] = 0; } - - $Scale = $this->computeScale($Data["Axis"][$AxisID]["Min"],$Data["Axis"][$AxisID]["Max"],$MaxDivs,$Factors,$AxisID); - - $Data["Axis"][$AxisID]["Margin"] = $AxisParameter["Identity"] == AXIS_X ? $XMargin : $YMargin; - $Data["Axis"][$AxisID]["ScaleMin"] = $Scale["XMin"]; - $Data["Axis"][$AxisID]["ScaleMax"] = $Scale["XMax"]; - $Data["Axis"][$AxisID]["Rows"] = $Scale["Rows"]; - $Data["Axis"][$AxisID]["RowHeight"] = $Scale["RowHeight"]; - - if ( isset($Scale["Format"]) ) { $Data["Axis"][$AxisID]["Format"] = $Scale["Format"]; } - - if ( !isset($Data["Axis"][$AxisID]["Display"]) ) { $Data["Axis"][$AxisID]["Display"] = NULL; } - if ( !isset($Data["Axis"][$AxisID]["Format"]) ) { $Data["Axis"][$AxisID]["Format"] = NULL; } - if ( !isset($Data["Axis"][$AxisID]["Unit"]) ) { $Data["Axis"][$AxisID]["Unit"] = NULL; } - } - - /* Still no X axis */ - if ( $GotXAxis == FALSE ) - { - if ( $Abscissa != NULL ) - { - $Points = count($Data["Series"][$Abscissa]["Data"]); - if ( $AutoAxisLabels ) - $AxisName = isset($Data["Series"][$Abscissa]["Description"]) ? $Data["Series"][$Abscissa]["Description"] : NULL; - else - $AxisName = NULL; - } - else - { - $Points = 0; - $AxisName = isset($Data["XAxisName"]) ? $Data["XAxisName"] : NULL; - foreach($Data["Series"] as $SerieID => $SerieParameter) - { if ( $SerieParameter["isDrawable"] ) { $Points = max($Points,count($SerieParameter["Data"])); } } - } - - $AxisID = count($Data["Axis"]); - $Data["Axis"][$AxisID]["Identity"] = AXIS_X; - if ( $Pos == SCALE_POS_LEFTRIGHT ) { $Data["Axis"][$AxisID]["Position"] = AXIS_POSITION_BOTTOM; } else { $Data["Axis"][$AxisID]["Position"] = AXIS_POSITION_LEFT; } - if ( isset($Data["AbscissaName"]) ) { $Data["Axis"][$AxisID]["Name"] = $Data["AbscissaName"]; } - if ( $XMargin == AUTO ) - { - if ( $Pos == SCALE_POS_LEFTRIGHT ) - { $Height = $this->GraphAreaX2-$this->GraphAreaX1; } - else - { $Height = $this->GraphAreaY2-$this->GraphAreaY1; } - - if ( $Points == 1 ) - $Data["Axis"][$AxisID]["Margin"] = $Height / 2; - else - $Data["Axis"][$AxisID]["Margin"] = ($Height/$Points) / 2; - } - else - { $Data["Axis"][$AxisID]["Margin"] = $XMargin; } - $Data["Axis"][$AxisID]["Rows"] = $Points-1; - if ( !isset($Data["Axis"][$AxisID]["Display"]) ) { $Data["Axis"][$AxisID]["Display"] = NULL; } - if ( !isset($Data["Axis"][$AxisID]["Format"]) ) { $Data["Axis"][$AxisID]["Format"] = NULL; } - if ( !isset($Data["Axis"][$AxisID]["Unit"]) ) { $Data["Axis"][$AxisID]["Unit"] = NULL; } - } - - /* Do we need to reverse the abscissa position? */ - if ( $Pos != SCALE_POS_LEFTRIGHT ) - { - if ( $Data["AbsicssaPosition"] == AXIS_POSITION_BOTTOM ) - { $Data["AbsicssaPosition"] = AXIS_POSITION_LEFT; } - else - { $Data["AbsicssaPosition"] = AXIS_POSITION_RIGHT; } - } - $Data["Axis"][$AxisID]["Position"] = $Data["AbsicssaPosition"]; - - $this->DataSet->saveOrientation($Pos); - $this->DataSet->saveAxisConfig($Data["Axis"]); - $this->DataSet->saveYMargin($YMargin); - - $FontColorRo = $this->FontColorR; $FontColorGo = $this->FontColorG; $FontColorBo = $this->FontColorB; - - $AxisPos["L"] = $this->GraphAreaX1; $AxisPos["R"] = $this->GraphAreaX2; $AxisPos["T"] = $this->GraphAreaY1; $AxisPos["B"] = $this->GraphAreaY2; - foreach($Data["Axis"] as $AxisID => $Parameters) - { - if ( isset($Parameters["Color"]) ) - { - $AxisR = $Parameters["Color"]["R"]; $AxisG = $Parameters["Color"]["G"]; $AxisB = $Parameters["Color"]["B"]; - $TickR = $Parameters["Color"]["R"]; $TickG = $Parameters["Color"]["G"]; $TickB = $Parameters["Color"]["B"]; - $this->setFontProperties(array("R"=>$Parameters["Color"]["R"],"G"=>$Parameters["Color"]["G"],"B"=>$Parameters["Color"]["B"])); - } - else - { - $AxisR = $AxisRo; $AxisG = $AxisGo; $AxisB = $AxisBo; - $TickR = $TickRo; $TickG = $TickGo; $TickB = $TickBo; - $this->setFontProperties(array("R"=>$FontColorRo,"G"=>$FontColorGo,"B"=>$FontColorBo)); - } - - $LastValue = "w00t"; $ID = 1; - if ( $Parameters["Identity"] == AXIS_X ) - { - if ( $Pos == SCALE_POS_LEFTRIGHT ) - { - if ( $Parameters["Position"] == AXIS_POSITION_BOTTOM ) - { - if ( $LabelRotation == 0 ) { $LabelAlign = TEXT_ALIGN_TOPMIDDLE; $YLabelOffset = 2; } - if ( $LabelRotation > 0 && $LabelRotation < 190 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $YLabelOffset = 5; } - if ( $LabelRotation == 180 ) { $LabelAlign = TEXT_ALIGN_BOTTOMMIDDLE; $YLabelOffset = 5; } - if ( $LabelRotation > 180 && $LabelRotation < 360 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $YLabelOffset = 2; } - - if ( !$RemoveXAxis ) - { - if ( $Floating ) - { $FloatingOffset = $YMargin; $this->drawLine($this->GraphAreaX1+$Parameters["Margin"],$AxisPos["B"],$this->GraphAreaX2-$Parameters["Margin"],$AxisPos["B"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - else - { $FloatingOffset = 0; $this->drawLine($this->GraphAreaX1,$AxisPos["B"],$this->GraphAreaX2,$AxisPos["B"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - - if ( $DrawArrows ) { $this->drawArrow($this->GraphAreaX2-$Parameters["Margin"],$AxisPos["B"],$this->GraphAreaX2+($ArrowSize*2),$AxisPos["B"],array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } - } - - $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"]*2; - - if ($Parameters["Rows"] == 0 ) { $Step = $Width; } else { $Step = $Width / ($Parameters["Rows"]); } - - $MaxBottom = $AxisPos["B"]; - for($i=0;$i<=$Parameters["Rows"];$i++) - { - $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step*$i; - $YPos = $AxisPos["B"]; - - if ( $Abscissa != NULL ) - { if ( isset($Data["Series"][$Abscissa]["Data"][$i]) ) { $Value = $this->scaleFormat($Data["Series"][$Abscissa]["Data"][$i],$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]); } else { $Value = ""; } } - else - { - if ( isset($Parameters["ScaleMin"]) && isset ($Parameters["RowHeight"]) ) - $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]); - else - $Value = $i; - } - - $ID++; $Skipped = TRUE; - if ( $this->isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip) && !$RemoveXAxis) - { - $Bounds = $this->drawText($XPos,$YPos+$OuterTickWidth+$YLabelOffset,$Value,array("Angle"=>$LabelRotation,"Align"=>$LabelAlign)); - $TxtBottom = $YPos+$OuterTickWidth+2+($Bounds[0]["Y"]-$Bounds[2]["Y"]); - $MaxBottom = max($MaxBottom,$TxtBottom); - $LastValue = $Value; - $Skipped = FALSE; - } - - if ( $RemoveXAxis ) { $Skipped = FALSE; } - - if ( $Skipped ) - { - if ( $DrawXLines ) { $this->drawLine($XPos,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,$SkippedAxisColor); } - if ( ($SkippedInnerTickWidth !=0 || $SkippedOuterTickWidth != 0) && !$RemoveXAxis ) { $this->drawLine($XPos,$YPos-$SkippedInnerTickWidth,$XPos,$YPos+$SkippedOuterTickWidth,$SkippedTickColor); } - } - else - { - if ( $DrawXLines && ($XPos != $this->GraphAreaX1 && $XPos != $this->GraphAreaX2) ) { $this->drawLine($XPos,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } - if ( ($InnerTickWidth !=0 || $OuterTickWidth != 0) && !$RemoveXAxis ) { $this->drawLine($XPos,$YPos-$InnerTickWidth,$XPos,$YPos+$OuterTickWidth,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); } - } - } - - if ( isset($Parameters["Name"]) && !$RemoveXAxis) - { - $YPos = $MaxBottom+2; - $XPos = $this->GraphAreaX1+($this->GraphAreaX2-$this->GraphAreaX1)/2; - $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_TOPMIDDLE)); - $MaxBottom = $Bounds[0]["Y"]; - - $this->DataSet->Data["GraphArea"]["Y2"] = $MaxBottom + $this->FontSize; - } - - $AxisPos["B"] = $MaxBottom + $ScaleSpacing; - } - elseif ( $Parameters["Position"] == AXIS_POSITION_TOP ) - { - if ( $LabelRotation == 0 ) { $LabelAlign = TEXT_ALIGN_BOTTOMMIDDLE; $YLabelOffset = 2; } - if ( $LabelRotation > 0 && $LabelRotation < 190 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $YLabelOffset = 2; } - if ( $LabelRotation == 180 ) { $LabelAlign = TEXT_ALIGN_TOPMIDDLE; $YLabelOffset = 5; } - if ( $LabelRotation > 180 && $LabelRotation < 360 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $YLabelOffset = 5; } - - if ( !$RemoveXAxis ) - { - if ( $Floating ) - { $FloatingOffset = $YMargin; $this->drawLine($this->GraphAreaX1+$Parameters["Margin"],$AxisPos["T"],$this->GraphAreaX2-$Parameters["Margin"],$AxisPos["T"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - else - { $FloatingOffset = 0; $this->drawLine($this->GraphAreaX1,$AxisPos["T"],$this->GraphAreaX2,$AxisPos["T"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - - if ( $DrawArrows ) { $this->drawArrow($this->GraphAreaX2-$Parameters["Margin"],$AxisPos["T"],$this->GraphAreaX2+($ArrowSize*2),$AxisPos["T"],array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } - } - - $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"]*2; - - if ($Parameters["Rows"] == 0 ) { $Step = $Width; } else { $Step = $Width / $Parameters["Rows"]; } - - $MinTop = $AxisPos["T"]; - for($i=0;$i<=$Parameters["Rows"];$i++) - { - $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step*$i; - $YPos = $AxisPos["T"]; - - if ( $Abscissa != NULL ) - { if ( isset($Data["Series"][$Abscissa]["Data"][$i]) ) { $Value = $this->scaleFormat($Data["Series"][$Abscissa]["Data"][$i],$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]); } else { $Value = ""; } } - else - { - if ( isset($Parameters["ScaleMin"]) && isset ($Parameters["RowHeight"]) ) - $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]); - else - $Value = $i; - } - - $ID++; $Skipped = TRUE; - if ( $this->isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip) && !$RemoveXAxis) - { - $Bounds = $this->drawText($XPos,$YPos-$OuterTickWidth-$YLabelOffset,$Value,array("Angle"=>$LabelRotation,"Align"=>$LabelAlign)); - $TxtBox = $YPos-$OuterTickWidth-2-($Bounds[0]["Y"]-$Bounds[2]["Y"]); - $MinTop = min($MinTop,$TxtBox); - $LastValue = $Value; - $Skipped = FALSE; - } - - if ( $RemoveXAxis ) { $Skipped = FALSE; } - - if ( $Skipped ) - { - if ( $DrawXLines ) { $this->drawLine($XPos,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,$SkippedAxisColor); } - if ( ($SkippedInnerTickWidth !=0 || $SkippedOuterTickWidth != 0) && !$RemoveXAxis ) { $this->drawLine($XPos,$YPos+$SkippedInnerTickWidth,$XPos,$YPos-$SkippedOuterTickWidth,$SkippedTickColor); } - } - else - { - if ( $DrawXLines ) { $this->drawLine($XPos,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } - if ( ($InnerTickWidth !=0 || $OuterTickWidth != 0) && !$RemoveXAxis ) { $this->drawLine($XPos,$YPos+$InnerTickWidth,$XPos,$YPos-$OuterTickWidth,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); } - } - - } - - if ( isset($Parameters["Name"]) && !$RemoveXAxis ) - { - $YPos = $MinTop-2; - $XPos = $this->GraphAreaX1+($this->GraphAreaX2-$this->GraphAreaX1)/2; - $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); - $MinTop = $Bounds[2]["Y"]; - - $this->DataSet->Data["GraphArea"]["Y1"] = $MinTop; - } - - $AxisPos["T"] = $MinTop - $ScaleSpacing; - } - } - elseif ( $Pos == SCALE_POS_TOPBOTTOM ) - { - if ( $Parameters["Position"] == AXIS_POSITION_LEFT ) - { - if ( $LabelRotation == 0 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $XLabelOffset = -2; } - if ( $LabelRotation > 0 && $LabelRotation < 190 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $XLabelOffset = -6; } - if ( $LabelRotation == 180 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $XLabelOffset = -2; } - if ( $LabelRotation > 180 && $LabelRotation < 360 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $XLabelOffset = -5; } - - if ( !$RemoveXAxis ) - { - if ( $Floating ) - { $FloatingOffset = $YMargin; $this->drawLine($AxisPos["L"],$this->GraphAreaY1+$Parameters["Margin"],$AxisPos["L"],$this->GraphAreaY2-$Parameters["Margin"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - else - { $FloatingOffset = 0; $this->drawLine($AxisPos["L"],$this->GraphAreaY1,$AxisPos["L"],$this->GraphAreaY2,array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - - if ( $DrawArrows ) { $this->drawArrow($AxisPos["L"],$this->GraphAreaY2-$Parameters["Margin"],$AxisPos["L"],$this->GraphAreaY2+($ArrowSize*2),array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } - } - - $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"]*2; - - if ($Parameters["Rows"] == 0 ) { $Step = $Height; } else { $Step = $Height / $Parameters["Rows"]; } - - $MinLeft = $AxisPos["L"]; - for($i=0;$i<=$Parameters["Rows"];$i++) - { - $YPos = $this->GraphAreaY1 + $Parameters["Margin"] + $Step*$i; - $XPos = $AxisPos["L"]; - - if ( $Abscissa != NULL ) - { if ( isset($Data["Series"][$Abscissa]["Data"][$i]) ) { $Value = $this->scaleFormat($Data["Series"][$Abscissa]["Data"][$i],$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]); } else { $Value = ""; } } - else - { - if ( isset($Parameters["ScaleMin"]) && isset ($Parameters["RowHeight"]) ) - $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]); - else - $Value = $i; - } - - $ID++; $Skipped = TRUE; - if ( $this->isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip) && !$RemoveXAxis) - { - $Bounds = $this->drawText($XPos-$OuterTickWidth+$XLabelOffset,$YPos,$Value,array("Angle"=>$LabelRotation,"Align"=>$LabelAlign)); - $TxtBox = $XPos-$OuterTickWidth-2-($Bounds[1]["X"]-$Bounds[0]["X"]); - $MinLeft = min($MinLeft,$TxtBox); - $LastValue = $Value; - $Skipped = FALSE; - } - - if ( $RemoveXAxis ) { $Skipped = FALSE; } - - if ( $Skipped ) - { - if ( $DrawXLines ) { $this->drawLine($this->GraphAreaX1+$FloatingOffset,$YPos,$this->GraphAreaX2-$FloatingOffset,$YPos,$SkippedAxisColor); } - if ( ($SkippedInnerTickWidth !=0 || $SkippedOuterTickWidth != 0) && !$RemoveXAxis ) { $this->drawLine($XPos-$SkippedOuterTickWidth,$YPos,$XPos+$SkippedInnerTickWidth,$YPos,$SkippedTickColor); } - } - else - { - if ( $DrawXLines && ($YPos != $this->GraphAreaY1 && $YPos != $this->GraphAreaY2) ) { $this->drawLine($this->GraphAreaX1+$FloatingOffset,$YPos,$this->GraphAreaX2-$FloatingOffset,$YPos,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } - if ( ($InnerTickWidth !=0 || $OuterTickWidth != 0) && !$RemoveXAxis ) { $this->drawLine($XPos-$OuterTickWidth,$YPos,$XPos+$InnerTickWidth,$YPos,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); } - } - - } - if ( isset($Parameters["Name"]) && !$RemoveXAxis ) - { - $XPos = $MinLeft-2; - $YPos = $this->GraphAreaY1+($this->GraphAreaY2-$this->GraphAreaY1)/2; - $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE,"Angle"=>90)); - $MinLeft = $Bounds[0]["X"]; - - $this->DataSet->Data["GraphArea"]["X1"] = $MinLeft; - } - - $AxisPos["L"] = $MinLeft - $ScaleSpacing; - } - elseif ( $Parameters["Position"] == AXIS_POSITION_RIGHT ) - { - if ( $LabelRotation == 0 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $XLabelOffset = 2; } - if ( $LabelRotation > 0 && $LabelRotation < 190 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $XLabelOffset = 6; } - if ( $LabelRotation == 180 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $XLabelOffset = 5; } - if ( $LabelRotation > 180 && $LabelRotation < 360 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $XLabelOffset = 7; } - - if ( !$RemoveXAxis ) - { - if ( $Floating ) - { $FloatingOffset = $YMargin; $this->drawLine($AxisPos["R"],$this->GraphAreaY1+$Parameters["Margin"],$AxisPos["R"],$this->GraphAreaY2-$Parameters["Margin"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - else - { $FloatingOffset = 0; $this->drawLine($AxisPos["R"],$this->GraphAreaY1,$AxisPos["R"],$this->GraphAreaY2,array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - - if ( $DrawArrows ) { $this->drawArrow($AxisPos["R"],$this->GraphAreaY2-$Parameters["Margin"],$AxisPos["R"],$this->GraphAreaY2+($ArrowSize*2),array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } - } - - $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"]*2; - - if ($Parameters["Rows"] == 0 ) { $Step = $Height; } else { $Step = $Height / $Parameters["Rows"]; } - - $MaxRight = $AxisPos["R"]; - for($i=0;$i<=$Parameters["Rows"];$i++) - { - $YPos = $this->GraphAreaY1 + $Parameters["Margin"] + $Step*$i; - $XPos = $AxisPos["R"]; - - if ( $Abscissa != NULL ) - { if ( isset($Data["Series"][$Abscissa]["Data"][$i]) ) { $Value = $this->scaleFormat($Data["Series"][$Abscissa]["Data"][$i],$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]); } else { $Value = ""; } } - else - { - if ( isset($Parameters["ScaleMin"]) && isset ($Parameters["RowHeight"]) ) - $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]); - else - $Value = $i; - } - - $ID++; $Skipped = TRUE; - if ( $this->isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip) && !$RemoveXAxis) - { - $Bounds = $this->drawText($XPos+$OuterTickWidth+$XLabelOffset,$YPos,$Value,array("Angle"=>$LabelRotation,"Align"=>$LabelAlign)); - $TxtBox = $XPos+$OuterTickWidth+2+($Bounds[1]["X"]-$Bounds[0]["X"]); - $MaxRight = max($MaxRight,$TxtBox); - $LastValue = $Value; - $Skipped = FALSE; - } - - if ( $RemoveXAxis ) { $Skipped = FALSE; } - - if ( $Skipped ) - { - if ( $DrawXLines ) { $this->drawLine($this->GraphAreaX1+$FloatingOffset,$YPos,$this->GraphAreaX2-$FloatingOffset,$YPos,$SkippedAxisColor); } - if ( ($SkippedInnerTickWidth != 0 || $SkippedOuterTickWidth != 0) && !$RemoveXAxis ) { $this->drawLine($XPos+$SkippedOuterTickWidth,$YPos,$XPos-$SkippedInnerTickWidth,$YPos,$SkippedTickColor); } - } - else - { - if ( $DrawXLines ) { $this->drawLine($this->GraphAreaX1+$FloatingOffset,$YPos,$this->GraphAreaX2-$FloatingOffset,$YPos,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } - if ( ($InnerTickWidth != 0 || $OuterTickWidth != 0) && !$RemoveXAxis ) { $this->drawLine($XPos+$OuterTickWidth,$YPos,$XPos-$InnerTickWidth,$YPos,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); } - } - - } - - if ( isset($Parameters["Name"]) && !$RemoveXAxis) - { - $XPos = $MaxRight+4; - $YPos = $this->GraphAreaY1+($this->GraphAreaY2-$this->GraphAreaY1)/2; - $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE,"Angle"=>270)); - $MaxRight = $Bounds[1]["X"]; - - $this->DataSet->Data["GraphArea"]["X2"] = $MaxRight + $this->FontSize; - } - - $AxisPos["R"] = $MaxRight + $ScaleSpacing; - } - } - } - - - - if ( $Parameters["Identity"] == AXIS_Y ) - { - if ( $Pos == SCALE_POS_LEFTRIGHT ) - { - if ( $Parameters["Position"] == AXIS_POSITION_LEFT ) - { - - if ( $Floating ) - { $FloatingOffset = $XMargin; $this->drawLine($AxisPos["L"],$this->GraphAreaY1+$Parameters["Margin"],$AxisPos["L"],$this->GraphAreaY2-$Parameters["Margin"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - else - { $FloatingOffset = 0; $this->drawLine($AxisPos["L"],$this->GraphAreaY1,$AxisPos["L"],$this->GraphAreaY2,array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - - if ( $DrawArrows ) { $this->drawArrow($AxisPos["L"],$this->GraphAreaY1+$Parameters["Margin"],$AxisPos["L"],$this->GraphAreaY1-($ArrowSize*2),array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } - - $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"]*2; - $Step = $Height / $Parameters["Rows"]; $SubTicksSize = $Step /2; $MinLeft = $AxisPos["L"]; - $LastY = NULL; - for($i=0;$i<=$Parameters["Rows"];$i++) - { - $YPos = $this->GraphAreaY2 - $Parameters["Margin"] - $Step*$i; - $XPos = $AxisPos["L"]; - $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Parameters["Display"],$Parameters["Format"],$Parameters["Unit"]); - - if ( $i%2 == 1 ) { $BGColor = array("R"=>$BackgroundR1,"G"=>$BackgroundG1,"B"=>$BackgroundB1,"Alpha"=>$BackgroundAlpha1); } else { $BGColor = array("R"=>$BackgroundR2,"G"=>$BackgroundG2,"B"=>$BackgroundB2,"Alpha"=>$BackgroundAlpha2); } - if ( $LastY != NULL && $CycleBackground && ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) )) { $this->drawFilledRectangle($this->GraphAreaX1+$FloatingOffset,$LastY,$this->GraphAreaX2-$FloatingOffset,$YPos,$BGColor); } - - if ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) ) { $this->drawLine($this->GraphAreaX1+$FloatingOffset,$YPos,$this->GraphAreaX2-$FloatingOffset,$YPos,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } - - if ( $DrawSubTicks && $i != $Parameters["Rows"] ) - $this->drawLine($XPos-$OuterSubTickWidth,$YPos-$SubTicksSize,$XPos+$InnerSubTickWidth,$YPos-$SubTicksSize,array("R"=>$SubTickR,"G"=>$SubTickG,"B"=>$SubTickB,"Alpha"=>$SubTickAlpha)); - - $this->drawLine($XPos-$OuterTickWidth,$YPos,$XPos+$InnerTickWidth,$YPos,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); - $Bounds = $this->drawText($XPos-$OuterTickWidth-2,$YPos,$Value,array("Align"=>TEXT_ALIGN_MIDDLERIGHT)); - $TxtLeft = $XPos-$OuterTickWidth-2-($Bounds[1]["X"]-$Bounds[0]["X"]); - $MinLeft = min($MinLeft,$TxtLeft); - - $LastY = $YPos; - } - - if ( isset($Parameters["Name"]) ) - { - $XPos = $MinLeft-2; - $YPos = $this->GraphAreaY1+($this->GraphAreaY2-$this->GraphAreaY1)/2; - $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE,"Angle"=>90)); - $MinLeft = $Bounds[2]["X"]; - - $this->DataSet->Data["GraphArea"]["X1"] = $MinLeft; - } - - $AxisPos["L"] = $MinLeft - $ScaleSpacing; - } - elseif ( $Parameters["Position"] == AXIS_POSITION_RIGHT ) - { - if ( $Floating ) - { $FloatingOffset = $XMargin; $this->drawLine($AxisPos["R"],$this->GraphAreaY1+$Parameters["Margin"],$AxisPos["R"],$this->GraphAreaY2-$Parameters["Margin"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - else - { $FloatingOffset = 0; $this->drawLine($AxisPos["R"],$this->GraphAreaY1,$AxisPos["R"],$this->GraphAreaY2,array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - - if ( $DrawArrows ) { $this->drawArrow($AxisPos["R"],$this->GraphAreaY1+$Parameters["Margin"],$AxisPos["R"],$this->GraphAreaY1-($ArrowSize*2),array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } - - $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"]*2; - $Step = $Height / $Parameters["Rows"]; $SubTicksSize = $Step /2; $MaxLeft = $AxisPos["R"]; - $LastY = NULL; - for($i=0;$i<=$Parameters["Rows"];$i++) - { - $YPos = $this->GraphAreaY2 - $Parameters["Margin"] - $Step*$i; - $XPos = $AxisPos["R"]; - $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Parameters["Display"],$Parameters["Format"],$Parameters["Unit"]); - - if ( $i%2 == 1 ) { $BGColor = array("R"=>$BackgroundR1,"G"=>$BackgroundG1,"B"=>$BackgroundB1,"Alpha"=>$BackgroundAlpha1); } else { $BGColor = array("R"=>$BackgroundR2,"G"=>$BackgroundG2,"B"=>$BackgroundB2,"Alpha"=>$BackgroundAlpha2); } - if ( $LastY != NULL && $CycleBackground && ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) )) { $this->drawFilledRectangle($this->GraphAreaX1+$FloatingOffset,$LastY,$this->GraphAreaX2-$FloatingOffset,$YPos,$BGColor); } - - if ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) ) { $this->drawLine($this->GraphAreaX1+$FloatingOffset,$YPos,$this->GraphAreaX2-$FloatingOffset,$YPos,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } - - if ( $DrawSubTicks && $i != $Parameters["Rows"] ) - $this->drawLine($XPos-$OuterSubTickWidth,$YPos-$SubTicksSize,$XPos+$InnerSubTickWidth,$YPos-$SubTicksSize,array("R"=>$SubTickR,"G"=>$SubTickG,"B"=>$SubTickB,"Alpha"=>$SubTickAlpha)); - - $this->drawLine($XPos-$InnerTickWidth,$YPos,$XPos+$OuterTickWidth,$YPos,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); - $Bounds = $this->drawText($XPos+$OuterTickWidth+2,$YPos,$Value,array("Align"=>TEXT_ALIGN_MIDDLELEFT)); - $TxtLeft = $XPos+$OuterTickWidth+2+($Bounds[1]["X"]-$Bounds[0]["X"]); - $MaxLeft = max($MaxLeft,$TxtLeft); - - $LastY = $YPos; - } - - if ( isset($Parameters["Name"]) ) - { - $XPos = $MaxLeft+6; - $YPos = $this->GraphAreaY1+($this->GraphAreaY2-$this->GraphAreaY1)/2; - $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE,"Angle"=>270)); - $MaxLeft = $Bounds[2]["X"]; - - $this->DataSet->Data["GraphArea"]["X2"] = $MaxLeft + $this->FontSize; - } - $AxisPos["R"] = $MaxLeft + $ScaleSpacing; - } - } - elseif ( $Pos == SCALE_POS_TOPBOTTOM ) - { - if ( $Parameters["Position"] == AXIS_POSITION_TOP ) - { - if ( $Floating ) - { $FloatingOffset = $XMargin; $this->drawLine($this->GraphAreaX1+$Parameters["Margin"],$AxisPos["T"],$this->GraphAreaX2-$Parameters["Margin"],$AxisPos["T"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - else - { $FloatingOffset = 0; $this->drawLine($this->GraphAreaX1,$AxisPos["T"],$this->GraphAreaX2,$AxisPos["T"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - - if ( $DrawArrows ) { $this->drawArrow($this->GraphAreaX2-$Parameters["Margin"],$AxisPos["T"],$this->GraphAreaX2+($ArrowSize*2),$AxisPos["T"],array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } - - $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"]*2; - $Step = $Width / $Parameters["Rows"]; $SubTicksSize = $Step /2; $MinTop = $AxisPos["T"]; - $LastX = NULL; - for($i=0;$i<=$Parameters["Rows"];$i++) - { - $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step*$i; - $YPos = $AxisPos["T"]; - $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Parameters["Display"],$Parameters["Format"],$Parameters["Unit"]); - - if ( $i%2 == 1 ) { $BGColor = array("R"=>$BackgroundR1,"G"=>$BackgroundG1,"B"=>$BackgroundB1,"Alpha"=>$BackgroundAlpha1); } else { $BGColor = array("R"=>$BackgroundR2,"G"=>$BackgroundG2,"B"=>$BackgroundB2,"Alpha"=>$BackgroundAlpha2); } - if ( $LastX != NULL && $CycleBackground && ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) )) { $this->drawFilledRectangle($LastX,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,$BGColor); } - - if ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) ) { $this->drawLine($XPos,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } - - if ( $DrawSubTicks && $i != $Parameters["Rows"] ) - $this->drawLine($XPos+$SubTicksSize,$YPos-$OuterSubTickWidth,$XPos+$SubTicksSize,$YPos+$InnerSubTickWidth,array("R"=>$SubTickR,"G"=>$SubTickG,"B"=>$SubTickB,"Alpha"=>$SubTickAlpha)); - - $this->drawLine($XPos,$YPos-$OuterTickWidth,$XPos,$YPos+$InnerTickWidth,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); - $Bounds = $this->drawText($XPos,$YPos-$OuterTickWidth-2,$Value,array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); - $TxtHeight = $YPos-$OuterTickWidth-2-($Bounds[1]["Y"]-$Bounds[2]["Y"]); - $MinTop = min($MinTop,$TxtHeight); - - $LastX = $XPos; - } - - if ( isset($Parameters["Name"]) ) - { - $YPos = $MinTop-2; - $XPos = $this->GraphAreaX1+($this->GraphAreaX2-$this->GraphAreaX1)/2; - $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); - $MinTop = $Bounds[2]["Y"]; - - $this->DataSet->Data["GraphArea"]["Y1"] = $MinTop; - } - - $AxisPos["T"] = $MinTop - $ScaleSpacing; - } - elseif ( $Parameters["Position"] == AXIS_POSITION_BOTTOM ) - { - if ( $Floating ) - { $FloatingOffset = $XMargin; $this->drawLine($this->GraphAreaX1+$Parameters["Margin"],$AxisPos["B"],$this->GraphAreaX2-$Parameters["Margin"],$AxisPos["B"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - else - { $FloatingOffset = 0; $this->drawLine($this->GraphAreaX1,$AxisPos["B"],$this->GraphAreaX2,$AxisPos["B"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - - if ( $DrawArrows ) { $this->drawArrow($this->GraphAreaX2-$Parameters["Margin"],$AxisPos["B"],$this->GraphAreaX2+($ArrowSize*2),$AxisPos["B"],array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } - - $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"]*2; - $Step = $Width / $Parameters["Rows"]; $SubTicksSize = $Step /2; $MaxBottom = $AxisPos["B"]; - $LastX = NULL; - for($i=0;$i<=$Parameters["Rows"];$i++) - { - $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step*$i; - $YPos = $AxisPos["B"]; - $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Parameters["Display"],$Parameters["Format"],$Parameters["Unit"]); - - if ( $i%2 == 1 ) { $BGColor = array("R"=>$BackgroundR1,"G"=>$BackgroundG1,"B"=>$BackgroundB1,"Alpha"=>$BackgroundAlpha1); } else { $BGColor = array("R"=>$BackgroundR2,"G"=>$BackgroundG2,"B"=>$BackgroundB2,"Alpha"=>$BackgroundAlpha2); } - if ( $LastX != NULL && $CycleBackground && ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) )) { $this->drawFilledRectangle($LastX,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,$BGColor); } - - if ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) ) { $this->drawLine($XPos,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } - - if ( $DrawSubTicks && $i != $Parameters["Rows"] ) - $this->drawLine($XPos+$SubTicksSize,$YPos-$OuterSubTickWidth,$XPos+$SubTicksSize,$YPos+$InnerSubTickWidth,array("R"=>$SubTickR,"G"=>$SubTickG,"B"=>$SubTickB,"Alpha"=>$SubTickAlpha)); - - $this->drawLine($XPos,$YPos-$OuterTickWidth,$XPos,$YPos+$InnerTickWidth,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); - $Bounds = $this->drawText($XPos,$YPos+$OuterTickWidth+2,$Value,array("Align"=>TEXT_ALIGN_TOPMIDDLE)); - $TxtHeight = $YPos+$OuterTickWidth+2+($Bounds[1]["Y"]-$Bounds[2]["Y"]); - $MaxBottom = max($MaxBottom,$TxtHeight); - - $LastX = $XPos; - } - - if ( isset($Parameters["Name"]) ) - { - $YPos = $MaxBottom+2; - $XPos = $this->GraphAreaX1+($this->GraphAreaX2-$this->GraphAreaX1)/2; - $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_TOPMIDDLE)); - $MaxBottom = $Bounds[0]["Y"]; - - $this->DataSet->Data["GraphArea"]["Y2"] = $MaxBottom + $this->FontSize; - } - - $AxisPos["B"] = $MaxBottom + $ScaleSpacing; - } - } - } - } - } - - function isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip) - { - if ( $LabelingMethod == LABELING_DIFFERENT && $Value != $LastValue ) { return(TRUE); } - if ( $LabelingMethod == LABELING_DIFFERENT && $Value == $LastValue ) { return(FALSE); } - if ( $LabelingMethod == LABELING_ALL && $LabelSkip == 0 ) { return(TRUE); } - if ( $LabelingMethod == LABELING_ALL && ($ID+$LabelSkip) % ($LabelSkip+1) != 1 ) { return(FALSE); } - - return(TRUE); - } - - /* Compute the scale, check for the best visual factors */ - function computeScale($XMin,$XMax,$MaxDivs,$Factors,$AxisID=0) - { - /* Compute each factors */ - $Results = ""; - foreach ($Factors as $Key => $Factor) - $Results[$Factor] = $this->processScale($XMin,$XMax,$MaxDivs,array($Factor),$AxisID); - - /* Remove scales that are creating to much decimals */ - $GoodScaleFactors = ""; - foreach ($Results as $Key => $Result) - { - $Decimals = preg_split("/\./",$Result["RowHeight"]); - if ( (!isset($Decimals[1])) || (strlen($Decimals[1]) < 6) ) { $GoodScaleFactors[] = $Key; } - } - - /* Found no correct scale, shame,... returns the 1st one as default */ - if ( $GoodScaleFactors == "" ) { return($Results[$Factors[0]]); } - - /* Find the factor that cause the maximum number of Rows */ - $MaxRows = 0; $BestFactor = 0; - foreach($GoodScaleFactors as $Key => $Factor) - { if ( $Results[$Factor]["Rows"] > $MaxRows ) { $MaxRows = $Results[$Factor]["Rows"]; $BestFactor = $Factor; } } - - /* Return the best visual scale */ - return($Results[$BestFactor]); - } - - /* Compute the best matching scale based on size & factors */ - function processScale($XMin,$XMax,$MaxDivs,$Factors,$AxisID) - { - $ScaleHeight = abs(ceil($XMax)-floor($XMin)); - - if ( isset($this->DataSet->Data["Axis"][$AxisID]["Format"]) ) - $Format = $this->DataSet->Data["Axis"][$AxisID]["Format"]; - else - $Format = NULL; - - if ( isset($this->DataSet->Data["Axis"][$AxisID]["Display"]) ) - $Mode = $this->DataSet->Data["Axis"][$AxisID]["Display"]; - else - $Mode = AXIS_FORMAT_DEFAULT; - - $Scale = ""; - if ( $XMin != $XMax ) - { - $Found = FALSE; $Rescaled = FALSE; $Scaled10Factor = .0001; $Result = 0; - while(!$Found) - { - foreach($Factors as $Key => $Factor) - { - if ( !$Found ) - { - if ( !($this->modulo($XMin,$Factor*$Scaled10Factor) == 0) || ($XMin != floor($XMin))) { $XMinRescaled = floor($XMin/($Factor*$Scaled10Factor))*$Factor*$Scaled10Factor; } else { $XMinRescaled = $XMin; } - if ( !($this->modulo($XMax,$Factor*$Scaled10Factor) == 0) || ($XMax != floor($XMax))) { $XMaxRescaled = floor($XMax/($Factor*$Scaled10Factor))*$Factor*$Scaled10Factor+($Factor*$Scaled10Factor); } else { $XMaxRescaled = $XMax; } - $ScaleHeightRescaled = abs($XMaxRescaled-$XMinRescaled); - - if ( !$Found && floor($ScaleHeightRescaled/($Factor*$Scaled10Factor)) <= $MaxDivs ) { $Found = TRUE; $Rescaled = TRUE; $Result = $Factor * $Scaled10Factor; } - } - } - $Scaled10Factor = $Scaled10Factor * 10; - } - - /* ReCall Min / Max / Height */ - if ( $Rescaled ) { $XMin = $XMinRescaled; $XMax = $XMaxRescaled; $ScaleHeight = $ScaleHeightRescaled; } - - /* Compute rows size */ - $Rows = floor($ScaleHeight / $Result); if ( $Rows == 0 ) { $Rows = 1; } - $RowHeight = $ScaleHeight / $Rows; - - /* Return the results */ - $Scale["Rows"] = $Rows; $Scale["RowHeight"] = $RowHeight; $Scale["XMin"] = $XMin; $Scale["XMax"] = $XMax; - - /* Compute the needed decimals for the metric view to avoid repetition of the same X Axis labels */ - if ( $Mode == AXIS_FORMAT_METRIC && $Format == NULL ) - { - $Done = FALSE; $GoodDecimals = 0; - for($Decimals=0;$Decimals<=10;$Decimals++) - { - if ( !$Done ) - { - $LastLabel = "zob"; $ScaleOK = TRUE; - for($i=0;$i<=$Rows;$i++) - { - $Value = $XMin + $i*$RowHeight; - $Label = $this->scaleFormat($Value,AXIS_FORMAT_METRIC,$Decimals); - - if ( $LastLabel == $Label ) { $ScaleOK = FALSE; } - $LastLabel = $Label; - } - if ( $ScaleOK ) { $Done = TRUE; $GoodDecimals = $Decimals; } - } - } - - $Scale["Format"] = $GoodDecimals; - } - } - else - { - /* If all values are the same we keep a +1/-1 scale */ - $Rows = 2; $XMin = $XMax-1; $XMax = $XMax+1; $RowHeight = 1; - - /* Return the results */ - $Scale["Rows"] = $Rows; $Scale["RowHeight"] = $RowHeight; $Scale["XMin"] = $XMin; $Scale["XMax"] = $XMax; - } - - return($Scale); - } - - function modulo($Value1,$Value2) - { - if (floor($Value2) == 0) { return(0); } - if (floor($Value2) != 0) { return($Value1 % $Value2); } - - $MinValue = min($Value1,$Value2); $Factor = 10; - while ( floor($MinValue*$Factor) == 0 ) - { $Factor = $Factor * 10; } - - return(($Value1*$Factor) % ($Value2*$Factor)); - } - - /* Draw an X threshold */ - function drawXThreshold($Value,$Format="") - { - $R = isset($Format["R"]) ? $Format["R"] : 255; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 50; - $Weight = isset($Format["Weight"]) ? $Format["Weight"] : NULL; - $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : 6; - $Wide = isset($Format["Wide"]) ? $Format["Wide"] : FALSE; - $WideFactor = isset($Format["WideFactor"]) ? $Format["WideFactor"] : 5; - $WriteCaption = isset($Format["WriteCaption"]) ? $Format["WriteCaption"] : FALSE; - $Caption = isset($Format["Caption"]) ? $Format["Caption"] : NULL; - $CaptionAlign = isset($Format["CaptionAlign"]) ? $Format["CaptionAlign"] : CAPTION_LEFT_TOP; - $CaptionOffset = isset($Format["CaptionOffset"]) ? $Format["CaptionOffset"] : 5; - $CaptionR = isset($Format["CaptionR"]) ? $Format["CaptionR"] : 255; - $CaptionG = isset($Format["CaptionG"]) ? $Format["CaptionG"] : 255; - $CaptionB = isset($Format["CaptionB"]) ? $Format["CaptionB"] : 255; - $CaptionAlpha = isset($Format["CaptionAlpha"]) ? $Format["CaptionAlpha"] : 100; - $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : TRUE; - $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : FALSE; - $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 3; - $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : TRUE; - $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 3; - $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0; - $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0; - $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0; - $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 30; - $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : ""; - $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 255; - $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 255; - $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 255; - $BoxBorderAlpha = isset($Format["BoxBorderAlpha"]) ? $Format["BoxBorderAlpha"] : 100; - $ValueIsLabel = isset($Format["ValueIsLabel"]) ? $Format["ValueIsLabel"] : FALSE; - - $Data = $this->DataSet->getData(); - $AbscissaMargin = $this->getAbscissaMargin($Data); - $XScale = $this->scaleGetXSettings(); - - if ( is_array($Value) ) { foreach ($Value as $Key => $ID) { $this->drawXThreshold($ID,$Format); } return(0); } - - if ( $ValueIsLabel ) - { - $Format["ValueIsLabel"] = FALSE; - foreach($Data["Series"][$Data["Abscissa"]]["Data"] as $Key => $SerieValue) - { if ( $SerieValue == $Value ) { $this->drawXThreshold($Key,$Format); } } - - return(0); - } - - $CaptionSettings = array("DrawBox"=>$DrawBox,"DrawBoxBorder"=>$DrawBoxBorder,"BorderOffset"=>$BorderOffset,"BoxRounded"=>$BoxRounded,"RoundedRadius"=>$RoundedRadius, - "BoxR"=>$BoxR,"BoxG"=>$BoxG,"BoxB"=>$BoxB,"BoxAlpha"=>$BoxAlpha,"BoxSurrounding"=>$BoxSurrounding, - "BoxBorderR"=>$BoxBorderR,"BoxBorderG"=>$BoxBorderG,"BoxBorderB"=>$BoxBorderB,"BoxBorderAlpha"=>$BoxBorderAlpha, - "R"=>$CaptionR,"G"=>$CaptionG,"B"=>$CaptionB,"Alpha"=>$CaptionAlpha); - - if ( $Caption == NULL ) - { - if ( isset($Data["Abscissa"]) ) - { - if ( isset($Data["Series"][$Data["Abscissa"]]["Data"][$Value]) ) - $Caption = $Data["Series"][$Data["Abscissa"]]["Data"][$Value]; - else - $Caption = $Value; - } - else - $Caption = $Value; - } - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - $XStep = (($this->GraphAreaX2 - $this->GraphAreaX1) - $XScale[0] *2 ) / ($XScale[1] == 0 ? 1 : $XScale[1]); - $XPos = $this->GraphAreaX1 + $XScale[0] + $XStep * $Value; - $YPos1 = $this->GraphAreaY1 + $Data["YMargin"]; - $YPos2 = $this->GraphAreaY2 - $Data["YMargin"]; - - if ( $XPos >= $this->GraphAreaX1 + $AbscissaMargin && $XPos <= $this->GraphAreaX2 - $AbscissaMargin ) - { - $this->drawLine($XPos,$YPos1,$XPos,$YPos2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); - - if ( $Wide ) - { - $this->drawLine($XPos-1,$YPos1,$XPos-1,$YPos2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); - $this->drawLine($XPos+1,$YPos1,$XPos+1,$YPos2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); - } - - if ( $WriteCaption ) - { - if ( $CaptionAlign == CAPTION_LEFT_TOP ) - { $Y = $YPos1 + $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE; } - else - { $Y = $YPos2 - $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_BOTTOMMIDDLE; } - - $this->drawText($XPos,$Y,$Caption,$CaptionSettings); - } - - return(array("X"=>$XPos)); - } - } - elseif( $Data["Orientation"] == SCALE_POS_TOPBOTTOM ) - { - $XStep = (($this->GraphAreaY2 - $this->GraphAreaY1) - $XScale[0] *2 ) / $XScale[1]; - $XPos = $this->GraphAreaY1 + $XScale[0] + $XStep * $Value; - $YPos1 = $this->GraphAreaX1 + $Data["YMargin"]; - $YPos2 = $this->GraphAreaX2 - $Data["YMargin"]; - - if ( $XPos >= $this->GraphAreaY1 + $AbscissaMargin && $XPos <= $this->GraphAreaY2 - $AbscissaMargin ) - { - $this->drawLine($YPos1,$XPos,$YPos2,$XPos,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); - - if ( $Wide ) - { - $this->drawLine($YPos1,$XPos-1,$YPos2,$XPos-1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); - $this->drawLine($YPos1,$XPos+1,$YPos2,$XPos+1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); - } - - if ( $WriteCaption ) - { - if ( $CaptionAlign == CAPTION_LEFT_TOP ) - { $Y = $YPos1 + $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLELEFT; } - else - { $Y = $YPos2 - $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLERIGHT; } - - $this->drawText($Y,$XPos,$Caption,$CaptionSettings); - } - - return(array("X"=>$XPos)); - } - } - } - - /* Draw an X threshold area */ - function drawXThresholdArea($Value1,$Value2,$Format="") - { - $R = isset($Format["R"]) ? $Format["R"] : 255; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 20; - $Border = isset($Format["Border"]) ? $Format["Border"] : TRUE; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B; - $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha + 20; - $BorderTicks = isset($Format["BorderTicks"]) ? $Format["BorderTicks"] : 2; - $AreaName = isset($Format["AreaName"]) ? $Format["AreaName"] : NULL; - $NameAngle = isset($Format["NameAngle"]) ? $Format["NameAngle"] : ZONE_NAME_ANGLE_AUTO; - $NameR = isset($Format["NameR"]) ? $Format["NameR"] : 255; - $NameG = isset($Format["NameG"]) ? $Format["NameG"] : 255; - $NameB = isset($Format["NameB"]) ? $Format["NameB"] : 255; - $NameAlpha = isset($Format["NameAlpha"]) ? $Format["NameAlpha"] : 100; - $DisableShadowOnArea = isset($Format["DisableShadowOnArea"]) ? $Format["DisableShadowOnArea"] : TRUE; - - $RestoreShadow = $this->Shadow; - if ( $DisableShadowOnArea && $this->Shadow ) { $this->Shadow = FALSE; } - - if ($BorderAlpha >100) { $BorderAlpha = 100;} - - $Data = $this->DataSet->getData(); - $XScale = $this->scaleGetXSettings(); - $AbscissaMargin = $this->getAbscissaMargin($Data); - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - $XStep = (($this->GraphAreaX2 - $this->GraphAreaX1) - $XScale[0] *2 ) / $XScale[1]; - $XPos1 = $this->GraphAreaX1 + $XScale[0] + $XStep * $Value1; - $XPos2 = $this->GraphAreaX1 + $XScale[0] + $XStep * $Value2; - $YPos1 = $this->GraphAreaY1 + $Data["YMargin"]; - $YPos2 = $this->GraphAreaY2 - $Data["YMargin"]; - - if ( $XPos1 < $this->GraphAreaX1 + $XScale[0] ) { $XPos1 = $this->GraphAreaX1 + $XScale[0]; } - if ( $XPos1 > $this->GraphAreaX2 - $XScale[0] ) { $XPos1 = $this->GraphAreaX2 - $XScale[0]; } - if ( $XPos2 < $this->GraphAreaX1 + $XScale[0] ) { $XPos2 = $this->GraphAreaX1 + $XScale[0]; } - if ( $XPos2 > $this->GraphAreaX2 - $XScale[0] ) { $XPos2 = $this->GraphAreaX2 - $XScale[0]; } - - $this->drawFilledRectangle($XPos1,$YPos1,$XPos2,$YPos2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); - - if ( $Border ) - { - $this->drawLine($XPos1,$YPos1,$XPos1,$YPos2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); - $this->drawLine($XPos2,$YPos1,$XPos2,$YPos2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); - } - - if ( $AreaName != NULL ) - { - $XPos = ($XPos2-$XPos1)/2 + $XPos1; - $YPos = ($YPos2-$YPos1)/2 + $YPos1; - - if ( $NameAngle == ZONE_NAME_ANGLE_AUTO ) - { - $TxtPos = $this->getTextBox($XPos,$YPos,$this->FontName,$this->FontSize,0,$AreaName); - $TxtWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"]; - if ( abs($XPos2 - $XPos1) > $TxtWidth ) { $NameAngle = 0; } else { $NameAngle = 90; } - } - $this->Shadow = $RestoreShadow; - $this->drawText($XPos,$YPos,$AreaName,array("R"=>$NameR,"G"=>$NameG,"B"=>$NameB,"Alpha"=>$NameAlpha,"Angle"=>$NameAngle,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE)); - if ( $DisableShadowOnArea ) { $this->Shadow = FALSE; } - } - - $this->Shadow = $RestoreShadow; - return(array("X1"=>$XPos1,"X2"=>$XPos2)); - } - elseif ( $Data["Orientation"] == SCALE_POS_TOPBOTTOM ) - { - $XStep = (($this->GraphAreaY2 - $this->GraphAreaY1) - $XScale[0] *2 ) / $XScale[1]; - $XPos1 = $this->GraphAreaY1 + $XScale[0] + $XStep * $Value1; - $XPos2 = $this->GraphAreaY1 + $XScale[0] + $XStep * $Value2; - $YPos1 = $this->GraphAreaX1 + $Data["YMargin"]; - $YPos2 = $this->GraphAreaX2 - $Data["YMargin"]; - - if ( $XPos1 < $this->GraphAreaY1 + $XScale[0] ) { $XPos1 = $this->GraphAreaY1 + $XScale[0]; } - if ( $XPos1 > $this->GraphAreaY2 - $XScale[0] ) { $XPos1 = $this->GraphAreaY2 - $XScale[0]; } - if ( $XPos2 < $this->GraphAreaY1 + $XScale[0] ) { $XPos2 = $this->GraphAreaY1 + $XScale[0]; } - if ( $XPos2 > $this->GraphAreaY2 - $XScale[0] ) { $XPos2 = $this->GraphAreaY2 - $XScale[0]; } - - $this->drawFilledRectangle($YPos1,$XPos1,$YPos2,$XPos2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); - - if ( $Border ) - { - $this->drawLine($YPos1,$XPos1,$YPos2,$XPos1,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); - $this->drawLine($YPos1,$XPos2,$YPos2,$XPos2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); - } - - if ( $AreaName != NULL ) - { - $XPos = ($XPos2-$XPos1)/2 + $XPos1; - $YPos = ($YPos2-$YPos1)/2 + $YPos1; - - $this->Shadow = $RestoreShadow; - $this->drawText($YPos,$XPos,$AreaName,array("R"=>$NameR,"G"=>$NameG,"B"=>$NameB,"Alpha"=>$NameAlpha,"Angle"=>0,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE)); - if ( $DisableShadowOnArea ) { $this->Shadow = FALSE; } - } - - $this->Shadow = $RestoreShadow; - return(array("X1"=>$XPos1,"X2"=>$XPos2)); - } - } - - /* Draw an Y threshold with the computed scale */ - function drawThreshold($Value,$Format="") - { - $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0; - $R = isset($Format["R"]) ? $Format["R"] : 255; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 50; - $Weight = isset($Format["Weight"]) ? $Format["Weight"] : NULL; - $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : 6; - $Wide = isset($Format["Wide"]) ? $Format["Wide"] : FALSE; - $WideFactor = isset($Format["WideFactor"]) ? $Format["WideFactor"] : 5; - $WriteCaption = isset($Format["WriteCaption"]) ? $Format["WriteCaption"] : FALSE; - $Caption = isset($Format["Caption"]) ? $Format["Caption"] : NULL; - $CaptionAlign = isset($Format["CaptionAlign"]) ? $Format["CaptionAlign"] : CAPTION_LEFT_TOP; - $CaptionOffset = isset($Format["CaptionOffset"]) ? $Format["CaptionOffset"] : 10; - $CaptionR = isset($Format["CaptionR"]) ? $Format["CaptionR"] : 255; - $CaptionG = isset($Format["CaptionG"]) ? $Format["CaptionG"] : 255; - $CaptionB = isset($Format["CaptionB"]) ? $Format["CaptionB"] : 255; - $CaptionAlpha = isset($Format["CaptionAlpha"]) ? $Format["CaptionAlpha"] : 100; - $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : TRUE; - $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : FALSE; - $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 5; - $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : TRUE; - $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 3; - $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0; - $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0; - $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0; - $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 20; - $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : ""; - $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 255; - $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 255; - $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 255; - $BoxBorderAlpha = isset($Format["BoxBorderAlpha"]) ? $Format["BoxBorderAlpha"] : 100; - $NoMargin = isset($Format["NoMargin"]) ? $Format["NoMargin"] : FALSE; - - if ( is_array($Value) ) { foreach ($Value as $Key => $ID) { $this->drawThreshold($ID,$Format); } return(0); } - - $CaptionSettings = array("DrawBox"=>$DrawBox,"DrawBoxBorder"=>$DrawBoxBorder,"BorderOffset"=>$BorderOffset,"BoxRounded"=>$BoxRounded,"RoundedRadius"=>$RoundedRadius, - "BoxR"=>$BoxR,"BoxG"=>$BoxG,"BoxB"=>$BoxB,"BoxAlpha"=>$BoxAlpha,"BoxSurrounding"=>$BoxSurrounding, - "BoxBorderR"=>$BoxBorderR,"BoxBorderG"=>$BoxBorderG,"BoxBorderB"=>$BoxBorderB,"BoxBorderAlpha"=>$BoxBorderAlpha, - "R"=>$CaptionR,"G"=>$CaptionG,"B"=>$CaptionB,"Alpha"=>$CaptionAlpha); - - $Data = $this->DataSet->getData(); - $AbscissaMargin = $this->getAbscissaMargin($Data); - - if ( $NoMargin ) { $AbscissaMargin = 0; } - if ( !isset($Data["Axis"][$AxisID]) ) { return(-1); } - if ( $Caption == NULL ) { $Caption = $Value; } - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - $YPos = $this->scaleComputeY($Value,array("AxisID"=>$AxisID)); - if ( $YPos >= $this->GraphAreaY1+$Data["Axis"][$AxisID]["Margin"] && $YPos <= $this->GraphAreaY2-$Data["Axis"][$AxisID]["Margin"] ) - { - $X1 = $this->GraphAreaX1 + $AbscissaMargin; - $X2 = $this->GraphAreaX2 - $AbscissaMargin; - - $this->drawLine($X1,$YPos,$X2,$YPos,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); - - if ( $Wide ) - { - $this->drawLine($X1,$YPos-1,$X2,$YPos-1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); - $this->drawLine($X1,$YPos+1,$X2,$YPos+1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); - } - - if ( $WriteCaption ) - { - if ( $CaptionAlign == CAPTION_LEFT_TOP ) - { $X = $X1 + $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLELEFT; } - else - { $X = $X2 - $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLERIGHT; } - - $this->drawText($X,$YPos,$Caption,$CaptionSettings); - } - } - - return(array("Y"=>$YPos)); - } - - if ( $Data["Orientation"] == SCALE_POS_TOPBOTTOM ) - { - $XPos = $this->scaleComputeY($Value,array("AxisID"=>$AxisID)); - if ( $XPos >= $this->GraphAreaX1+$Data["Axis"][$AxisID]["Margin"] && $XPos <= $this->GraphAreaX2-$Data["Axis"][$AxisID]["Margin"] ) - { - $Y1 = $this->GraphAreaY1 + $AbscissaMargin; - $Y2 = $this->GraphAreaY2 - $AbscissaMargin; - - $this->drawLine($XPos,$Y1,$XPos,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); - - if ( $Wide ) - { - $this->drawLine($XPos-1,$Y1,$XPos-1,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); - $this->drawLine($XPos+1,$Y1,$XPos+1,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); - } - - if ( $WriteCaption ) - { - if ( $CaptionAlign == CAPTION_LEFT_TOP ) - { $Y = $Y1 + $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE; } - else - { $Y = $Y2 - $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_BOTTOMMIDDLE; } - - $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE; - $this->drawText($XPos,$Y,$Caption,$CaptionSettings); - } - } - - return(array("Y"=>$XPos)); - } - } - - /* Draw a threshold with the computed scale */ - function drawThresholdArea($Value1,$Value2,$Format="") - { - $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0; - $R = isset($Format["R"]) ? $Format["R"] : 255; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 20; - $Border = isset($Format["Border"]) ? $Format["Border"] : TRUE; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B; - $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha + 20; - $BorderTicks = isset($Format["BorderTicks"]) ? $Format["BorderTicks"] : 2; - $AreaName = isset($Format["AreaName"]) ? $Format["AreaName"] : NULL; - $NameAngle = isset($Format["NameAngle"]) ? $Format["NameAngle"] : ZONE_NAME_ANGLE_AUTO; - $NameR = isset($Format["NameR"]) ? $Format["NameR"] : 255; - $NameG = isset($Format["NameG"]) ? $Format["NameG"] : 255; - $NameB = isset($Format["NameB"]) ? $Format["NameB"] : 255; - $NameAlpha = isset($Format["NameAlpha"]) ? $Format["NameAlpha"] : 100; - $DisableShadowOnArea = isset($Format["DisableShadowOnArea"]) ? $Format["DisableShadowOnArea"] : TRUE; - $NoMargin = isset($Format["NoMargin"]) ? $Format["NoMargin"] : FALSE; - - if ($Value1 > $Value2) { list($Value1, $Value2) = array($Value2, $Value1); } - - $RestoreShadow = $this->Shadow; - if ( $DisableShadowOnArea && $this->Shadow ) { $this->Shadow = FALSE; } - - if ($BorderAlpha >100) { $BorderAlpha = 100;} - - $Data = $this->DataSet->getData(); - $AbscissaMargin = $this->getAbscissaMargin($Data); - - if ( $NoMargin ) { $AbscissaMargin = 0; } - if ( !isset($Data["Axis"][$AxisID]) ) { return(-1); } - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - $XPos1 = $this->GraphAreaX1 + $AbscissaMargin; - $XPos2 = $this->GraphAreaX2 - $AbscissaMargin; - $YPos1 = $this->scaleComputeY($Value1,array("AxisID"=>$AxisID)); - $YPos2 = $this->scaleComputeY($Value2,array("AxisID"=>$AxisID)); - - if ( $YPos1 < $this->GraphAreaY1+$Data["Axis"][$AxisID]["Margin"] ) { $YPos1 = $this->GraphAreaY1+$Data["Axis"][$AxisID]["Margin"]; } - if ( $YPos1 > $this->GraphAreaY2-$Data["Axis"][$AxisID]["Margin"] ) { $YPos1 = $this->GraphAreaY2-$Data["Axis"][$AxisID]["Margin"]; } - if ( $YPos2 < $this->GraphAreaY1+$Data["Axis"][$AxisID]["Margin"] ) { $YPos2 = $this->GraphAreaY1+$Data["Axis"][$AxisID]["Margin"]; } - if ( $YPos2 > $this->GraphAreaY2-$Data["Axis"][$AxisID]["Margin"] ) { $YPos2 = $this->GraphAreaY2-$Data["Axis"][$AxisID]["Margin"]; } - - $this->drawFilledRectangle($XPos1,$YPos1,$XPos2,$YPos2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); - if ( $Border ) - { - $this->drawLine($XPos1,$YPos1,$XPos2,$YPos1,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); - $this->drawLine($XPos1,$YPos2,$XPos2,$YPos2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); - } - - if ( $AreaName != NULL ) - { - $XPos = ($XPos2-$XPos1)/2 + $XPos1; - $YPos = ($YPos2-$YPos1)/2 + $YPos1; - $this->Shadow = $RestoreShadow; - $this->drawText($XPos,$YPos,$AreaName,array("R"=>$NameR,"G"=>$NameG,"B"=>$NameB,"Alpha"=>$NameAlpha,"Angle"=>0,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE)); - if ( $DisableShadowOnArea ) { $this->Shadow = FALSE; } - } - - $this->Shadow = $RestoreShadow; - return(array("Y1"=>$YPos1,"Y2"=>$YPos2)); - } - elseif ( $Data["Orientation"] == SCALE_POS_TOPBOTTOM ) - { - $YPos1 = $this->GraphAreaY1 + $AbscissaMargin; - $YPos2 = $this->GraphAreaY2 - $AbscissaMargin; - $XPos1 = $this->scaleComputeY($Value1,array("AxisID"=>$AxisID)); - $XPos2 = $this->scaleComputeY($Value2,array("AxisID"=>$AxisID)); - - if ( $XPos1 < $this->GraphAreaX1+$Data["Axis"][$AxisID]["Margin"] ) { $XPos1 = $this->GraphAreaX1+$Data["Axis"][$AxisID]["Margin"]; } - if ( $XPos1 > $this->GraphAreaX2-$Data["Axis"][$AxisID]["Margin"] ) { $XPos1 = $this->GraphAreaX2-$Data["Axis"][$AxisID]["Margin"]; } - if ( $XPos2 < $this->GraphAreaX1+$Data["Axis"][$AxisID]["Margin"] ) { $XPos2 = $this->GraphAreaX1+$Data["Axis"][$AxisID]["Margin"]; } - if ( $XPos2 > $this->GraphAreaX2-$Data["Axis"][$AxisID]["Margin"] ) { $XPos2 = $this->GraphAreaX2-$Data["Axis"][$AxisID]["Margin"]; } - - $this->drawFilledRectangle($XPos1,$YPos1,$XPos2,$YPos2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); - if ( $Border ) - { - $this->drawLine($XPos1,$YPos1,$XPos1,$YPos2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); - $this->drawLine($XPos2,$YPos1,$XPos2,$YPos2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); - } - - if ( $AreaName != NULL ) - { - $XPos = ($YPos2-$YPos1)/2 + $YPos1; - $YPos = ($XPos2-$XPos1)/2 + $XPos1; - - if ( $NameAngle == ZONE_NAME_ANGLE_AUTO ) - { - $TxtPos = $this->getTextBox($XPos,$YPos,$this->FontName,$this->FontSize,0,$AreaName); - $TxtWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"]; - if ( abs($XPos2 - $XPos1) > $TxtWidth ) { $NameAngle = 0; } else { $NameAngle = 90; } - } - $this->Shadow = $RestoreShadow; - $this->drawText($YPos,$XPos,$AreaName,array("R"=>$NameR,"G"=>$NameG,"B"=>$NameB,"Alpha"=>$NameAlpha,"Angle"=>$NameAngle,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE)); - if ( $DisableShadowOnArea ) { $this->Shadow = FALSE; } - } - - $this->Shadow = $RestoreShadow; - return(array("Y1"=>$XPos1,"Y2"=>$XPos2)); - } - } - - function scaleGetXSettings() - { - $Data = $this->DataSet->getData(); - foreach($Data["Axis"] as $AxisID => $Settings) - { - if ( $Settings["Identity"] == AXIS_X ) - { - $Rows = $Settings["Rows"]; - - return(array($Settings["Margin"],$Rows)); - } - } - } - - function scaleComputeY($Values,$Option="",$ReturnOnly0Height=FALSE) - { - $AxisID = isset($Option["AxisID"]) ? $Option["AxisID"] : 0; - $SerieName = isset($Option["SerieName"]) ? $Option["SerieName"] : NULL; - - $Data = $this->DataSet->getData(); - if ( !isset($Data["Axis"][$AxisID]) ) { return(-1); } - - if ( $SerieName != NULL ) { $AxisID = $Data["Series"][$SerieName]["Axis"]; } - if ( !is_array($Values) ) { $tmp = $Values; $Values = ""; $Values[0] = $tmp; } - - $Result = ""; - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Data["Axis"][$AxisID]["Margin"]*2; - $ScaleHeight = $Data["Axis"][$AxisID]["ScaleMax"] - $Data["Axis"][$AxisID]["ScaleMin"]; - $Step = $Height / $ScaleHeight; - - if ( $ReturnOnly0Height ) - { foreach($Values as $Key => $Value) { if ( $Value == VOID ) { $Result[] = VOID; } else { $Result[] = $Step * $Value; } } } - else - { foreach($Values as $Key => $Value) { if ( $Value == VOID ) { $Result[] = VOID; } else { $Result[] = $this->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"] - ($Step * ($Value-$Data["Axis"][$AxisID]["ScaleMin"])); } } } - } - else - { - $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Data["Axis"][$AxisID]["Margin"]*2; - $ScaleWidth = $Data["Axis"][$AxisID]["ScaleMax"] - $Data["Axis"][$AxisID]["ScaleMin"]; - $Step = $Width / $ScaleWidth; - - if ( $ReturnOnly0Height ) - { foreach($Values as $Key => $Value) { if ( $Value == VOID ) { $Result[] = VOID; } else { $Result[] = $Step * $Value; } } } - else - { foreach($Values as $Key => $Value) { if ( $Value == VOID ) { $Result[] = VOID; } else { $Result[] = $this->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"] + ($Step * ($Value-$Data["Axis"][$AxisID]["ScaleMin"])); } } } - } - - if ( count($Result) == 1 ) - return($Result[0]); - else - return($Result); - } - - /* Format the axis values */ - function scaleFormat($Value,$Mode=NULL,$Format=NULL,$Unit=NULL) - { - if ( $Value == VOID ) { return(""); } - - if ( $Mode == AXIS_FORMAT_CUSTOM ) - { if ( function_exists($Format) ) { return(call_user_func($Format,$Value)); } } - - if ( $Mode == AXIS_FORMAT_DATE ) - { if ( $Format == NULL ) { $Pattern = "d/m/Y"; } else { $Pattern = $Format; } return(date($Pattern,$Value)); } - - if ( $Mode == AXIS_FORMAT_TIME ) - { if ( $Format == NULL ) { $Pattern = "H:i:s"; } else { $Pattern = $Format; } return(date($Pattern,$Value)); } - - if ( $Mode == AXIS_FORMAT_CURRENCY ) - { return($Format.number_format($Value,2)); } - - if ( $Mode == AXIS_FORMAT_METRIC ) - { - if (abs($Value) > 1000000000) - return(round($Value/1000000000,$Format)."g".$Unit); - if (abs($Value) > 1000000) - return(round($Value/1000000,$Format)."m".$Unit); - elseif (abs($Value) >= 1000) - return(round($Value/1000,$Format)."k".$Unit); - - } - return($Value.$Unit); - } - - /* Write Max value on a chart */ - function writeBounds($Type=BOUND_BOTH,$Format=NULL) - { - $MaxLabelTxt = isset($Format["MaxLabelTxt"]) ? $Format["MaxLabelTxt"] : "max="; - $MinLabelTxt = isset($Format["MinLabelTxt"]) ? $Format["MinLabelTxt"] : "min="; - $Decimals = isset($Format["Decimals"]) ? $Format["Decimals"] : 1; - $ExcludedSeries = isset($Format["ExcludedSeries"]) ? $Format["ExcludedSeries"] : ""; - $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 4; - $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; - $MaxDisplayR = isset($Format["MaxDisplayR"]) ? $Format["MaxDisplayR"] : 0; - $MaxDisplayG = isset($Format["MaxDisplayG"]) ? $Format["MaxDisplayG"] : 0; - $MaxDisplayB = isset($Format["MaxDisplayB"]) ? $Format["MaxDisplayB"] : 0; - $MinDisplayR = isset($Format["MinDisplayR"]) ? $Format["MinDisplayR"] : 255; - $MinDisplayG = isset($Format["MinDisplayG"]) ? $Format["MinDisplayG"] : 255; - $MinDisplayB = isset($Format["MinDisplayB"]) ? $Format["MinDisplayB"] : 255; - $MinLabelPos = isset($Format["MinLabelPos"]) ? $Format["MinLabelPos"] : BOUND_LABEL_POS_AUTO; - $MaxLabelPos = isset($Format["MaxLabelPos"]) ? $Format["MaxLabelPos"] : BOUND_LABEL_POS_AUTO; - $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : TRUE; - $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : FALSE; - $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 5; - $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : TRUE; - $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 3; - $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0; - $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0; - $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0; - $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 20; - $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : ""; - $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 255; - $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 255; - $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 255; - $BoxBorderAlpha = isset($Format["BoxBorderAlpha"]) ? $Format["BoxBorderAlpha"] : 100; - - $CaptionSettings = array("DrawBox"=>$DrawBox,"DrawBoxBorder"=>$DrawBoxBorder,"BorderOffset"=>$BorderOffset,"BoxRounded"=>$BoxRounded,"RoundedRadius"=>$RoundedRadius, - "BoxR"=>$BoxR,"BoxG"=>$BoxG,"BoxB"=>$BoxB,"BoxAlpha"=>$BoxAlpha,"BoxSurrounding"=>$BoxSurrounding, - "BoxBorderR"=>$BoxBorderR,"BoxBorderG"=>$BoxBorderG,"BoxBorderB"=>$BoxBorderB,"BoxBorderAlpha"=>$BoxBorderAlpha); - - list($XMargin,$XDivs) = $this->scaleGetXSettings(); - - $Data = $this->DataSet->getData(); - foreach($Data["Series"] as $SerieName => $Serie) - { - if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] && !isset($ExcludedSeries[$SerieName])) - { - $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; - if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; } - - $MinValue = $this->DataSet->getMin($SerieName); - $MaxValue = $this->DataSet->getMax($SerieName); - - $MinPos = VOID; $MaxPos = VOID; - foreach($Serie["Data"] as $Key => $Value) - { - if ( $Value == $MinValue && $MinPos == VOID ) { $MinPos = $Key; } - if ( $Value == $MaxValue ) { $MaxPos = $Key; } - } - - $AxisID = $Serie["Axis"]; - $Mode = $Data["Axis"][$AxisID]["Display"]; - $Format = $Data["Axis"][$AxisID]["Format"]; - $Unit = $Data["Axis"][$AxisID]["Unit"]; - - $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; - $X = $this->GraphAreaX1 + $XMargin; - $SerieOffset = isset($Serie["XOffset"]) ? $Serie["XOffset"] : 0; - - if ( $Type == BOUND_MAX || $Type == BOUND_BOTH ) - { - if ( $MaxLabelPos == BOUND_LABEL_POS_TOP || ( $MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue >= 0) ) { $YPos = $PosArray[$MaxPos] - $DisplayOffset + 2; $Align = TEXT_ALIGN_BOTTOMMIDDLE; } - if ( $MaxLabelPos == BOUND_LABEL_POS_BOTTOM || ( $MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue < 0) ) { $YPos = $PosArray[$MaxPos] + $DisplayOffset + 2; $Align = TEXT_ALIGN_TOPMIDDLE; } - - $XPos = $X + $MaxPos*$XStep + $SerieOffset; - $Label = $MaxLabelTxt.$this->scaleFormat(round($MaxValue,$Decimals),$Mode,$Format,$Unit); - - $TxtPos = $this->getTextBox($XPos,$YPos,$this->FontName,$this->FontSize,0,$Label); - $XOffset = 0; $YOffset = 0; - if ( $TxtPos[0]["X"] < $this->GraphAreaX1 ) { $XOffset = (($this->GraphAreaX1 - $TxtPos[0]["X"])/2); } - if ( $TxtPos[1]["X"] > $this->GraphAreaX2 ) { $XOffset = -(($TxtPos[1]["X"] - $this->GraphAreaX2)/2); } - if ( $TxtPos[2]["Y"] < $this->GraphAreaY1 ) { $YOffset = $this->GraphAreaY1 - $TxtPos[2]["Y"]; } - if ( $TxtPos[0]["Y"] > $this->GraphAreaY2 ) { $YOffset = -($TxtPos[0]["Y"] - $this->GraphAreaY2); } - - $CaptionSettings["R"] = $MaxDisplayR; $CaptionSettings["G"] = $MaxDisplayG; - $CaptionSettings["B"] = $MaxDisplayB; $CaptionSettings["Align"] = $Align; - - $this->drawText($XPos+$XOffset,$YPos+$YOffset,$Label,$CaptionSettings); - } - - if ( $Type == BOUND_MIN || $Type == BOUND_BOTH ) - { - if ( $MinLabelPos == BOUND_LABEL_POS_TOP || ( $MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue >= 0) ) { $YPos = $PosArray[$MinPos] - $DisplayOffset + 2; $Align = TEXT_ALIGN_BOTTOMMIDDLE; } - if ( $MinLabelPos == BOUND_LABEL_POS_BOTTOM || ( $MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue < 0) ) { $YPos = $PosArray[$MinPos] + $DisplayOffset + 2; $Align = TEXT_ALIGN_TOPMIDDLE; } - - $XPos = $X + $MinPos*$XStep + $SerieOffset; - $Label = $MinLabelTxt.$this->scaleFormat(round($MinValue,$Decimals),$Mode,$Format,$Unit); - - $TxtPos = $this->getTextBox($XPos,$YPos,$this->FontName,$this->FontSize,0,$Label); - $XOffset = 0; $YOffset = 0; - if ( $TxtPos[0]["X"] < $this->GraphAreaX1 ) { $XOffset = (($this->GraphAreaX1 - $TxtPos[0]["X"])/2); } - if ( $TxtPos[1]["X"] > $this->GraphAreaX2 ) { $XOffset = -(($TxtPos[1]["X"] - $this->GraphAreaX2)/2); } - if ( $TxtPos[2]["Y"] < $this->GraphAreaY1 ) { $YOffset = $this->GraphAreaY1 - $TxtPos[2]["Y"]; } - if ( $TxtPos[0]["Y"] > $this->GraphAreaY2 ) { $YOffset = -($TxtPos[0]["Y"] - $this->GraphAreaY2); } - - $CaptionSettings["R"] = $MinDisplayR; $CaptionSettings["G"] = $MinDisplayG; - $CaptionSettings["B"] = $MinDisplayB; $CaptionSettings["Align"] = $Align; - - $this->drawText($XPos+$XOffset,$YPos-$DisplayOffset+$YOffset,$Label,$CaptionSettings); - } - } - else - { - $XStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; - $X = $this->GraphAreaY1 + $XMargin; - $SerieOffset = isset($Serie["XOffset"]) ? $Serie["XOffset"] : 0; - - if ( $Type == BOUND_MAX || $Type == BOUND_BOTH ) - { - if ( $MaxLabelPos == BOUND_LABEL_POS_TOP || ( $MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue >= 0) ) { $YPos = $PosArray[$MaxPos] + $DisplayOffset + 2; $Align = TEXT_ALIGN_MIDDLELEFT; } - if ( $MaxLabelPos == BOUND_LABEL_POS_BOTTOM || ( $MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue < 0) ) { $YPos = $PosArray[$MaxPos] - $DisplayOffset + 2; $Align = TEXT_ALIGN_MIDDLERIGHT; } - - $XPos = $X + $MaxPos*$XStep + $SerieOffset; - $Label = $MaxLabelTxt.$this->scaleFormat($MaxValue,$Mode,$Format,$Unit); - - $TxtPos = $this->getTextBox($YPos,$XPos,$this->FontName,$this->FontSize,0,$Label); - $XOffset = 0; $YOffset = 0; - if ( $TxtPos[0]["X"] < $this->GraphAreaX1 ) { $XOffset = $this->GraphAreaX1 - $TxtPos[0]["X"]; } - if ( $TxtPos[1]["X"] > $this->GraphAreaX2 ) { $XOffset = -($TxtPos[1]["X"] - $this->GraphAreaX2); } - if ( $TxtPos[2]["Y"] < $this->GraphAreaY1 ) { $YOffset = ($this->GraphAreaY1 - $TxtPos[2]["Y"])/2; } - if ( $TxtPos[0]["Y"] > $this->GraphAreaY2 ) { $YOffset = -(($TxtPos[0]["Y"] - $this->GraphAreaY2)/2);} - - $CaptionSettings["R"] = $MaxDisplayR; $CaptionSettings["G"] = $MaxDisplayG; - $CaptionSettings["B"] = $MaxDisplayB; $CaptionSettings["Align"] = $Align; - - $this->drawText($YPos+$XOffset,$XPos+$YOffset,$Label,$CaptionSettings); - } - - if ( $Type == BOUND_MIN || $Type == BOUND_BOTH ) - { - if ( $MinLabelPos == BOUND_LABEL_POS_TOP || ( $MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue >= 0) ) { $YPos = $PosArray[$MinPos] + $DisplayOffset + 2; $Align = TEXT_ALIGN_MIDDLELEFT; } - if ( $MinLabelPos == BOUND_LABEL_POS_BOTTOM || ( $MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue < 0) ) { $YPos = $PosArray[$MinPos] - $DisplayOffset + 2; $Align = TEXT_ALIGN_MIDDLERIGHT; } - - $XPos = $X + $MinPos*$XStep + $SerieOffset; - $Label = $MinLabelTxt.$this->scaleFormat($MinValue,$Mode,$Format,$Unit); - - $TxtPos = $this->getTextBox($YPos,$XPos,$this->FontName,$this->FontSize,0,$Label); - $XOffset = 0; $YOffset = 0; - if ( $TxtPos[0]["X"] < $this->GraphAreaX1 ) { $XOffset = $this->GraphAreaX1 - $TxtPos[0]["X"]; } - if ( $TxtPos[1]["X"] > $this->GraphAreaX2 ) { $XOffset = -($TxtPos[1]["X"] - $this->GraphAreaX2); } - if ( $TxtPos[2]["Y"] < $this->GraphAreaY1 ) { $YOffset = ($this->GraphAreaY1 - $TxtPos[2]["Y"])/2; } - if ( $TxtPos[0]["Y"] > $this->GraphAreaY2 ) { $YOffset = -(($TxtPos[0]["Y"] - $this->GraphAreaY2)/2);} - - $CaptionSettings["R"] = $MinDisplayR; $CaptionSettings["G"] = $MinDisplayG; - $CaptionSettings["B"] = $MinDisplayB; $CaptionSettings["Align"] = $Align; - - $this->drawText($YPos+$XOffset,$XPos+$YOffset,$Label,$CaptionSettings); - } - } - } - } - } - - /* Draw a plot chart */ - function drawPlotChart($Format=NULL) - { - $PlotSize = isset($Format["PlotSize"]) ? $Format["PlotSize"] : NULL; - $PlotBorder = isset($Format["PlotBorder"]) ? $Format["PlotBorder"] : FALSE; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 50; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 50; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 50; - $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 30; - $BorderSize = isset($Format["BorderSize"]) ? $Format["BorderSize"] : 2; - $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; - $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : FALSE; - $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 4; - $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; - $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; - $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; - $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; - $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; - - $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; - - $Data = $this->DataSet->getData(); - list($XMargin,$XDivs) = $this->scaleGetXSettings(); - - foreach($Data["Series"] as $SerieName => $Serie) - { - if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) - { - if ( isset($Serie["Weight"]) ) { $SerieWeight = $Serie["Weight"] + 2; } else { $SerieWeight = 2; } - if ( $PlotSize != NULL ) { $SerieWeight = $PlotSize; } - - $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; - if ( $Surrounding != NULL ) { $BorderR = $R + $Surrounding; $BorderG = $G + $Surrounding; $BorderB = $B + $Surrounding; } - if ( isset($Serie["Picture"]) ) - { $Picture = $Serie["Picture"]; list($PicWidth,$PicHeight,$PicType) = $this->getPicInfo($Picture); } - else { $Picture = NULL; $PicOffset = 0; } - - if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; } - - $AxisID = $Serie["Axis"]; - $Shape = $Serie["Shape"]; - $Mode = $Data["Axis"][$AxisID]["Display"]; - $Format = $Data["Axis"][$AxisID]["Format"]; - $Unit = $Data["Axis"][$AxisID]["Unit"]; - - if (isset($Serie["Description"])) { $SerieDescription = $Serie["Description"]; } else { $SerieDescription = $SerieName; } - - $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); - - $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } - if ( $Picture != NULL ) { $PicOffset = $PicHeight / 2; $SerieWeight = 0; } - $X = $this->GraphAreaX1 + $XMargin; - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - foreach($PosArray as $Key => $Y) - { - if ( $DisplayValues ) - $this->drawText($X,$Y-$DisplayOffset-$SerieWeight-$BorderSize-$PicOffset,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); - - if ( $Y != VOID ) - { - if ( $RecordImageMap ) { $this->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$SerieWeight,$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - - if ( $Picture != NULL ) - { $this->drawFromPicture($PicType,$Picture,$X-$PicWidth/2,$Y-$PicHeight/2); } - else - { $this->drawShape($X,$Y,$Shape,$SerieWeight,$PlotBorder,$BorderSize,$R,$G,$B,$Alpha,$BorderR,$BorderG,$BorderB,$BorderAlpha); } - } - $X = $X + $XStep; - } - } - else - { - if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } - if ( $Picture != NULL ) { $PicOffset = $PicWidth / 2; $SerieWeight = 0; } - $Y = $this->GraphAreaY1 + $XMargin; - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - foreach($PosArray as $Key => $X) - { - if ( $DisplayValues ) - $this->drawText($X+$DisplayOffset+$SerieWeight+$BorderSize+$PicOffset,$Y,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("Angle"=>270,"R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); - - if ( $X != VOID ) - { - if ( $RecordImageMap ) { $this->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$SerieWeight,$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - - if ( $Picture != NULL ) - { $this->drawFromPicture($PicType,$Picture,$X-$PicWidth/2,$Y-$PicHeight/2); } - else - { $this->drawShape($X,$Y,$Shape,$SerieWeight,$PlotBorder,$BorderSize,$R,$G,$B,$Alpha,$BorderR,$BorderG,$BorderB,$BorderAlpha); } - } - $Y = $Y + $YStep; - } - } - } - } - } - - /* Draw a spline chart */ - function drawSplineChart($Format=NULL) - { - $BreakVoid = isset($Format["BreakVoid"]) ? $Format["BreakVoid"] : TRUE; - $VoidTicks = isset($Format["VoidTicks"]) ? $Format["VoidTicks"] : 4; - $BreakR = isset($Format["BreakR"]) ? $Format["BreakR"] : NULL; // 234 - $BreakG = isset($Format["BreakG"]) ? $Format["BreakG"] : NULL; // 55 - $BreakB = isset($Format["BreakB"]) ? $Format["BreakB"] : NULL; // 26 - $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : FALSE; - $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2; - $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; - $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; - $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; - $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; - $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; - $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 5; - - $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; - - $Data = $this->DataSet->getData(); - list($XMargin,$XDivs) = $this->scaleGetXSettings(); - foreach($Data["Series"] as $SerieName => $Serie) - { - if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) - { - $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; $Weight = $Serie["Weight"]; - - if ( $BreakR == NULL ) - $BreakSettings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$VoidTicks); - else - $BreakSettings = array("R"=>$BreakR,"G"=>$BreakG,"B"=>$BreakB,"Alpha"=>$Alpha,"Ticks"=>$VoidTicks,"Weight"=>$Weight); - - if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; } - - $AxisID = $Serie["Axis"]; - $Mode = $Data["Axis"][$AxisID]["Display"]; - $Format = $Data["Axis"][$AxisID]["Format"]; - $Unit = $Data["Axis"][$AxisID]["Unit"]; - - if (isset($Serie["Description"])) { $SerieDescription = $Serie["Description"]; } else { $SerieDescription = $SerieName; } - - $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); - - $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } - $X = $this->GraphAreaX1 + $XMargin; $WayPoints = ""; - $Force = $XStep / 5; - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - $LastGoodY = NULL; $LastGoodX = NULL; $LastX = 1; $LastY = 1; - foreach($PosArray as $Key => $Y) - { - if ( $DisplayValues ) - $this->drawText($X,$Y-$DisplayOffset,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); - - if ( $RecordImageMap && $Y != VOID ) { $this->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$ImageMapPlotSize,$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - - if ( $Y == VOID && $LastY != NULL ) - { $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); $WayPoints = ""; } - - if ( $Y != VOID && $LastY == NULL && $LastGoodY != NULL && !$BreakVoid ) - { $this->drawLine($LastGoodX,$LastGoodY,$X,$Y,$BreakSettings); } - - if ( $Y != VOID ) - $WayPoints[] = array($X,$Y); - - if ( $Y != VOID ) { $LastGoodY = $Y; $LastGoodX = $X; } - if ( $Y == VOID ) { $Y = NULL; } - - $LastX = $X; $LastY = $Y; - $X = $X + $XStep; - } - $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); - } - else - { - if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } - $Y = $this->GraphAreaY1 + $XMargin; $WayPoints = ""; - $Force = $YStep / 5; - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - $LastGoodY = NULL; $LastGoodX = NULL; $LastX = 1; $LastY = 1; - foreach($PosArray as $Key => $X) - { - if ( $DisplayValues ) - $this->drawText($X+$DisplayOffset,$Y,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("Angle"=>270,"R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); - - if ( $RecordImageMap && $X != VOID ) { $this->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$ImageMapPlotSize,$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - - if ( $X == VOID && $LastX != NULL ) - { $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); $WayPoints = ""; } - - if ( $X != VOID && $LastX == NULL && $LastGoodX != NULL && !$BreakVoid ) - { $this->drawLine($LastGoodX,$LastGoodY,$X,$Y,$BreakSettings); } - - if ( $X != VOID ) - $WayPoints[] = array($X,$Y); - - if ( $X != VOID ) { $LastGoodX = $X; $LastGoodY = $Y; } - if ( $X == VOID ) { $X = NULL; } - - $LastX = $X; $LastY = $Y; - $Y = $Y + $YStep; - } - $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); - } - } - } - } - - /* Draw a filled spline chart */ - function drawFilledSplineChart($Format=NULL) - { - $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : FALSE; - $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2; - $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; - $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; - $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; - $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; - $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : TRUE; - $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : NULL; - - $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; - - $Data = $this->DataSet->getData(); - list($XMargin,$XDivs) = $this->scaleGetXSettings(); - foreach($Data["Series"] as $SerieName => $Serie) - { - if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) - { - $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; - if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; } - - $AxisID = $Serie["Axis"]; - $Mode = $Data["Axis"][$AxisID]["Display"]; - $Format = $Data["Axis"][$AxisID]["Format"]; - $Unit = $Data["Axis"][$AxisID]["Unit"]; - - $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); - if ( $AroundZero ) { $YZero = $this->scaleComputeY(0,array("AxisID"=>$Serie["Axis"])); } - - if ( $Threshold != NULL ) - { - foreach($Threshold as $Key => $Params) - { - $Threshold[$Key]["MinX"] = $this->scaleComputeY($Params["Min"],array("AxisID"=>$Serie["Axis"])); - $Threshold[$Key]["MaxX"] = $this->scaleComputeY($Params["Max"],array("AxisID"=>$Serie["Axis"])); - } - } - - $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } - $X = $this->GraphAreaX1 + $XMargin; $WayPoints = ""; - $Force = $XStep / 5; - - if ( !$AroundZero ) { $YZero = $this->GraphAreaY2-1; } - if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; } - if ( $YZero < $this->GraphAreaY1+1 ) { $YZero = $this->GraphAreaY1+1; } - - $LastX = ""; $LastY = ""; - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - foreach($PosArray as $Key => $Y) - { - if ( $DisplayValues ) - $this->drawText($X,$Y-$DisplayOffset,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); - - if ( $Y == VOID ) - { - $Area = $this->drawSpline($WayPoints,array("Force"=>$Force,"PathOnly"=>TRUE)); - - if ( $Area != "" ) - { - foreach ($Area as $key => $Points) - { - $Corners = ""; $Corners[] = $Area[$key][0]["X"]; $Corners[] = $YZero; - foreach($Points as $subKey => $Point) - { - if ( $subKey == count($Points)-1) { $Corners[] = $Point["X"]-1; } else { $Corners[] = $Point["X"]; } - $Corners[] = $Point["Y"]+1; - } - $Corners[] = $Points[$subKey]["X"]-1; $Corners[] = $YZero; - - $this->drawPolygonChart($Corners,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/2,"NoBorder"=>TRUE,"Threshold"=>$Threshold)); - } - $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); - } - - $WayPoints = ""; - } - else - $WayPoints[] = array($X,$Y-.5); /* -.5 for AA visual fix */ - - $X = $X + $XStep; - } - $Area = $this->drawSpline($WayPoints,array("Force"=>$Force,"PathOnly"=>TRUE)); - - if ( $Area != "" ) - { - foreach ($Area as $key => $Points) - { - $Corners = ""; $Corners[] = $Area[$key][0]["X"]; $Corners[] = $YZero; - foreach($Points as $subKey => $Point) - { - if ( $subKey == count($Points)-1) { $Corners[] = $Point["X"]-1; } else { $Corners[] = $Point["X"]; } - $Corners[] = $Point["Y"]+1; - } - $Corners[] = $Points[$subKey]["X"]-1; $Corners[] = $YZero; - - $this->drawPolygonChart($Corners,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/2,"NoBorder"=>TRUE,"Threshold"=>$Threshold)); - } - $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); - } - } - else - { - if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } - $Y = $this->GraphAreaY1 + $XMargin; $WayPoints = ""; - $Force = $YStep / 5; - - if ( !$AroundZero ) { $YZero = $this->GraphAreaX1+1; } - if ( $YZero > $this->GraphAreaX2-1 ) { $YZero = $this->GraphAreaX2-1; } - if ( $YZero < $this->GraphAreaX1+1 ) { $YZero = $this->GraphAreaX1+1; } - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - foreach($PosArray as $Key => $X) - { - if ( $DisplayValues ) - $this->drawText($X+$DisplayOffset,$Y,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("Angle"=>270,"R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); - - if ( $X == VOID ) - { - $Area = $this->drawSpline($WayPoints,array("Force"=>$Force,"PathOnly"=>TRUE)); - - if ( $Area != "" ) - { - foreach ($Area as $key => $Points) - { - $Corners = ""; $Corners[] = $YZero; $Corners[] = $Area[$key][0]["Y"]; - foreach($Points as $subKey => $Point) - { - if ( $subKey == count($Points)-1) { $Corners[] = $Point["X"]-1; } else { $Corners[] = $Point["X"]; } - $Corners[] = $Point["Y"]; - } - $Corners[] = $YZero; $Corners[] = $Points[$subKey]["Y"]-1; - - $this->drawPolygonChart($Corners,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/2,"NoBorder"=>TRUE,"Threshold"=>$Threshold)); - } - $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); - } - - $WayPoints = ""; - } - else - $WayPoints[] = array($X,$Y); - - $Y = $Y + $YStep; - } - $Area = $this->drawSpline($WayPoints,array("Force"=>$Force,"PathOnly"=>TRUE)); - - if ( $Area != "" ) - { - foreach ($Area as $key => $Points) - { - $Corners = ""; $Corners[] = $YZero; $Corners[] = $Area[$key][0]["Y"]; - foreach($Points as $subKey => $Point) - { - if ( $subKey == count($Points)-1) { $Corners[] = $Point["X"]-1; } else { $Corners[] = $Point["X"]; } - $Corners[] = $Point["Y"]; - } - $Corners[] = $YZero; $Corners[] = $Points[$subKey]["Y"]-1; - - $this->drawPolygonChart($Corners,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/2,"NoBorder"=>TRUE,"Threshold"=>$Threshold)); - } - $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); - } - - } - } - } - } - - /* Draw a line chart */ - function drawLineChart($Format=NULL) - { - $BreakVoid = isset($Format["BreakVoid"]) ? $Format["BreakVoid"] : TRUE; - $VoidTicks = isset($Format["VoidTicks"]) ? $Format["VoidTicks"] : 4; - $BreakR = isset($Format["BreakR"]) ? $Format["BreakR"] : NULL; - $BreakG = isset($Format["BreakG"]) ? $Format["BreakG"] : NULL; - $BreakB = isset($Format["BreakB"]) ? $Format["BreakB"] : NULL; - $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : FALSE; - $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2; - $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; - $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; - $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; - $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; - $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; - $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 5; - $ForceColor = isset($Format["ForceColor"]) ? $Format["ForceColor"] : FALSE; - $ForceR = isset($Format["ForceR"]) ? $Format["ForceR"] : 0; - $ForceG = isset($Format["ForceG"]) ? $Format["ForceG"] : 0; - $ForceB = isset($Format["ForceB"]) ? $Format["ForceB"] : 0; - $ForceAlpha = isset($Format["ForceAlpha"]) ? $Format["ForceAlpha"] : 100; - - $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; - - $Data = $this->DataSet->getData(); - list($XMargin,$XDivs) = $this->scaleGetXSettings(); - foreach($Data["Series"] as $SerieName => $Serie) - { - if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) - { - $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; $Weight = $Serie["Weight"]; - - if ( $ForceColor ) - { $R = $ForceR; $G = $ForceG; $B = $ForceB; $Alpha = $ForceAlpha; } - - if ( $BreakR == NULL ) - $BreakSettings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$VoidTicks,"Weight"=>$Weight); - else - $BreakSettings = array("R"=>$BreakR,"G"=>$BreakG,"B"=>$BreakB,"Alpha"=>$Alpha,"Ticks"=>$VoidTicks,"Weight"=>$Weight); - - if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; } - - $AxisID = $Serie["Axis"]; - $Mode = $Data["Axis"][$AxisID]["Display"]; - $Format = $Data["Axis"][$AxisID]["Format"]; - $Unit = $Data["Axis"][$AxisID]["Unit"]; - - if (isset($Serie["Description"])) { $SerieDescription = $Serie["Description"]; } else { $SerieDescription = $SerieName; } - - $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); - - $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } - $X = $this->GraphAreaX1 + $XMargin; $LastX = NULL; $LastY = NULL; - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - $LastGoodY = NULL; $LastGoodX = NULL; - foreach($PosArray as $Key => $Y) - { - if ( $DisplayValues && $Serie["Data"][$Key] != VOID ) - { - if ( $Serie["Data"][$Key] > 0 ) { $Align = TEXT_ALIGN_BOTTOMMIDDLE; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_TOPMIDDLE; $Offset = -$DisplayOffset; } - $this->drawText($X,$Y-$Offset-$Weight,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>$Align)); - } - - if ( $RecordImageMap && $Y != VOID ) { $this->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$ImageMapPlotSize,$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - - if ( $Y != VOID && $LastX != NULL && $LastY != NULL ) - $this->drawLine($LastX,$LastY,$X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); - - if ( $Y != VOID && $LastY == NULL && $LastGoodY != NULL && !$BreakVoid ) - { $this->drawLine($LastGoodX,$LastGoodY,$X,$Y,$BreakSettings); $LastGoodY = NULL; } - - if ( $Y != VOID ) { $LastGoodY = $Y; $LastGoodX = $X; } - if ( $Y == VOID ) { $Y = NULL; } - - $LastX = $X; $LastY = $Y; - $X = $X + $XStep; - } - } - else - { - if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } - $Y = $this->GraphAreaY1 + $XMargin; $LastX = NULL; $LastY = NULL; - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - $LastGoodY = NULL; $LastGoodX = NULL; - foreach($PosArray as $Key => $X) - { - if ( $DisplayValues && $Serie["Data"][$Key] != VOID ) - { $this->drawText($X+$DisplayOffset+$Weight,$Y,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("Angle"=>270,"R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); } - - if ( $RecordImageMap && $X != VOID ) { $this->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$ImageMapPlotSize,$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - - if ( $X != VOID && $LastX != NULL && $LastY != NULL ) - $this->drawLine($LastX,$LastY,$X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); - - if ( $X != VOID && $LastX == NULL && $LastGoodY != NULL && !$BreakVoid ) - { $this->drawLine($LastGoodX,$LastGoodY,$X,$Y,$BreakSettings); $LastGoodY = NULL; } - - if ( $X != VOID ) { $LastGoodY = $Y; $LastGoodX = $X; } - if ( $X == VOID ) { $X = NULL; } - - $LastX = $X; $LastY = $Y; - $Y = $Y + $YStep; - } - } - } - } - } - - /* Draw a line chart */ - function drawZoneChart($SerieA,$SerieB,$Format=NULL) - { - $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0; - $LineR = isset($Format["LineR"]) ? $Format["LineR"] : 150; - $LineG = isset($Format["LineG"]) ? $Format["LineG"] : 150; - $LineB = isset($Format["LineB"]) ? $Format["LineB"] : 150; - $LineAlpha = isset($Format["LineAlpha"]) ? $Format["LineAlpha"] : 50; - $LineTicks = isset($Format["LineTicks"]) ? $Format["LineTicks"] : 1; - $AreaR = isset($Format["AreaR"]) ? $Format["AreaR"] : 150; - $AreaG = isset($Format["AreaG"]) ? $Format["AreaG"] : 150; - $AreaB = isset($Format["AreaB"]) ? $Format["AreaB"] : 150; - $AreaAlpha = isset($Format["AreaAlpha"]) ? $Format["AreaAlpha"] : 5; - - $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; - - $Data = $this->DataSet->getData(); - if ( !isset($Data["Series"][$SerieA]["Data"]) || !isset($Data["Series"][$SerieB]["Data"]) ) { return(0); } - $SerieAData = $Data["Series"][$SerieA]["Data"]; - $SerieBData = $Data["Series"][$SerieB]["Data"]; - - list($XMargin,$XDivs) = $this->scaleGetXSettings(); - - $Mode = $Data["Axis"][$AxisID]["Display"]; - $Format = $Data["Axis"][$AxisID]["Format"]; - $Unit = $Data["Axis"][$AxisID]["Unit"]; - - $PosArrayA = $this->scaleComputeY($SerieAData,array("AxisID"=>$AxisID)); - $PosArrayB = $this->scaleComputeY($SerieBData,array("AxisID"=>$AxisID)); - if ( count($PosArrayA) != count($PosArrayB) ) { return(0); } - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } - $X = $this->GraphAreaX1 + $XMargin; $LastX = NULL; $LastY = NULL; - - $LastX = NULL; $LastY1 = NULL; $LastY2 = NULL; - $BoundsA = ""; $BoundsB = ""; - foreach($PosArrayA as $Key => $Y1) - { - $Y2 = $PosArrayB[$Key]; - - $BoundsA[] = $X; $BoundsA[] = $Y1; - $BoundsB[] = $X; $BoundsB[] = $Y2; - - $LastX = $X; - $LastY1 = $Y1; $LastY2 = $Y2; - - $X = $X + $XStep; - } - $Bounds = array_merge($BoundsA,$this->reversePlots($BoundsB)); - $this->drawPolygonChart($Bounds,array("R"=>$AreaR,"G"=>$AreaG,"B"=>$AreaB,"Alpha"=>$AreaAlpha)); - - for($i=0;$i<=count($BoundsA)-4;$i=$i+2) - { - $this->drawLine($BoundsA[$i],$BoundsA[$i+1],$BoundsA[$i+2],$BoundsA[$i+3],array("R"=>$LineR,"G"=>$LineG,"B"=>$LineB,"Alpha"=>$LineAlpha,"Ticks"=>$LineTicks)); - $this->drawLine($BoundsB[$i],$BoundsB[$i+1],$BoundsB[$i+2],$BoundsB[$i+3],array("R"=>$LineR,"G"=>$LineG,"B"=>$LineB,"Alpha"=>$LineAlpha,"Ticks"=>$LineTicks)); - } - } - else - { - if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } - $Y = $this->GraphAreaY1 + $XMargin; $LastX = NULL; $LastY = NULL; - - $LastY = NULL; $LastX1 = NULL; $LastX2 = NULL; - $BoundsA = ""; $BoundsB = ""; - foreach($PosArrayA as $Key => $X1) - { - $X2 = $PosArrayB[$Key]; - - $BoundsA[] = $X1; $BoundsA[] = $Y; - $BoundsB[] = $X2; $BoundsB[] = $Y; - - $LastY = $Y; - $LastX1 = $X1; $LastX2 = $X2; - - $Y = $Y + $YStep; - } - $Bounds = array_merge($BoundsA,$this->reversePlots($BoundsB)); - $this->drawPolygonChart($Bounds,array("R"=>$AreaR,"G"=>$AreaG,"B"=>$AreaB,"Alpha"=>$AreaAlpha)); - - for($i=0;$i<=count($BoundsA)-4;$i=$i+2) - { - $this->drawLine($BoundsA[$i],$BoundsA[$i+1],$BoundsA[$i+2],$BoundsA[$i+3],array("R"=>$LineR,"G"=>$LineG,"B"=>$LineB,"Alpha"=>$LineAlpha,"Ticks"=>$LineTicks)); - $this->drawLine($BoundsB[$i],$BoundsB[$i+1],$BoundsB[$i+2],$BoundsB[$i+3],array("R"=>$LineR,"G"=>$LineG,"B"=>$LineB,"Alpha"=>$LineAlpha,"Ticks"=>$LineTicks)); - } - } - } - - /* Draw a step chart */ - function drawStepChart($Format=NULL) - { - $BreakVoid = isset($Format["BreakVoid"]) ? $Format["BreakVoid"] : FALSE; - $ReCenter = isset($Format["ReCenter"]) ? $Format["ReCenter"] : TRUE; - $VoidTicks = isset($Format["VoidTicks"]) ? $Format["VoidTicks"] : 4; - $BreakR = isset($Format["BreakR"]) ? $Format["BreakR"] : NULL; - $BreakG = isset($Format["BreakG"]) ? $Format["BreakG"] : NULL; - $BreakB = isset($Format["BreakB"]) ? $Format["BreakB"] : NULL; - $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] :FALSE; - $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2; - $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; - $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; - $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; - $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; - $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; - $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 5; - - $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; - - $Data = $this->DataSet->getData(); - list($XMargin,$XDivs) = $this->scaleGetXSettings(); - foreach($Data["Series"] as $SerieName => $Serie) - { - if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) - { - $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; $Weight = $Serie["Weight"]; - - if (isset($Serie["Description"])) { $SerieDescription = $Serie["Description"]; } else { $SerieDescription = $SerieName; } - - if ( $BreakR == NULL ) - $BreakSettings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$VoidTicks,"Weight"=>$Weight); - else - $BreakSettings = array("R"=>$BreakR,"G"=>$BreakG,"B"=>$BreakB,"Alpha"=>$Alpha,"Ticks"=>$VoidTicks,"Weight"=>$Weight); - - if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; } - - $AxisID = $Serie["Axis"]; - $Mode = $Data["Axis"][$AxisID]["Display"]; - $Format = $Data["Axis"][$AxisID]["Format"]; - $Unit = $Data["Axis"][$AxisID]["Unit"]; - $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight); - - $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); - - $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } - $X = $this->GraphAreaX1 + $XMargin; $LastX = NULL; $LastY = NULL; - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - $LastGoodY = NULL; $LastGoodX = NULL; $Init = FALSE; - foreach($PosArray as $Key => $Y) - { - if ( $DisplayValues && $Serie["Data"][$Key] != VOID ) - { - if ( $Y <= $LastY ) { $Align = TEXT_ALIGN_BOTTOMMIDDLE; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_TOPMIDDLE; $Offset = -$DisplayOffset; } - $this->drawText($X,$Y-$Offset-$Weight,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>$Align)); - } - - if ( $Y != VOID && $LastX != NULL && $LastY != NULL ) - { - $this->drawLine($LastX,$LastY,$X,$LastY,$Color); - $this->drawLine($X,$LastY,$X,$Y,$Color); - if ( $ReCenter && $X+$XStep < $this->GraphAreaX2 - $XMargin ) - { - $this->drawLine($X,$Y,$X+$XStep,$Y,$Color); - if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X-$ImageMapPlotSize).",".floor($Y-$ImageMapPlotSize).",".floor($X+$XStep+$ImageMapPlotSize).",".floor($Y+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - } - else - { if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastX-$ImageMapPlotSize).",".floor($LastY-$ImageMapPlotSize).",".floor($X+$ImageMapPlotSize).",".floor($LastY+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } } - } - - if ( $Y != VOID && $LastY == NULL && $LastGoodY != NULL && !$BreakVoid ) - { - if ( $ReCenter ) - { - $this->drawLine($LastGoodX+$XStep,$LastGoodY,$X,$LastGoodY,$BreakSettings); - if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastGoodX+$XStep-$ImageMapPlotSize).",".floor($LastGoodY-$ImageMapPlotSize).",".floor($X+$ImageMapPlotSize).",".floor($LastGoodY+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - } - else - { - $this->drawLine($LastGoodX,$LastGoodY,$X,$LastGoodY,$BreakSettings); - if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastGoodX-$ImageMapPlotSize).",".floor($LastGoodY-$ImageMapPlotSize).",".floor($X+$ImageMapPlotSize).",".floor($LastGoodY+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - } - - $this->drawLine($X,$LastGoodY,$X,$Y,$BreakSettings); - $LastGoodY = NULL; - } - elseif( !$BreakVoid && $LastGoodY == NULL && $Y != VOID ) - { - $this->drawLine($this->GraphAreaX1 + $XMargin,$Y,$X,$Y,$BreakSettings); - if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($this->GraphAreaX1+$XMargin-$ImageMapPlotSize).",".floor($Y-$ImageMapPlotSize).",".floor($X+$ImageMapPlotSize).",".floor($Y+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - } - - if ( $Y != VOID ) { $LastGoodY = $Y; $LastGoodX = $X; } - if ( $Y == VOID ) { $Y = NULL; } - - if ( !$Init && $ReCenter ) { $X = $X - $XStep/2; $Init = TRUE; } - $LastX = $X; $LastY = $Y; - if ( $LastX < $this->GraphAreaX1 + $XMargin ) { $LastX = $this->GraphAreaX1 + $XMargin; } - $X = $X + $XStep; - } - if ( $ReCenter ) - { - $this->drawLine($LastX,$LastY,$this->GraphAreaX2 - $XMargin,$LastY,$Color); - if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastX-$ImageMapPlotSize).",".floor($LastY-$ImageMapPlotSize).",".floor($this->GraphAreaX2-$XMargin+$ImageMapPlotSize).",".floor($LastY+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - } - } - else - { - if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } - $Y = $this->GraphAreaY1 + $XMargin; $LastX = NULL; $LastY = NULL; - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - $LastGoodY = NULL; $LastGoodX = NULL; $Init = FALSE; - foreach($PosArray as $Key => $X) - { - if ( $DisplayValues && $Serie["Data"][$Key] != VOID ) - { - if ( $X >= $LastX ) { $Align = TEXT_ALIGN_MIDDLELEFT; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_MIDDLERIGHT; $Offset = -$DisplayOffset; } - $this->drawText($X+$Offset+$Weight,$Y,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>$Align)); - } - - if ( $X != VOID && $LastX != NULL && $LastY != NULL ) - { - $this->drawLine($LastX,$LastY,$LastX,$Y,$Color); - $this->drawLine($LastX,$Y,$X,$Y,$Color); - - if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastX-$ImageMapPlotSize).",".floor($LastY-$ImageMapPlotSize).",".floor($LastX+$XStep+$ImageMapPlotSize).",".floor($Y+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - } - - if ( $X != VOID && $LastX == NULL && $LastGoodY != NULL && !$BreakVoid ) - { - $this->drawLine($LastGoodX,$LastGoodY,$LastGoodX,$LastGoodY+$YStep,$Color); - if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastGoodX-$ImageMapPlotSize).",".floor($LastGoodY-$ImageMapPlotSize).",".floor($LastGoodX+$ImageMapPlotSize).",".floor($LastGoodY+$YStep+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - - $this->drawLine($LastGoodX,$LastGoodY+$YStep,$LastGoodX,$Y,$BreakSettings); - if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastGoodX-$ImageMapPlotSize).",".floor($LastGoodY+$YStep-$ImageMapPlotSize).",".floor($LastGoodX+$ImageMapPlotSize).",".floor($YStep+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - - $this->drawLine($LastGoodX,$Y,$X,$Y,$BreakSettings); - $LastGoodY = NULL; - } - elseif ( $X != VOID && $LastGoodY == NULL && !$BreakVoid ) - { - $this->drawLine($X,$this->GraphAreaY1 + $XMargin,$X,$Y,$BreakSettings); - if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X-$ImageMapPlotSize).",".floor($this->GraphAreaY1+$XMargin-$ImageMapPlotSize).",".floor($X+$ImageMapPlotSize).",".floor($Y+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - } - - if ( $X != VOID ) { $LastGoodY = $Y; $LastGoodX = $X; } - if ( $X == VOID ) { $X = NULL; } - - if ( !$Init && $ReCenter ) { $Y = $Y - $YStep/2; $Init = TRUE; } - $LastX = $X; $LastY = $Y; - if ( $LastY < $this->GraphAreaY1 + $XMargin ) { $LastY = $this->GraphAreaY1 + $XMargin; } - $Y = $Y + $YStep; - } - if ( $ReCenter ) - { - $this->drawLine($LastX,$LastY,$LastX,$this->GraphAreaY2 - $XMargin,$Color); - if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastX-$ImageMapPlotSize).",".floor($LastY-$ImageMapPlotSize).",".floor($LastX+$ImageMapPlotSize).",".floor($this->GraphAreaY2-$XMargin+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - } - } - } - } - } - - /* Draw a step chart */ - function drawFilledStepChart($Format=NULL) - { - $ReCenter = isset($Format["ReCenter"]) ? $Format["ReCenter"] : TRUE; - $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] :FALSE; - $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2; - $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; - $ForceTransparency = isset($Format["ForceTransparency"]) ? $Format["ForceTransparency"] : NULL; - $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; - $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; - $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; - $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : TRUE; - - $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; - - $Data = $this->DataSet->getData(); - list($XMargin,$XDivs) = $this->scaleGetXSettings(); - foreach($Data["Series"] as $SerieName => $Serie) - { - if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) - { - $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; $Weight = $Serie["Weight"]; - - if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; } - - $AxisID = $Serie["Axis"]; - $Mode = $Data["Axis"][$AxisID]["Display"]; - $Format = $Data["Axis"][$AxisID]["Format"]; - $Unit = $Data["Axis"][$AxisID]["Unit"]; - - $Color = array("R"=>$R,"G"=>$G,"B"=>$B); - if ( $ForceTransparency != NULL ) { $Color["Alpha"] = $ForceTransparency; } else { $Color["Alpha"] = $Alpha; } - - $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); - $YZero = $this->scaleComputeY(0,array("AxisID"=>$Serie["Axis"])); - - $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; } - if ( $YZero < $this->GraphAreaY1+1 ) { $YZero = $this->GraphAreaY1+1; } - - if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } - $X = $this->GraphAreaX1 + $XMargin; $LastX = NULL; $LastY = NULL; - - if ( !$AroundZero ) { $YZero = $this->GraphAreaY2-1; } - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - $LastGoodY = NULL; $LastGoodX = NULL; $Points = ""; $Init = FALSE; - foreach($PosArray as $Key => $Y) - { - if ( $Y == VOID && $LastX != NULL && $LastY != NULL && $Points !="" ) - { - $Points[] = $LastX; $Points[] = $LastY; - $Points[] = $X; $Points[] = $LastY; - $Points[] = $X; $Points[] = $YZero; - $this->drawPolygon($Points,$Color); - $Points = ""; - } - - if ( $Y != VOID && $LastX != NULL && $LastY != NULL ) - { - if ( $Points == "") { $Points[] = $LastX; $Points[] = $YZero; } - $Points[] = $LastX; $Points[] = $LastY; - $Points[] = $X; $Points[] = $LastY; - $Points[] = $X; $Points[] = $Y; - } - - if ( $Y != VOID ) { $LastGoodY = $Y; $LastGoodX = $X; } - if ( $Y == VOID ) { $Y = NULL; } - - if ( !$Init && $ReCenter ) { $X = $X - $XStep/2; $Init = TRUE; } - $LastX = $X; $LastY = $Y; - if ( $LastX < $this->GraphAreaX1 + $XMargin ) { $LastX = $this->GraphAreaX1 + $XMargin; } - $X = $X + $XStep; - } - - if ( $ReCenter ) - { - $Points[] = $LastX+$XStep/2; $Points[] = $LastY; - $Points[] = $LastX+$XStep/2; $Points[] = $YZero; - } - else - { $Points[] = $LastX; $Points[] = $YZero; } - - $this->drawPolygon($Points,$Color); - } - else - { - if ( $YZero < $this->GraphAreaX1+1 ) { $YZero = $this->GraphAreaX1+1; } - if ( $YZero > $this->GraphAreaX2-1 ) { $YZero = $this->GraphAreaX2-1; } - - if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } - $Y = $this->GraphAreaY1 + $XMargin; $LastX = NULL; $LastY = NULL; - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - $LastGoodY = NULL; $LastGoodX = NULL; $Points = ""; - foreach($PosArray as $Key => $X) - { - if ( $X == VOID && $LastX != NULL && $LastY != NULL && $Points !="" ) - { - $Points[] = $LastX; $Points[] = $LastY; - $Points[] = $LastX; $Points[] = $Y; - $Points[] = $YZero; $Points[] = $Y; - $this->drawPolygon($Points,$Color); - $Points = ""; - } - - if ( $X != VOID && $LastX != NULL && $LastY != NULL ) - { - if ( $Points == "") { $Points[] = $YZero; $Points[] = $LastY; } - $Points[] = $LastX; $Points[] = $LastY; - $Points[] = $LastX; $Points[] = $Y; - $Points[] = $X; $Points[] = $Y; - } - - if ( $X != VOID ) { $LastGoodY = $Y; $LastGoodX = $X; } - if ( $X == VOID ) { $X = NULL; } - - if ( $LastX == NULL && $ReCenter ) { $Y = $Y - $YStep/2; } - $LastX = $X; $LastY = $Y; - if ( $LastY < $this->GraphAreaY1 + $XMargin ) { $LastY = $this->GraphAreaY1 + $XMargin; } - $Y = $Y + $YStep; - } - - if ( $ReCenter ) - { - $Points[] = $LastX; $Points[] = $LastY+$YStep/2; - $Points[] = $YZero; $Points[] = $LastY+$YStep/2; - } - else - { $Points[] = $YZero; $Points[] = $LastY; } - - $this->drawPolygon($Points,$Color); - } - } - } - } - - /* Draw an area chart */ - function drawAreaChart($Format=NULL) - { - $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : FALSE; - $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2; - $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; - $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; - $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; - $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; - $ForceTransparency = isset($Format["ForceTransparency"]) ? $Format["ForceTransparency"] : 25; - $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : TRUE; - $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : NULL; - - $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; - - $Data = $this->DataSet->getData(); - list($XMargin,$XDivs) = $this->scaleGetXSettings(); - - foreach($Data["Series"] as $SerieName => $Serie) - { - if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) - { - $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; - if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; } - - $AxisID = $Serie["Axis"]; - $Mode = $Data["Axis"][$AxisID]["Display"]; - $Format = $Data["Axis"][$AxisID]["Format"]; - $Unit = $Data["Axis"][$AxisID]["Unit"]; - - $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); - $YZero = $this->scaleComputeY(0,array("AxisID"=>$Serie["Axis"])); - - if ( $Threshold != NULL ) - { - foreach($Threshold as $Key => $Params) - { - $Threshold[$Key]["MinX"] = $this->scaleComputeY($Params["Min"],array("AxisID"=>$Serie["Axis"])); - $Threshold[$Key]["MaxX"] = $this->scaleComputeY($Params["Max"],array("AxisID"=>$Serie["Axis"])); - } - } - - $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; } - - $Areas = ""; $AreaID = 0; - $Areas[$AreaID][] = $this->GraphAreaX1 + $XMargin; - if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaY2-1; } - - if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } - $X = $this->GraphAreaX1 + $XMargin; $LastX = NULL; $LastY = NULL; - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - foreach($PosArray as $Key => $Y) - { - if ( $DisplayValues && $Serie["Data"][$Key] != VOID ) - { - if ( $Serie["Data"][$Key] > 0 ) { $Align = TEXT_ALIGN_BOTTOMMIDDLE; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_TOPMIDDLE; $Offset = -$DisplayOffset; } - $this->drawText($X,$Y-$Offset,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>$Align)); - } - - if ( $Y == VOID && isset($Areas[$AreaID]) ) - { - if($LastX == NULL) - { $Areas[$AreaID][] = $X; } - else - { $Areas[$AreaID][] = $LastX; } - - if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaY2-1; } - $AreaID++; - } - elseif ($Y != VOID) - { - if ( !isset($Areas[$AreaID]) ) - { - $Areas[$AreaID][] = $X; - if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaY2-1; } - } - - $Areas[$AreaID][] = $X; - $Areas[$AreaID][] = $Y; - } - - $LastX = $X; - $X = $X + $XStep; - } - $Areas[$AreaID][] = $LastX; - if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaY2-1; } - - /* Handle shadows in the areas */ - if ( $this->Shadow ) - { - $ShadowArea = ""; - foreach($Areas as $Key => $Points) - { - $ShadowArea[$Key] = ""; - foreach($Points as $Key2 => $Value) - { - if ( $Key2 % 2 == 0 ) - { $ShadowArea[$Key][] = $Value + $this->ShadowX; } - else - { $ShadowArea[$Key][] = $Value + $this->ShadowY; } - } - } - - foreach($ShadowArea as $Key => $Points) - $this->drawPolygonChart($Points,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa)); - } - - $Alpha = $ForceTransparency != NULL ? $ForceTransparency : $Alpha; - $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Threshold"=>$Threshold); - - foreach($Areas as $Key => $Points) - $this->drawPolygonChart($Points,$Color); - } - else - { - if ( $YZero < $this->GraphAreaX1+1 ) { $YZero = $this->GraphAreaX1+1; } - if ( $YZero > $this->GraphAreaX2-1 ) { $YZero = $this->GraphAreaX2-1; } - - $Areas = ""; $AreaID = 0; - if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaX1+1; } - $Areas[$AreaID][] = $this->GraphAreaY1 + $XMargin; - - if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } - $Y = $this->GraphAreaY1 + $XMargin; $LastX = NULL; $LastY = NULL; - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - foreach($PosArray as $Key => $X) - { - if ( $DisplayValues && $Serie["Data"][$Key] != VOID ) - { - if ( $Serie["Data"][$Key] > 0 ) { $Align = TEXT_ALIGN_BOTTOMMIDDLE; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_TOPMIDDLE; $Offset = -$DisplayOffset; } - $this->drawText($X+$Offset,$Y,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("Angle"=>270,"R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>$Align)); - } - - if ( $X == VOID && isset($Areas[$AreaID]) ) - { - if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaX1+1; } - - if($LastY == NULL) - { $Areas[$AreaID][] = $Y; } - else - { $Areas[$AreaID][] = $LastY; } - - $AreaID++; - } - elseif ($X != VOID) - { - if ( !isset($Areas[$AreaID]) ) - { - if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaX1+1; } - $Areas[$AreaID][] = $Y; - } - - $Areas[$AreaID][] = $X; - $Areas[$AreaID][] = $Y; - } - - $LastX = $X; $LastY = $Y; - $Y = $Y + $YStep; - } - if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaX1+1; } - $Areas[$AreaID][] = $LastY; - - /* Handle shadows in the areas */ - if ( $this->Shadow ) - { - $ShadowArea = ""; - foreach($Areas as $Key => $Points) - { - $ShadowArea[$Key] = ""; - foreach($Points as $Key2 => $Value) - { - if ( $Key2 % 2 == 0 ) - { $ShadowArea[$Key][] = $Value + $this->ShadowX; } - else - { $ShadowArea[$Key][] = $Value + $this->ShadowY; } - } - } - - foreach($ShadowArea as $Key => $Points) - $this->drawPolygonChart($Points,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa)); - } - - $Alpha = $ForceTransparency != NULL ? $ForceTransparency : $Alpha; - $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Threshold"=>$Threshold); - - foreach($Areas as $Key => $Points) - $this->drawPolygonChart($Points,$Color); - } - } - } - } - - - /* Draw a bar chart */ - function drawBarChart($Format=NULL) - { - $Floating0Serie = isset($Format["Floating0Serie"]) ? $Format["Floating0Serie"] : NULL; - $Floating0Value = isset($Format["Floating0Value"]) ? $Format["Floating0Value"] : NULL; - $Draw0Line = isset($Format["Draw0Line"]) ? $Format["Draw0Line"] : FALSE; - $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : FALSE; - $DisplayOrientation = isset($Format["DisplayOrientation"]) ? $Format["DisplayOrientation"] : ORIENTATION_HORIZONTAL; - $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2; - $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; - $DisplayFont = isset($Format["DisplaySize"]) ? $Format["DisplaySize"] : $this->FontName; - $DisplaySize = isset($Format["DisplaySize"]) ? $Format["DisplaySize"] : $this->FontSize; - $DisplayPos = isset($Format["DisplayPos"]) ? $Format["DisplayPos"] : LABEL_POS_OUTSIDE; - $DisplayShadow = isset($Format["DisplayShadow"]) ? $Format["DisplayShadow"] : TRUE; - $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; - $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; - $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; - $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : TRUE; - $Interleave = isset($Format["Interleave"]) ? $Format["Interleave"] : .5; - $Rounded = isset($Format["Rounded"]) ? $Format["Rounded"] : FALSE; - $RoundRadius = isset($Format["RoundRadius"]) ? $Format["RoundRadius"] : 4; - $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1; - $Gradient = isset($Format["Gradient"]) ? $Format["Gradient"] : FALSE; - $GradientMode = isset($Format["GradientMode"]) ? $Format["GradientMode"] : GRADIENT_SIMPLE; - $GradientAlpha = isset($Format["GradientAlpha"]) ? $Format["GradientAlpha"] : 20; - $GradientStartR = isset($Format["GradientStartR"]) ? $Format["GradientStartR"] : 255; - $GradientStartG = isset($Format["GradientStartG"]) ? $Format["GradientStartG"] : 255; - $GradientStartB = isset($Format["GradientStartB"]) ? $Format["GradientStartB"] : 255; - $GradientEndR = isset($Format["GradientEndR"]) ? $Format["GradientEndR"] : 0; - $GradientEndG = isset($Format["GradientEndG"]) ? $Format["GradientEndG"] : 0; - $GradientEndB = isset($Format["GradientEndB"]) ? $Format["GradientEndB"] : 0; - $TxtMargin = isset($Format["TxtMargin"]) ? $Format["TxtMargin"] : 6; - $OverrideColors = isset($Format["OverrideColors"]) ? $Format["OverrideColors"] : NULL; - $OverrideSurrounding = isset($Format["OverrideSurrounding"]) ? $Format["OverrideSurrounding"] : 30; - $InnerSurrounding = isset($Format["InnerSurrounding"]) ? $Format["InnerSurrounding"] : NULL; - $InnerBorderR = isset($Format["InnerBorderR"]) ? $Format["InnerBorderR"] : -1; - $InnerBorderG = isset($Format["InnerBorderG"]) ? $Format["InnerBorderG"] : -1; - $InnerBorderB = isset($Format["InnerBorderB"]) ? $Format["InnerBorderB"] : -1; - $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; - - $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; - - $Data = $this->DataSet->getData(); - list($XMargin,$XDivs) = $this->scaleGetXSettings(); - - if ( $OverrideColors != NULL ) - { - $OverrideColors = $this->validatePalette($OverrideColors,$OverrideSurrounding); - $this->DataSet->saveExtendedData("Palette",$OverrideColors); - } - - $RestoreShadow = $this->Shadow; - - $SeriesCount = $this->countDrawableSeries(); - $CurrentSerie = 0; - foreach($Data["Series"] as $SerieName => $Serie) - { - if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) - { - $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; - if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; } - if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; } - if ( $InnerSurrounding != NULL ) { $InnerBorderR = $R+$InnerSurrounding; $InnerBorderG = $G+$InnerSurrounding; $InnerBorderB = $B+$InnerSurrounding; } - if ( $InnerBorderR == -1 ) { $InnerColor = NULL; } else { $InnerColor = array("R"=>$InnerBorderR,"G"=>$InnerBorderG,"B"=>$InnerBorderB); } - $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB); - - $AxisID = $Serie["Axis"]; - $Mode = $Data["Axis"][$AxisID]["Display"]; - $Format = $Data["Axis"][$AxisID]["Format"]; - $Unit = $Data["Axis"][$AxisID]["Unit"]; - - if (isset($Serie["Description"])) { $SerieDescription = $Serie["Description"]; } else { $SerieDescription = $SerieName; } - - $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); - - if ( $Floating0Value != NULL ) - { $YZero = $this->scaleComputeY($Floating0Value,array("AxisID"=>$Serie["Axis"])); } - else - { $YZero = $this->scaleComputeY(0,array("AxisID"=>$Serie["Axis"])); } - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; } - if ( $YZero < $this->GraphAreaY1+1 ) { $YZero = $this->GraphAreaY1+1; } - - if ( $XDivs == 0 ) { $XStep = 0; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } - $X = $this->GraphAreaX1 + $XMargin; - - if ( $AroundZero ) { $Y1 = $YZero; } else { $Y1 = $this->GraphAreaY2-1; } - if ( $XDivs == 0 ) { $XSize = ($this->GraphAreaX2-$this->GraphAreaX1)/($SeriesCount+$Interleave); } else { $XSize = ($XStep / ($SeriesCount+$Interleave) ); } - - $XOffset = -($XSize*$SeriesCount)/2 + $CurrentSerie * $XSize; - if ( $X + $XOffset <= $this->GraphAreaX1 ) { $XOffset = $this->GraphAreaX1 - $X + 1 ; } - - $this->DataSet->Data["Series"][$SerieName]["XOffset"] = $XOffset + $XSize / 2; - - if ( $Rounded || $BorderR != -1) { $XSpace = 1; } else { $XSpace = 0; } - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - - $ID = 0; - foreach($PosArray as $Key => $Y2) - { - if ( $Floating0Serie != NULL ) - { - if ( isset($Data["Series"][$Floating0Serie]["Data"][$Key]) ) - { $Value = $Data["Series"][$Floating0Serie]["Data"][$Key]; } - else - { $Value = 0; } - - $YZero = $this->scaleComputeY($Value,array("AxisID"=>$Serie["Axis"])); - if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; } - if ( $YZero < $this->GraphAreaY1+1 ) { $YZero = $this->GraphAreaY1+1; } - - if ( $AroundZero ) { $Y1 = $YZero; } else { $Y1 = $this->GraphAreaY2-1; } - } - - if ( $OverrideColors != NULL ) - { if ( isset($OverrideColors[$ID]) ) { $Color = array("R"=>$OverrideColors[$ID]["R"],"G"=>$OverrideColors[$ID]["G"],"B"=>$OverrideColors[$ID]["B"],"Alpha"=>$OverrideColors[$ID]["Alpha"],"BorderR"=>$OverrideColors[$ID]["BorderR"],"BorderG"=>$OverrideColors[$ID]["BorderG"],"BorderB"=>$OverrideColors[$ID]["BorderB"]); } else { $Color = $this->getRandomColor(); } } - - if ( $Y2 != VOID ) - { - $BarHeight = $Y1 - $Y2; - - if ( $Serie["Data"][$Key] == 0 ) - { - $this->drawLine($X+$XOffset+$XSpace,$Y1,$X+$XOffset+$XSize-$XSpace,$Y1,$Color); - if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X+$XOffset+$XSpace).",".floor($Y1-1).",".floor($X+$XOffset+$XSize-$XSpace).",".floor($Y1+1),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - } - else - { - if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X+$XOffset+$XSpace).",".floor($Y1).",".floor($X+$XOffset+$XSize-$XSpace).",".floor($Y2),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - - if ( $Rounded ) - $this->drawRoundedFilledRectangle($X+$XOffset+$XSpace,$Y1,$X+$XOffset+$XSize-$XSpace,$Y2,$RoundRadius,$Color); - else - { - $this->drawFilledRectangle($X+$XOffset+$XSpace,$Y1,$X+$XOffset+$XSize-$XSpace,$Y2,$Color); - - if ( $InnerColor != NULL ) { $this->drawRectangle($X+$XOffset+$XSpace+1,min($Y1,$Y2)+1,$X+$XOffset+$XSize-$XSpace-1,max($Y1,$Y2)-1,$InnerColor); } - - if ( $Gradient ) - { - $this->Shadow = FALSE; - - if ( $GradientMode == GRADIENT_SIMPLE ) - { - if ( $Serie["Data"][$Key] >= 0 ) - $GradienColor = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha); - else - $GradienColor = array("StartR"=>$GradientEndR,"StartG"=>$GradientEndG,"StartB"=>$GradientEndB,"EndR"=>$GradientStartR,"EndG"=>$GradientStartG,"EndB"=>$GradientStartB,"Alpha"=>$GradientAlpha); - - $this->drawGradientArea($X+$XOffset+$XSpace,$Y1,$X+$XOffset+$XSize-$XSpace,$Y2,DIRECTION_VERTICAL,$GradienColor); - } - elseif ( $GradientMode == GRADIENT_EFFECT_CAN ) - { - $GradienColor1 = array("StartR"=>$GradientEndR,"StartG"=>$GradientEndG,"StartB"=>$GradientEndB,"EndR"=>$GradientStartR,"EndG"=>$GradientStartG,"EndB"=>$GradientStartB,"Alpha"=>$GradientAlpha); - $GradienColor2 = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha); - $XSpan = floor($XSize / 3); - - $this->drawGradientArea($X+$XOffset+$XSpace,$Y1,$X+$XOffset+$XSpan-$XSpace,$Y2,DIRECTION_HORIZONTAL,$GradienColor1); - $this->drawGradientArea($X+$XOffset+$XSpan+$XSpace,$Y1,$X+$XOffset+$XSize-$XSpace,$Y2,DIRECTION_HORIZONTAL,$GradienColor2); - } - $this->Shadow = $RestoreShadow; - } - } - - if ( $Draw0Line ) - { - $Line0Color = array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20); - - if ( abs($Y1 - $Y2) > 3 ) { $Line0Width = 3; } else { $Line0Width = 1; } - if ( $Y1 - $Y2 < 0 ) { $Line0Width = -$Line0Width; } - - $this->drawFilledRectangle($X+$XOffset+$XSpace,floor($Y1),$X+$XOffset+$XSize-$XSpace,floor($Y1)-$Line0Width,$Line0Color); - $this->drawLine($X+$XOffset+$XSpace,floor($Y1),$X+$XOffset+$XSize-$XSpace,floor($Y1),$Line0Color); - } - } - - if ( $DisplayValues && $Serie["Data"][$Key] != VOID ) - { - if ( $DisplayShadow ) { $this->Shadow = TRUE; } - - $Caption = $this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit); - $TxtPos = $this->getTextBox(0,0,$DisplayFont,$DisplaySize,90,$Caption); - $TxtHeight = $TxtPos[0]["Y"] - $TxtPos[1]["Y"] + $TxtMargin; - - if ( $DisplayPos == LABEL_POS_INSIDE && abs($TxtHeight) < abs($BarHeight) ) - { - $CenterX = (($X+$XOffset+$XSize-$XSpace)-($X+$XOffset+$XSpace))/2 + $X+$XOffset+$XSpace; - $CenterY = ($Y2-$Y1)/2 + $Y1; - - $this->drawText($CenterX,$CenterY,$Caption,array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontSize"=>$DisplaySize,"Angle"=>90)); - } - else - { - if ( $Serie["Data"][$Key] >= 0 ) { $Align = TEXT_ALIGN_BOTTOMMIDDLE; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_TOPMIDDLE; $Offset = -$DisplayOffset; } - $this->drawText($X+$XOffset+$XSize/2,$Y2-$Offset,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>$Align,"FontSize"=>$DisplaySize)); - } - - $this->Shadow = $RestoreShadow; - } - } - - $X = $X + $XStep; - $ID++; - } - } - else - { - if ( $YZero < $this->GraphAreaX1+1 ) { $YZero = $this->GraphAreaX1+1; } - if ( $YZero > $this->GraphAreaX2-1 ) { $YZero = $this->GraphAreaX2-1; } - - if ( $XDivs == 0 ) { $YStep = 0; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } - - $Y = $this->GraphAreaY1 + $XMargin; - - if ( $AroundZero ) { $X1 = $YZero; } else { $X1 = $this->GraphAreaX1+1; } - if ( $XDivs == 0 ) { $YSize = ($this->GraphAreaY2-$this->GraphAreaY1)/($SeriesCount+$Interleave); } else { $YSize = ($YStep / ($SeriesCount+$Interleave) ); } - - $YOffset = -($YSize*$SeriesCount)/2 + $CurrentSerie * $YSize; - if ( $Y + $YOffset <= $this->GraphAreaY1 ) { $YOffset = $this->GraphAreaY1 - $Y + 1 ; } - - $this->DataSet->Data["Series"][$SerieName]["XOffset"] = $YOffset + $YSize / 2; - - if ( $Rounded || $BorderR != -1 ) { $YSpace = 1; } else { $YSpace = 0; } - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - - $ID = 0 ; - foreach($PosArray as $Key => $X2) - { - if ( $Floating0Serie != NULL ) - { - if ( isset($Data["Series"][$Floating0Serie]["Data"][$Key]) ) - $Value = $Data["Series"][$Floating0Serie]["Data"][$Key]; - else { $Value = 0; } - - $YZero = $this->scaleComputeY($Value,array("AxisID"=>$Serie["Axis"])); - if ( $YZero < $this->GraphAreaX1+1 ) { $YZero = $this->GraphAreaX1+1; } - if ( $YZero > $this->GraphAreaX2-1 ) { $YZero = $this->GraphAreaX2-1; } - if ( $AroundZero ) { $X1 = $YZero; } else { $X1 = $this->GraphAreaX1+1; } - } - - if ( $OverrideColors != NULL ) - { if ( isset($OverrideColors[$ID]) ) { $Color = array("R"=>$OverrideColors[$ID]["R"],"G"=>$OverrideColors[$ID]["G"],"B"=>$OverrideColors[$ID]["B"],"Alpha"=>$OverrideColors[$ID]["Alpha"],"BorderR"=>$OverrideColors[$ID]["BorderR"],"BorderG"=>$OverrideColors[$ID]["BorderG"],"BorderB"=>$OverrideColors[$ID]["BorderB"]); } else { $Color = $this->getRandomColor(); } } - - if ( $X2 != VOID ) - { - $BarWidth = $X2 - $X1; - - if ( $Serie["Data"][$Key] == 0 ) - { - $this->drawLine($X1,$Y+$YOffset+$YSpace,$X1,$Y+$YOffset+$YSize-$YSpace,$Color); - if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X1-1).",".floor($Y+$YOffset+$YSpace).",".floor($X1+1).",".floor($Y+$YOffset+$YSize-$YSpace),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - } - else - { - if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X1).",".floor($Y+$YOffset+$YSpace).",".floor($X2).",".floor($Y+$YOffset+$YSize-$YSpace),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - - if ( $Rounded ) - $this->drawRoundedFilledRectangle($X1+1,$Y+$YOffset+$YSpace,$X2,$Y+$YOffset+$YSize-$YSpace,$RoundRadius,$Color); - else - { - $this->drawFilledRectangle($X1,$Y+$YOffset+$YSpace,$X2,$Y+$YOffset+$YSize-$YSpace,$Color); - - if ( $InnerColor != NULL ) { $this->drawRectangle(min($X1,$X2)+1,$Y+$YOffset+$YSpace+1,max($X1,$X2)-1,$Y+$YOffset+$YSize-$YSpace-1,$InnerColor); } - - if ( $Gradient ) - { - $this->Shadow = FALSE; - - if ( $GradientMode == GRADIENT_SIMPLE ) - { - if ( $Serie["Data"][$Key] >= 0 ) - $GradienColor = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha); - else - $GradienColor = array("StartR"=>$GradientEndR,"StartG"=>$GradientEndG,"StartB"=>$GradientEndB,"EndR"=>$GradientStartR,"EndG"=>$GradientStartG,"EndB"=>$GradientStartB,"Alpha"=>$GradientAlpha); - - $this->drawGradientArea($X1,$Y+$YOffset+$YSpace,$X2,$Y+$YOffset+$YSize-$YSpace,DIRECTION_HORIZONTAL,$GradienColor); - } - elseif ( $GradientMode == GRADIENT_EFFECT_CAN ) - { - $GradienColor1 = array("StartR"=>$GradientEndR,"StartG"=>$GradientEndG,"StartB"=>$GradientEndB,"EndR"=>$GradientStartR,"EndG"=>$GradientStartG,"EndB"=>$GradientStartB,"Alpha"=>$GradientAlpha); - $GradienColor2 = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha); - $YSpan = floor($YSize / 3); - - $this->drawGradientArea($X1,$Y+$YOffset+$YSpace,$X2,$Y+$YOffset+$YSpan-$YSpace,DIRECTION_VERTICAL,$GradienColor1); - $this->drawGradientArea($X1,$Y+$YOffset+$YSpan,$X2,$Y+$YOffset+$YSize-$YSpace,DIRECTION_VERTICAL,$GradienColor2); - } - $this->Shadow = $RestoreShadow; - } - } - - if ( $Draw0Line ) - { - $Line0Color = array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20); - - if ( abs($X1 - $X2) > 3 ) { $Line0Width = 3; } else { $Line0Width = 1; } - if ( $X2 - $X1 < 0 ) { $Line0Width = -$Line0Width; } - - $this->drawFilledRectangle(floor($X1),$Y+$YOffset+$YSpace,floor($X1)+$Line0Width,$Y+$YOffset+$YSize-$YSpace,$Line0Color); - $this->drawLine(floor($X1),$Y+$YOffset+$YSpace,floor($X1),$Y+$YOffset+$YSize-$YSpace,$Line0Color); - } - } - - if ( $DisplayValues && $Serie["Data"][$Key] != VOID ) - { - if ( $DisplayShadow ) { $this->Shadow = TRUE; } - - $Caption = $this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit); - $TxtPos = $this->getTextBox(0,0,$DisplayFont,$DisplaySize,0,$Caption); - $TxtWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"] + $TxtMargin; - - if ( $DisplayPos == LABEL_POS_INSIDE && abs($TxtWidth) < abs($BarWidth) ) - { - $CenterX = ($X2-$X1)/2 + $X1; - $CenterY = (($Y+$YOffset+$YSize-$YSpace)-($Y+$YOffset+$YSpace))/2 + ($Y+$YOffset+$YSpace); - - $this->drawText($CenterX,$CenterY,$Caption,array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontSize"=>$DisplaySize)); - } - else - { - if ( $Serie["Data"][$Key] >= 0 ) { $Align = TEXT_ALIGN_MIDDLELEFT; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_MIDDLERIGHT; $Offset = -$DisplayOffset; } - $this->drawText($X2+$Offset,$Y+$YOffset+$YSize/2,$Caption,array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>$Align,"FontSize"=>$DisplaySize)); - } - - $this->Shadow = $RestoreShadow; - } - } - $Y = $Y + $YStep; - $ID++; - } - } - $CurrentSerie++; - } - } - } - - /* Draw a bar chart */ - function drawStackedBarChart($Format=NULL) - { - $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : FALSE; - $DisplayOrientation = isset($Format["DisplayOrientation"]) ? $Format["DisplayOrientation"] : ORIENTATION_AUTO; - $DisplayRound = isset($Format["DisplayRound"]) ? $Format["DisplayRound"] : 0; - $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; - $DisplayFont = isset($Format["DisplayFont"]) ? $Format["DisplayFont"] : $this->FontName; - $DisplaySize = isset($Format["DisplaySize"]) ? $Format["DisplaySize"] : $this->FontSize; - $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; - $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; - $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; - $Interleave = isset($Format["Interleave"]) ? $Format["Interleave"] : .5; - $Rounded = isset($Format["Rounded"]) ? $Format["Rounded"] : FALSE; - $RoundRadius = isset($Format["RoundRadius"]) ? $Format["RoundRadius"] : 4; - $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1; - $Gradient = isset($Format["Gradient"]) ? $Format["Gradient"] : FALSE; - $GradientMode = isset($Format["GradientMode"]) ? $Format["GradientMode"] : GRADIENT_SIMPLE; - $GradientAlpha = isset($Format["GradientAlpha"]) ? $Format["GradientAlpha"] : 20; - $GradientStartR = isset($Format["GradientStartR"]) ? $Format["GradientStartR"] : 255; - $GradientStartG = isset($Format["GradientStartG"]) ? $Format["GradientStartG"] : 255; - $GradientStartB = isset($Format["GradientStartB"]) ? $Format["GradientStartB"] : 255; - $GradientEndR = isset($Format["GradientEndR"]) ? $Format["GradientEndR"] : 0; - $GradientEndG = isset($Format["GradientEndG"]) ? $Format["GradientEndG"] : 0; - $GradientEndB = isset($Format["GradientEndB"]) ? $Format["GradientEndB"] : 0; - $InnerSurrounding = isset($Format["InnerSurrounding"]) ? $Format["InnerSurrounding"] : NULL; - $InnerBorderR = isset($Format["InnerBorderR"]) ? $Format["InnerBorderR"] : -1; - $InnerBorderG = isset($Format["InnerBorderG"]) ? $Format["InnerBorderG"] : -1; - $InnerBorderB = isset($Format["InnerBorderB"]) ? $Format["InnerBorderB"] : -1; - $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; - $FontFactor = isset($Format["FontFactor"]) ? $Format["FontFactor"] : 8; - - $this->LastChartLayout = CHART_LAST_LAYOUT_STACKED; - - $Data = $this->DataSet->getData(); - list($XMargin,$XDivs) = $this->scaleGetXSettings(); - - $RestoreShadow = $this->Shadow; - - $LastX = ""; $LastY = ""; - foreach($Data["Series"] as $SerieName => $Serie) - { - if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) - { - $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; - if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = 255; $DisplayG = 255; $DisplayB = 255; } - if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; } - if ( $InnerSurrounding != NULL ) { $InnerBorderR = $R+$InnerSurrounding; $InnerBorderG = $G+$InnerSurrounding; $InnerBorderB = $B+$InnerSurrounding; } - if ( $InnerBorderR == -1 ) { $InnerColor = NULL; } else { $InnerColor = array("R"=>$InnerBorderR,"G"=>$InnerBorderG,"B"=>$InnerBorderB); } - - $AxisID = $Serie["Axis"]; - $Mode = $Data["Axis"][$AxisID]["Display"]; - $Format = $Data["Axis"][$AxisID]["Format"]; - $Unit = $Data["Axis"][$AxisID]["Unit"]; - - if (isset($Serie["Description"])) { $SerieDescription = $Serie["Description"]; } else { $SerieDescription = $SerieName; } - - $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"]),TRUE); - $YZero = $this->scaleComputeY(0,array("AxisID"=>$Serie["Axis"])); - - $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; - - $Color = array("TransCorner"=>TRUE,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB); - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; } - if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; } - - if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } - $X = $this->GraphAreaX1 + $XMargin; - - $XSize = ($XStep / (1+$Interleave) ); - $XOffset = -($XSize/2); - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - foreach($PosArray as $Key => $Height) - { - if ( $Height != VOID && $Serie["Data"][$Key] != 0 ) - { - if ( $Serie["Data"][$Key] > 0 ) { $Pos = "+"; } else { $Pos = "-"; } - - if ( !isset($LastY[$Key] ) ) { $LastY[$Key] = ""; } - if ( !isset($LastY[$Key][$Pos] ) ) { $LastY[$Key][$Pos] = $YZero; } - - $Y1 = $LastY[$Key][$Pos]; - $Y2 = $Y1 - $Height; - - if ( ($Rounded || $BorderR != -1) && ($Pos == "+" && $Y1 != $YZero) ) { $YSpaceUp = 1; } else { $YSpaceUp = 0; } - if ( ($Rounded || $BorderR != -1) && ($Pos == "-" && $Y1 != $YZero) ) { $YSpaceDown = 1; } else { $YSpaceDown = 0; } - - if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X+$XOffset).",".floor($Y1-$YSpaceUp+$YSpaceDown).",".floor($X+$XOffset+$XSize).",".floor($Y2),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - - if ( $Rounded ) - $this->drawRoundedFilledRectangle($X+$XOffset,$Y1-$YSpaceUp+$YSpaceDown,$X+$XOffset+$XSize,$Y2,$RoundRadius,$Color); - else - { - $this->drawFilledRectangle($X+$XOffset,$Y1-$YSpaceUp+$YSpaceDown,$X+$XOffset+$XSize,$Y2,$Color); - - if ( $InnerColor != NULL ) { $RestoreShadow = $this->Shadow; $this->Shadow = FALSE; $this->drawRectangle(min($X+$XOffset+1,$X+$XOffset+$XSize),min($Y1-$YSpaceUp+$YSpaceDown,$Y2)+1,max($X+$XOffset+1,$X+$XOffset+$XSize)-1,max($Y1-$YSpaceUp+$YSpaceDown,$Y2)-1,$InnerColor); $this->Shadow = $RestoreShadow;} - - if ( $Gradient ) - { - $this->Shadow = FALSE; - - if ( $GradientMode == GRADIENT_SIMPLE ) - { - $GradientColor = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha); - $this->drawGradientArea($X+$XOffset,$Y1-1-$YSpaceUp+$YSpaceDown,$X+$XOffset+$XSize,$Y2+1,DIRECTION_VERTICAL,$GradientColor); - } - elseif ( $GradientMode == GRADIENT_EFFECT_CAN ) - { - $GradientColor1 = array("StartR"=>$GradientEndR,"StartG"=>$GradientEndG,"StartB"=>$GradientEndB,"EndR"=>$GradientStartR,"EndG"=>$GradientStartG,"EndB"=>$GradientStartB,"Alpha"=>$GradientAlpha); - $GradientColor2 = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha); - $XSpan = floor($XSize / 3); - - $this->drawGradientArea($X+$XOffset-.5,$Y1-.5-$YSpaceUp+$YSpaceDown,$X+$XOffset+$XSpan,$Y2+.5,DIRECTION_HORIZONTAL,$GradientColor1); - $this->drawGradientArea($X+$XSpan+$XOffset-.5,$Y1-.5-$YSpaceUp+$YSpaceDown,$X+$XOffset+$XSize,$Y2+.5,DIRECTION_HORIZONTAL,$GradientColor2); - } - $this->Shadow = $RestoreShadow; - } - } - - if ( $DisplayValues ) - { - $BarHeight = abs($Y2-$Y1)-2; - $BarWidth = $XSize+($XOffset/2)-$FontFactor; - - $Caption = $this->scaleFormat(round($Serie["Data"][$Key],$DisplayRound),$Mode,$Format,$Unit); - $TxtPos = $this->getTextBox(0,0,$DisplayFont,$DisplaySize,0,$Caption); - $TxtHeight = abs($TxtPos[2]["Y"] - $TxtPos[0]["Y"]); - $TxtWidth = abs($TxtPos[1]["X"] - $TxtPos[0]["X"]); - - $XCenter = ( ($X+$XOffset+$XSize) - ($X+$XOffset) ) / 2 + $X+$XOffset; - $YCenter = ( ($Y2) - ($Y1-$YSpaceUp+$YSpaceDown) ) / 2 + $Y1-$YSpaceUp+$YSpaceDown; - - $Done = FALSE; - if ( $DisplayOrientation == ORIENTATION_HORIZONTAL || $DisplayOrientation == ORIENTATION_AUTO ) - { - if ( $TxtHeight < $BarHeight && $TxtWidth < $BarWidth ) - { - $this->drawText($XCenter,$YCenter,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontSize"=>$DisplaySize,"FontName"=>$DisplayFont)); - $Done = TRUE; - } - } - - if ( $DisplayOrientation == ORIENTATION_VERTICAL || ( $DisplayOrientation == ORIENTATION_AUTO && !$Done) ) - { - if ( $TxtHeight < $BarWidth && $TxtWidth < $BarHeight ) - $this->drawText($XCenter,$YCenter,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Angle"=>90,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontSize"=>$DisplaySize,"FontName"=>$DisplayFont)); - } - } - - $LastY[$Key][$Pos] = $Y2; - } - - $X = $X + $XStep; - } - } - else - { - if ( $YZero < $this->GraphAreaX1+1 ) { $YZero = $this->GraphAreaX1+1; } - if ( $YZero > $this->GraphAreaX2-1 ) { $YZero = $this->GraphAreaX2-1; } - - if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } - $Y = $this->GraphAreaY1 + $XMargin; - - $YSize = $YStep / (1+$Interleave); - $YOffset = -($YSize/2); - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - foreach($PosArray as $Key => $Width) - { - if ( $Width != VOID && $Serie["Data"][$Key] != 0 ) - { - if ( $Serie["Data"][$Key] > 0 ) { $Pos = "+"; } else { $Pos = "-"; } - - if ( !isset($LastX[$Key] ) ) { $LastX[$Key] = ""; } - if ( !isset($LastX[$Key][$Pos] ) ) { $LastX[$Key][$Pos] = $YZero; } - - $X1 = $LastX[$Key][$Pos]; - $X2 = $X1 + $Width; - - if ( ($Rounded || $BorderR != -1) && ($Pos == "+" && $X1 != $YZero) ) { $XSpaceLeft = 2; } else { $XSpaceLeft = 0; } - if ( ($Rounded || $BorderR != -1) && ($Pos == "-" && $X1 != $YZero) ) { $XSpaceRight = 2; } else { $XSpaceRight = 0; } - - if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X1+$XSpaceLeft).",".floor($Y+$YOffset).",".floor($X2-$XSpaceRight).",".floor($Y+$YOffset+$YSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } - - if ( $Rounded ) - $this->drawRoundedFilledRectangle($X1+$XSpaceLeft,$Y+$YOffset,$X2-$XSpaceRight,$Y+$YOffset+$YSize,$RoundRadius,$Color); - else - { - $this->drawFilledRectangle($X1+$XSpaceLeft,$Y+$YOffset,$X2-$XSpaceRight,$Y+$YOffset+$YSize,$Color); - - if ( $InnerColor != NULL ) { $RestoreShadow = $this->Shadow; $this->Shadow = FALSE; $this->drawRectangle(min($X1+$XSpaceLeft,$X2-$XSpaceRight)+1,min($Y+$YOffset,$Y+$YOffset+$YSize)+1,max($X1+$XSpaceLeft,$X2-$XSpaceRight)-1,max($Y+$YOffset,$Y+$YOffset+$YSize)-1,$InnerColor); $this->Shadow = $RestoreShadow;} - - if ( $Gradient ) - { - $this->Shadow = FALSE; - - if ( $GradientMode == GRADIENT_SIMPLE ) - { - $GradientColor = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha); - $this->drawGradientArea($X1+$XSpaceLeft,$Y+$YOffset,$X2-$XSpaceRight,$Y+$YOffset+$YSize,DIRECTION_HORIZONTAL,$GradientColor); - } - elseif ( $GradientMode == GRADIENT_EFFECT_CAN ) - { - $GradientColor1 = array("StartR"=>$GradientEndR,"StartG"=>$GradientEndG,"StartB"=>$GradientEndB,"EndR"=>$GradientStartR,"EndG"=>$GradientStartG,"EndB"=>$GradientStartB,"Alpha"=>$GradientAlpha); - $GradientColor2 = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha); - $YSpan = floor($YSize / 3); - - $this->drawGradientArea($X1+$XSpaceLeft,$Y+$YOffset,$X2-$XSpaceRight,$Y+$YOffset+$YSpan,DIRECTION_VERTICAL,$GradientColor1); - $this->drawGradientArea($X1+$XSpaceLeft,$Y+$YOffset+$YSpan,$X2-$XSpaceRight,$Y+$YOffset+$YSize,DIRECTION_VERTICAL,$GradientColor2); - } - $this->Shadow = $RestoreShadow; - } - } - - if ( $DisplayValues ) - { - $BarWidth = abs($X2-$X1)-$FontFactor; - $BarHeight = $YSize+($YOffset/2)-$FontFactor/2; - $Caption = $this->scaleFormat(round($Serie["Data"][$Key],$DisplayRound),$Mode,$Format,$Unit); - $TxtPos = $this->getTextBox(0,0,$DisplayFont,$DisplaySize,0,$Caption); - $TxtHeight = abs($TxtPos[2]["Y"] - $TxtPos[0]["Y"]); - $TxtWidth = abs($TxtPos[1]["X"] - $TxtPos[0]["X"]); - - $XCenter = ( $X2 - $X1 ) / 2 + $X1; - $YCenter = ( ($Y+$YOffset+$YSize) - ($Y+$YOffset) ) / 2 + $Y+$YOffset; - - $Done = FALSE; - if ( $DisplayOrientation == ORIENTATION_HORIZONTAL || $DisplayOrientation == ORIENTATION_AUTO ) - { - if ( $TxtHeight < $BarHeight && $TxtWidth < $BarWidth ) - { - $this->drawText($XCenter,$YCenter,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontSize"=>$DisplaySize,"FontName"=>$DisplayFont)); - $Done = TRUE; - } - } - - if ( $DisplayOrientation == ORIENTATION_VERTICAL || ( $DisplayOrientation == ORIENTATION_AUTO && !$Done) ) - { - if ( $TxtHeight < $BarWidth && $TxtWidth < $BarHeight ) - $this->drawText($XCenter,$YCenter,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Angle"=>90,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontSize"=>$DisplaySize,"FontName"=>$DisplayFont)); - } - } - - $LastX[$Key][$Pos] = $X2; - } - - $Y = $Y + $YStep; - } - } - } - } - } - - /* Draw a stacked area chart */ - function drawStackedAreaChart($Format=NULL) - { - $DrawLine = isset($Format["DrawLine"]) ? $Format["DrawLine"] : FALSE; - $LineSurrounding = isset($Format["LineSurrounding"]) ? $Format["LineSurrounding"] : NULL; - $LineR = isset($Format["LineR"]) ? $Format["LineR"] : VOID; - $LineG = isset($Format["LineG"]) ? $Format["LineG"] : VOID; - $LineB = isset($Format["LineB"]) ? $Format["LineB"] : VOID; - $LineAlpha = isset($Format["LineAlpha"]) ? $Format["LineAlpha"] : 100; - $DrawPlot = isset($Format["DrawPlot"]) ? $Format["DrawPlot"] : FALSE; - $PlotRadius = isset($Format["PlotRadius"]) ? $Format["PlotRadius"] : 2; - $PlotBorder = isset($Format["PlotBorder"]) ? $Format["PlotBorder"] : 1; - $PlotBorderSurrounding = isset($Format["PlotBorderSurrounding"]) ? $Format["PlotBorderSurrounding"] : NULL; - $PlotBorderR = isset($Format["PlotBorderR"]) ? $Format["PlotBorderR"] : 0; - $PlotBorderG = isset($Format["PlotBorderG"]) ? $Format["PlotBorderG"] : 0; - $PlotBorderB = isset($Format["PlotBorderB"]) ? $Format["PlotBorderB"] : 0; - $PlotBorderAlpha = isset($Format["PlotBorderAlpha"]) ? $Format["PlotBorderAlpha"] : 50; - $ForceTransparency = isset($Format["ForceTransparency"]) ? $Format["ForceTransparency"] : NULL; - - $this->LastChartLayout = CHART_LAST_LAYOUT_STACKED; - - $Data = $this->DataSet->getData(); - list($XMargin,$XDivs) = $this->scaleGetXSettings(); - - $RestoreShadow = $this->Shadow; - $this->Shadow = FALSE; - - /* Build the offset data series */ - $OffsetData = ""; - $OverallOffset = ""; - $SerieOrder = ""; - foreach($Data["Series"] as $SerieName => $Serie) - { - if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) - { - $SerieOrder[] = $SerieName; - - foreach($Serie["Data"] as $Key => $Value) - { - if ( $Value == VOID ) { $Value = 0; } - if ($Value >= 0) { $Sign = "+"; } else { $Sign = "-"; } - if ( !isset($OverallOffset[$Key]) || !isset($OverallOffset[$Key][$Sign]) ) { $OverallOffset[$Key][$Sign] = 0; } - - if ( $Sign == "+" ) - { $Data["Series"][$SerieName]["Data"][$Key] = $Value + $OverallOffset[$Key][$Sign]; } - else - { $Data["Series"][$SerieName]["Data"][$Key] = $Value - $OverallOffset[$Key][$Sign]; } - - $OverallOffset[$Key][$Sign] = $OverallOffset[$Key][$Sign] + abs($Value); - } - } - } - $SerieOrder = array_reverse($SerieOrder); - - $LastX = ""; $LastY = ""; - foreach($SerieOrder as $Key => $SerieName) - { - $Serie = $Data["Series"][$SerieName]; - if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) - { - $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; - if ( $ForceTransparency != NULL ) { $Alpha = $ForceTransparency; } - - $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); - - if ( $LineSurrounding != NULL ) - $LineColor = array("R"=>$R+$LineSurrounding,"G"=>$G+$LineSurrounding,"B"=>$B+$LineSurrounding,"Alpha"=>$Alpha); - elseif ( $LineR != VOID ) - $LineColor = array("R"=>$LineR,"G"=>$LineG,"B"=>$LineB,"Alpha"=>$LineAlpha); - else - $LineColor = $Color; - - if ( $PlotBorderSurrounding != NULL ) - $PlotBorderColor = array("R"=>$R+$PlotBorderSurrounding,"G"=>$G+$PlotBorderSurrounding,"B"=>$B+$PlotBorderSurrounding,"Alpha"=>$PlotBorderAlpha); - else - $PlotBorderColor = array("R"=>$PlotBorderR,"G"=>$PlotBorderG,"B"=>$PlotBorderB,"Alpha"=>$PlotBorderAlpha); - - $AxisID = $Serie["Axis"]; - $Mode = $Data["Axis"][$AxisID]["Display"]; - $Format = $Data["Axis"][$AxisID]["Format"]; - $Unit = $Data["Axis"][$AxisID]["Unit"]; - - $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"]),TRUE); - $YZero = $this->scaleComputeY(0,array("AxisID"=>$Serie["Axis"])); - - $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - if ( $YZero < $this->GraphAreaY1+1 ) { $YZero = $this->GraphAreaY1+1; } - if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; } - - if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } - $X = $this->GraphAreaX1 + $XMargin; - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - - $Plots = ""; $Plots[] = $X; $Plots[] = $YZero; - foreach($PosArray as $Key => $Height) - { - if ( $Height != VOID ) { $Plots[] = $X; $Plots[] = $YZero-$Height; } - $X = $X + $XStep; - } - $Plots[] = $X-$XStep; $Plots[] = $YZero; - - $this->drawPolygon($Plots,$Color); - - $this->Shadow = $RestoreShadow; - if ( $DrawLine ) { for($i=2; $i<=count($Plots)-6; $i=$i+2) { $this->drawLine($Plots[$i],$Plots[$i+1],$Plots[$i+2],$Plots[$i+3],$LineColor); } } - if ( $DrawPlot ) - { - for($i=2; $i<=count($Plots)-4; $i=$i+2) - { - if ( $PlotBorder != 0 ) - { $this->drawFilledCircle($Plots[$i],$Plots[$i+1],$PlotRadius+$PlotBorder,$PlotBorderColor); } - - $this->drawFilledCircle($Plots[$i],$Plots[$i+1],$PlotRadius,$Color); - } - } - $this->Shadow = FALSE; - } - elseif ( $Data["Orientation"] == SCALE_POS_TOPBOTTOM ) - { - if ( $YZero < $this->GraphAreaX1+1 ) { $YZero = $this->GraphAreaX1+1; } - if ( $YZero > $this->GraphAreaX2-1 ) { $YZero = $this->GraphAreaX2-1; } - - if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } - $Y = $this->GraphAreaY1 + $XMargin; - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - - $Plots = ""; $Plots[] = $YZero; $Plots[] = $Y; - foreach($PosArray as $Key => $Height) - { - if ( $Height != VOID ) { $Plots[] = $YZero+$Height; $Plots[] = $Y; } - $Y = $Y + $YStep; - } - $Plots[] = $YZero; $Plots[] = $Y-$YStep; - - $this->drawPolygon($Plots,$Color); - - $this->Shadow = $RestoreShadow; - if ( $DrawLine ) { for($i=2; $i<=count($Plots)-6; $i=$i+2) { $this->drawLine($Plots[$i],$Plots[$i+1],$Plots[$i+2],$Plots[$i+3],$LineColor); } } - if ( $DrawPlot ) - { - for($i=2; $i<=count($Plots)-4; $i=$i+2) - { - if ( $PlotBorder != 0 ) - { $this->drawFilledCircle($Plots[$i],$Plots[$i+1],$PlotRadius+$PlotBorder,$PlotBorderColor); } - - $this->drawFilledCircle($Plots[$i],$Plots[$i+1],$PlotRadius,$Color); - } - } - $this->Shadow = FALSE; - } - } - } - $this->Shadow = $RestoreShadow; - } - - /* Returns a random color */ - function getRandomColor($Alpha=100) - { return(array("R"=>rand(0,255),"G"=>rand(0,255),"B"=>rand(0,255),"Alpha"=>$Alpha)); } - - /* Validate a palette */ - function validatePalette($Colors,$Surrounding=NULL) - { - $Result = ""; - - if ( !is_array($Colors) ) { return($this->getRandomColor()); } - - foreach($Colors as $Key => $Values) - { - if ( isset($Values["R"]) ) { $Result[$Key]["R"] = $Values["R"]; } else { $Result[$Key]["R"] = rand(0,255); } - if ( isset($Values["G"]) ) { $Result[$Key]["G"] = $Values["G"]; } else { $Result[$Key]["G"] = rand(0,255); } - if ( isset($Values["B"]) ) { $Result[$Key]["B"] = $Values["B"]; } else { $Result[$Key]["B"] = rand(0,255); } - if ( isset($Values["Alpha"]) ) { $Result[$Key]["Alpha"] = $Values["Alpha"]; } else { $Result[$Key]["Alpha"] = 100; } - - if ( $Surrounding != NULL ) - { - $Result[$Key]["BorderR"] = $Result[$Key]["R"] + $Surrounding; - $Result[$Key]["BorderG"] = $Result[$Key]["G"] + $Surrounding; - $Result[$Key]["BorderB"] = $Result[$Key]["B"] + $Surrounding; - } - else - { - if ( isset($Values["BorderR"]) ) { $Result[$Key]["BorderR"] = $Values["BorderR"]; } else { $Result[$Key]["BorderR"] = $Result[$Key]["R"]; } - if ( isset($Values["BorderG"]) ) { $Result[$Key]["BorderG"] = $Values["BorderG"]; } else { $Result[$Key]["BorderG"] = $Result[$Key]["G"]; } - if ( isset($Values["BorderB"]) ) { $Result[$Key]["BorderB"] = $Values["BorderB"]; } else { $Result[$Key]["BorderB"] = $Result[$Key]["B"]; } - if ( isset($Values["BorderAlpha"]) ) { $Result[$Key]["BorderAlpha"] = $Values["BorderAlpha"]; } else { $Result[$Key]["BorderAlpha"] = $Result[$Key]["Alpha"]; } - } - } - - return($Result); - } - - /* Draw the derivative chart associated to the data series */ - function drawDerivative($Format=NULL) - { - $Offset = isset($Format["Offset"]) ? $Format["Offset"] : 10; - $SerieSpacing = isset($Format["SerieSpacing"]) ? $Format["SerieSpacing"] : 3; - $DerivativeHeight = isset($Format["DerivativeHeight"]) ? $Format["DerivativeHeight"] : 4; - $ShadedSlopeBox = isset($Format["ShadedSlopeBox"]) ? $Format["ShadedSlopeBox"] : FALSE; - $DrawBackground = isset($Format["DrawBackground"]) ? $Format["DrawBackground"] : TRUE; - $BackgroundR = isset($Format["BackgroundR"]) ? $Format["BackgroundR"] : 255; - $BackgroundG = isset($Format["BackgroundG"]) ? $Format["BackgroundG"] : 255; - $BackgroundB = isset($Format["BackgroundB"]) ? $Format["BackgroundB"] : 255; - $BackgroundAlpha = isset($Format["BackgroundAlpha"]) ? $Format["BackgroundAlpha"] : 20; - $DrawBorder = isset($Format["DrawBorder"]) ? $Format["DrawBorder"] : TRUE; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 0; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 0; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 0; - $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 100; - $Caption = isset($Format["Caption"]) ? $Format["Caption"] : TRUE; - $CaptionHeight = isset($Format["CaptionHeight"]) ? $Format["CaptionHeight"] : 10; - $CaptionWidth = isset($Format["CaptionWidth"]) ? $Format["CaptionWidth"] : 20; - $CaptionMargin = isset($Format["CaptionMargin"]) ? $Format["CaptionMargin"] : 4; - $CaptionLine = isset($Format["CaptionLine"]) ? $Format["CaptionLine"] : FALSE; - $CaptionBox = isset($Format["CaptionBox"]) ? $Format["CaptionBox"] : FALSE; - $CaptionBorderR = isset($Format["CaptionBorderR"]) ? $Format["CaptionBorderR"] : 0; - $CaptionBorderG = isset($Format["CaptionBorderG"]) ? $Format["CaptionBorderG"] : 0; - $CaptionBorderB = isset($Format["CaptionBorderB"]) ? $Format["CaptionBorderB"] : 0; - $CaptionFillR = isset($Format["CaptionFillR"]) ? $Format["CaptionFillR"] : 255; - $CaptionFillG = isset($Format["CaptionFillG"]) ? $Format["CaptionFillG"] : 255; - $CaptionFillB = isset($Format["CaptionFillB"]) ? $Format["CaptionFillB"] : 255; - $CaptionFillAlpha = isset($Format["CaptionFillAlpha"]) ? $Format["CaptionFillAlpha"] : 80; - $PositiveSlopeStartR = isset($Format["PositiveSlopeStartR"]) ? $Format["PositiveSlopeStartR"] : 184; - $PositiveSlopeStartG = isset($Format["PositiveSlopeStartG"]) ? $Format["PositiveSlopeStartG"] : 234; - $PositiveSlopeStartB = isset($Format["PositiveSlopeStartB"]) ? $Format["PositiveSlopeStartB"] : 88; - $PositiveSlopeEndR = isset($Format["PositiveSlopeStartR"]) ? $Format["PositiveSlopeStartR"] : 239; - $PositiveSlopeEndG = isset($Format["PositiveSlopeStartG"]) ? $Format["PositiveSlopeStartG"] : 31; - $PositiveSlopeEndB = isset($Format["PositiveSlopeStartB"]) ? $Format["PositiveSlopeStartB"] : 36; - $NegativeSlopeStartR = isset($Format["NegativeSlopeStartR"]) ? $Format["NegativeSlopeStartR"] : 184; - $NegativeSlopeStartG = isset($Format["NegativeSlopeStartG"]) ? $Format["NegativeSlopeStartG"] : 234; - $NegativeSlopeStartB = isset($Format["NegativeSlopeStartB"]) ? $Format["NegativeSlopeStartB"] : 88; - $NegativeSlopeEndR = isset($Format["NegativeSlopeStartR"]) ? $Format["NegativeSlopeStartR"] : 67; - $NegativeSlopeEndG = isset($Format["NegativeSlopeStartG"]) ? $Format["NegativeSlopeStartG"] : 124; - $NegativeSlopeEndB = isset($Format["NegativeSlopeStartB"]) ? $Format["NegativeSlopeStartB"] : 227; - - $Data = $this->DataSet->getData(); - - list($XMargin,$XDivs) = $this->scaleGetXSettings(); - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - $YPos = $this->DataSet->Data["GraphArea"]["Y2"] + $Offset; - else - $XPos = $this->DataSet->Data["GraphArea"]["X2"] + $Offset; - - foreach($Data["Series"] as $SerieName => $Serie) - { - if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) - { - $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; $Weight = $Serie["Weight"]; - - $AxisID = $Serie["Axis"]; - $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - if ( $Caption ) - { - if ( $CaptionLine ) - { - $StartX = floor($this->GraphAreaX1-$CaptionWidth+$XMargin-$CaptionMargin); - $EndX = floor($this->GraphAreaX1-$CaptionMargin+$XMargin); - - $CaptionSettings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight); - if ( $CaptionBox ) { $this->drawFilledRectangle($StartX,$YPos,$EndX,$YPos+$CaptionHeight,array("R"=>$CaptionFillR,"G"=>$CaptionFillG,"B"=>$CaptionFillB,"BorderR"=>$CaptionBorderR,"BorderG"=>$CaptionBorderG,"BorderB"=>$CaptionBorderB,"Alpha"=>$CaptionFillAlpha)); } - $this->drawLine($StartX+2,$YPos+($CaptionHeight/2),$EndX-2,$YPos+($CaptionHeight/2),$CaptionSettings); - } - else - $this->drawFilledRectangle($this->GraphAreaX1-$CaptionWidth+$XMargin-$CaptionMargin,$YPos,$this->GraphAreaX1-$CaptionMargin+$XMargin,$YPos+$CaptionHeight,array("R"=>$R,"G"=>$G,"B"=>$B,"BorderR"=>$CaptionBorderR,"BorderG"=>$CaptionBorderG,"BorderB"=>$CaptionBorderB)); - } - - if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } - $X = $this->GraphAreaX1 + $XMargin; - - $TopY = $YPos + ($CaptionHeight/2) - ($DerivativeHeight/2); - $BottomY = $YPos + ($CaptionHeight/2) + ($DerivativeHeight/2); - - $StartX = floor($this->GraphAreaX1+$XMargin); - $EndX = floor($this->GraphAreaX2-$XMargin); - - if ( $DrawBackground ) { $this->drawFilledRectangle($StartX-1,$TopY-1,$EndX+1,$BottomY+1,array("R"=>$BackgroundR,"G"=>$BackgroundG,"B"=>$BackgroundB,"Alpha"=>$BackgroundAlpha)); } - if ( $DrawBorder ) { $this->drawRectangle($StartX-1,$TopY-1,$EndX+1,$BottomY+1,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha)); } - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - - $RestoreShadow = $this->Shadow; - $this->Shadow = FALSE; - - /* Determine the Max slope index */ - $LastX = NULL; $LastY = NULL; $MinSlope = 0; $MaxSlope = 1; - foreach($PosArray as $Key => $Y) - { - if ( $Y != VOID && $LastX != NULL ) - { $Slope = ($LastY - $Y); if ( $Slope > $MaxSlope ) { $MaxSlope = $Slope; } if ( $Slope < $MinSlope ) { $MinSlope = $Slope; } } - - if ( $Y == VOID ) - { $LastX = NULL; $LastY = NULL; } - else - { $LastX = $X; $LastY = $Y; } - } - - $LastX = NULL; $LastY = NULL; $LastColor = NULL; - foreach($PosArray as $Key => $Y) - { - if ( $Y != VOID && $LastY != NULL ) - { - $Slope = ($LastY - $Y); - - if ( $Slope >= 0 ) - { - $SlopeIndex = (100 / $MaxSlope) * $Slope; - $R = (($PositiveSlopeEndR - $PositiveSlopeStartR)/100)*$SlopeIndex+$PositiveSlopeStartR; - $G = (($PositiveSlopeEndG - $PositiveSlopeStartG)/100)*$SlopeIndex+$PositiveSlopeStartG; - $B = (($PositiveSlopeEndB - $PositiveSlopeStartB)/100)*$SlopeIndex+$PositiveSlopeStartB; - } - elseif ( $Slope < 0 ) - { - $SlopeIndex = (100 / abs($MinSlope)) * abs($Slope); - $R = (($NegativeSlopeEndR - $NegativeSlopeStartR)/100)*$SlopeIndex+$NegativeSlopeStartR; - $G = (($NegativeSlopeEndG - $NegativeSlopeStartG)/100)*$SlopeIndex+$NegativeSlopeStartG; - $B = (($NegativeSlopeEndB - $NegativeSlopeStartB)/100)*$SlopeIndex+$NegativeSlopeStartB; - } - - $Color = array("R"=>$R,"G"=>$G,"B"=>$B); - - if ( $ShadedSlopeBox && $LastColor != NULL ) // && $Slope != 0 - { - $GradientSettings = array("StartR"=>$LastColor["R"],"StartG"=>$LastColor["G"],"StartB"=>$LastColor["B"],"EndR"=>$R,"EndG"=>$G,"EndB"=>$B); - $this->drawGradientArea($LastX,$TopY,$X,$BottomY,DIRECTION_HORIZONTAL,$GradientSettings); - } - elseif ( !$ShadedSlopeBox || $LastColor == NULL ) // || $Slope == 0 - $this->drawFilledRectangle(floor($LastX),$TopY,floor($X),$BottomY,$Color); - - $LastColor = $Color; - } - - if ( $Y == VOID ) - { $LastY = NULL; } - else - { $LastX = $X; $LastY = $Y; } - - $X = $X + $XStep; - } - - $YPos = $YPos + $CaptionHeight + $SerieSpacing; - } - else - { - if ( $Caption ) - { - $StartY = floor($this->GraphAreaY1-$CaptionWidth+$XMargin-$CaptionMargin); - $EndY = floor($this->GraphAreaY1-$CaptionMargin+$XMargin); - if ( $CaptionLine ) - { - $CaptionSettings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight); - if ( $CaptionBox ) { $this->drawFilledRectangle($XPos,$StartY,$XPos+$CaptionHeight,$EndY,array("R"=>$CaptionFillR,"G"=>$CaptionFillG,"B"=>$CaptionFillB,"BorderR"=>$CaptionBorderR,"BorderG"=>$CaptionBorderG,"BorderB"=>$CaptionBorderB,"Alpha"=>$CaptionFillAlpha)); } - $this->drawLine($XPos+($CaptionHeight/2),$StartY+2,$XPos+($CaptionHeight/2),$EndY-2,$CaptionSettings); - } - else - $this->drawFilledRectangle($XPos,$StartY,$XPos+$CaptionHeight,$EndY,array("R"=>$R,"G"=>$G,"B"=>$B,"BorderR"=>$CaptionBorderR,"BorderG"=>$CaptionBorderG,"BorderB"=>$CaptionBorderB)); - } - - - if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $XStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } - $Y = $this->GraphAreaY1 + $XMargin; - - $TopX = $XPos + ($CaptionHeight/2) - ($DerivativeHeight/2); - $BottomX = $XPos + ($CaptionHeight/2) + ($DerivativeHeight/2); - - $StartY = floor($this->GraphAreaY1+$XMargin); - $EndY = floor($this->GraphAreaY2-$XMargin); - - if ( $DrawBackground ) { $this->drawFilledRectangle($TopX-1,$StartY-1,$BottomX+1,$EndY+1,array("R"=>$BackgroundR,"G"=>$BackgroundG,"B"=>$BackgroundB,"Alpha"=>$BackgroundAlpha)); } - if ( $DrawBorder ) { $this->drawRectangle($TopX-1,$StartY-1,$BottomX+1,$EndY+1,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha)); } - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - - $RestoreShadow = $this->Shadow; - $this->Shadow = FALSE; - - /* Determine the Max slope index */ - $LastX = NULL; $LastY = NULL; $MinSlope = 0; $MaxSlope = 1; - foreach($PosArray as $Key => $X) - { - if ( $X != VOID && $LastX != NULL ) - { $Slope = ($X - $LastX); if ( $Slope > $MaxSlope ) { $MaxSlope = $Slope; } if ( $Slope < $MinSlope ) { $MinSlope = $Slope; } } - - if ( $X == VOID ) - { $LastX = NULL; } - else - { $LastX = $X; } - } - - $LastX = NULL; $LastY = NULL; $LastColor = NULL; - foreach($PosArray as $Key => $X) - { - if ( $X != VOID && $LastX != NULL ) - { - $Slope = ($X - $LastX); - - if ( $Slope >= 0 ) - { - $SlopeIndex = (100 / $MaxSlope) * $Slope; - $R = (($PositiveSlopeEndR - $PositiveSlopeStartR)/100)*$SlopeIndex+$PositiveSlopeStartR; - $G = (($PositiveSlopeEndG - $PositiveSlopeStartG)/100)*$SlopeIndex+$PositiveSlopeStartG; - $B = (($PositiveSlopeEndB - $PositiveSlopeStartB)/100)*$SlopeIndex+$PositiveSlopeStartB; - } - elseif ( $Slope < 0 ) - { - $SlopeIndex = (100 / abs($MinSlope)) * abs($Slope); - $R = (($NegativeSlopeEndR - $NegativeSlopeStartR)/100)*$SlopeIndex+$NegativeSlopeStartR; - $G = (($NegativeSlopeEndG - $NegativeSlopeStartG)/100)*$SlopeIndex+$NegativeSlopeStartG; - $B = (($NegativeSlopeEndB - $NegativeSlopeStartB)/100)*$SlopeIndex+$NegativeSlopeStartB; - } - - $Color = array("R"=>$R,"G"=>$G,"B"=>$B); - - if ( $ShadedSlopeBox && $LastColor != NULL ) - { - $GradientSettings = array("StartR"=>$LastColor["R"],"StartG"=>$LastColor["G"],"StartB"=>$LastColor["B"],"EndR"=>$R,"EndG"=>$G,"EndB"=>$B); - - $this->drawGradientArea($TopX,$LastY,$BottomX,$Y,DIRECTION_VERTICAL,$GradientSettings); - } - elseif ( !$ShadedSlopeBox || $LastColor == NULL ) - $this->drawFilledRectangle($TopX,floor($LastY),$BottomX,floor($Y),$Color); - - $LastColor = $Color; - } - - if ( $X == VOID ) - { $LastX = NULL; } - else - { $LastX = $X; $LastY = $Y; } - - $Y = $Y + $XStep; - } - - $XPos = $XPos + $CaptionHeight + $SerieSpacing; - } - - $this->Shadow = $RestoreShadow; - } - } - } - - /* Draw the line of best fit */ - function drawBestFit($Format="") - { - $OverrideTicks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL; - $OverrideR = isset($Format["R"]) ? $Format["R"] : VOID; - $OverrideG = isset($Format["G"]) ? $Format["G"] : VOID; - $OverrideB = isset($Format["B"]) ? $Format["B"] : VOID; - $OverrideAlpha = isset($Format["Alpha"]) ? $Format["Alpha"] : VOID; - - $Data = $this->DataSet->getData(); - list($XMargin,$XDivs) = $this->scaleGetXSettings(); - - foreach($Data["Series"] as $SerieName => $Serie) - { - if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) - { - if ( $OverrideR != VOID && $OverrideG != VOID && $OverrideB != VOID ) { $R = $OverrideR; $G = $OverrideG; $B = $OverrideB; } else { $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; } - if ( $OverrideTicks == NULL ) { $Ticks = $Serie["Ticks"]; } else { $Ticks = $OverrideTicks; } - if ( $OverrideAlpha == VOID ) { $Alpha = $Serie["Color"]["Alpha"]; } else { $Alpha = $OverrideAlpha; } - - $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks); - - $AxisID = $Serie["Axis"]; - $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } - $X = $this->GraphAreaX1 + $XMargin; - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - $Sxy = 0; $Sx = 0; $Sy = 0; $Sxx = 0; - foreach($PosArray as $Key => $Y) - { - if ( $Y != VOID ) - { - $Sxy = $Sxy + $X*$Y; - $Sx = $Sx + $X; - $Sy = $Sy + $Y; - $Sxx = $Sxx + $X*$X; - } - - $X = $X + $XStep; - } - $n = count($this->DataSet->stripVOID($PosArray)); //$n = count($PosArray); - $M = (($n*$Sxy)-($Sx*$Sy)) / (($n*$Sxx)-($Sx*$Sx)); - $B = (($Sy)-($M*$Sx))/($n); - - $X1 = $this->GraphAreaX1 + $XMargin; - $Y1 = $M * $X1 + $B; - $X2 = $this->GraphAreaX2 - $XMargin; - $Y2 = $M * $X2 + $B; - - if ( $Y1 < $this->GraphAreaY1 ) { $X1 = $X1 + ($this->GraphAreaY1-$Y1); $Y1 = $this->GraphAreaY1; } - if ( $Y1 > $this->GraphAreaY2 ) { $X1 = $X1 + ($Y1-$this->GraphAreaY2); $Y1 = $this->GraphAreaY2; } - if ( $Y2 < $this->GraphAreaY1 ) { $X2 = $X2 - ($this->GraphAreaY1-$Y2); $Y2 = $this->GraphAreaY1; } - if ( $Y2 > $this->GraphAreaY2 ) { $X2 = $X2 - ($Y2-$this->GraphAreaY2); $Y2 = $this->GraphAreaY2; } - - $this->drawLine($X1,$Y1,$X2,$Y2,$Color); - } - else - { - if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } - $Y = $this->GraphAreaY1 + $XMargin; - - if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } - $Sxy = 0; $Sx = 0; $Sy = 0; $Sxx = 0; - foreach($PosArray as $Key => $X) - { - if ( $X != VOID ) - { - $Sxy = $Sxy + $X*$Y; - $Sx = $Sx + $Y; - $Sy = $Sy + $X; - $Sxx = $Sxx + $Y*$Y; - } - - $Y = $Y + $YStep; - } - $n = count($this->DataSet->stripVOID($PosArray)); //$n = count($PosArray); - $M = (($n*$Sxy)-($Sx*$Sy)) / (($n*$Sxx)-($Sx*$Sx)); - $B = (($Sy)-($M*$Sx))/($n); - - $Y1 = $this->GraphAreaY1 + $XMargin; - $X1 = $M * $Y1 + $B; - $Y2 = $this->GraphAreaY2 - $XMargin; - $X2 = $M * $Y2 + $B; - - if ( $X1 < $this->GraphAreaX1 ) { $Y1 = $Y1 + ($this->GraphAreaX1-$X1); $X1 = $this->GraphAreaX1; } - if ( $X1 > $this->GraphAreaX2 ) { $Y1 = $Y1 + ($X1-$this->GraphAreaX2); $X1 = $this->GraphAreaX2; } - if ( $X2 < $this->GraphAreaX1 ) { $Y2 = $Y2 - ($this->GraphAreaY1-$X2); $X2 = $this->GraphAreaX1; } - if ( $X2 > $this->GraphAreaX2 ) { $Y2 = $Y2 - ($X2-$this->GraphAreaX2); $X2 = $this->GraphAreaX2; } - - $this->drawLine($X1,$Y1,$X2,$Y2,$Color); - } - } - } - } - - /* Write labels */ - function writeLabel($SeriesName,$Indexes,$Format="") - { - $OverrideTitle = isset($Format["OverrideTitle"]) ? $Format["OverrideTitle"] : NULL; - $ForceLabels = isset($Format["ForceLabels"]) ? $Format["ForceLabels"] : NULL; - $DrawPoint = isset($Format["DrawPoint"]) ? $Format["DrawPoint"] : LABEL_POINT_BOX; - $DrawVerticalLine = isset($Format["DrawVerticalLine"]) ? $Format["DrawVerticalLine"] : FALSE; - $VerticalLineR = isset($Format["VerticalLineR"]) ? $Format["VerticalLineR"] : 0; - $VerticalLineG = isset($Format["VerticalLineG"]) ? $Format["VerticalLineG"] : 0; - $VerticalLineB = isset($Format["VerticalLineB"]) ? $Format["VerticalLineB"] : 0; - $VerticalLineAlpha = isset($Format["VerticalLineAlpha"]) ? $Format["VerticalLineAlpha"] : 40; - $VerticalLineTicks = isset($Format["VerticalLineTicks"]) ? $Format["VerticalLineTicks"] : 2; - - $Data = $this->DataSet->getData(); - list($XMargin,$XDivs) = $this->scaleGetXSettings(); - - if ( !is_array($Indexes) ) { $Index = $Indexes; $Indexes = ""; $Indexes[] = $Index; } - if ( !is_array($SeriesName) ) { $SerieName = $SeriesName; $SeriesName = ""; $SeriesName[] = $SerieName; } - if ( $ForceLabels != NULL && !is_array($ForceLabels) ) { $ForceLabel = $ForceLabels; $ForceLabels = ""; $ForceLabels[] = $ForceLabel; } - - foreach ($Indexes as $Key => $Index) - { - $Series = ""; - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } - $X = $this->GraphAreaX1 + $XMargin + $Index * $XStep; - - if ( $DrawVerticalLine ) { $this->drawLine($X,$this->GraphAreaY1+$Data["YMargin"],$X,$this->GraphAreaY2-$Data["YMargin"],array("R"=>$VerticalLineR,"G"=>$VerticalLineG,"B"=>$VerticalLineB,"Alpha"=>$VerticalLineAlpha,"Ticks"=>$VerticalLineTicks)); } - - $MinY = $this->GraphAreaY2; - foreach ($SeriesName as $iKey => $SerieName) - { - if ( isset($Data["Series"][$SerieName]["Data"][$Index]) ) - { - $AxisID = $Data["Series"][$SerieName]["Axis"]; - - if ( $OverrideTitle != NULL) - $Description = $OverrideTitle; - elseif ( count($SeriesName) == 1 ) - { - if ( isset($Data["Abscissa"]) && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) ) - $Description = $Data["Series"][$SerieName]["Description"]." - ".$Data["Series"][$Data["Abscissa"]]["Data"][$Index]; - else - $Description = $Data["Series"][$SerieName]["Description"]; - } - elseif ( isset($Data["Abscissa"]) && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) ) - $Description = $Data["Series"][$Data["Abscissa"]]["Data"][$Index]; - - $AxisMode = $Data["Axis"][$AxisID]["Display"]; - $AxisFormat = $Data["Axis"][$AxisID]["Format"]; - $AxisUnit = $Data["Axis"][$AxisID]["Unit"]; - - $Serie = ""; - $Serie["R"] = $Data["Series"][$SerieName]["Color"]["R"]; - $Serie["G"] = $Data["Series"][$SerieName]["Color"]["G"]; - $Serie["B"] = $Data["Series"][$SerieName]["Color"]["B"]; - $Serie["Alpha"] = $Data["Series"][$SerieName]["Color"]["Alpha"]; - - if ( count($SeriesName) == 1 && isset($Data["Series"][$SerieName]["XOffset"]) ) - $SerieOffset = $Data["Series"][$SerieName]["XOffset"]; - else - $SerieOffset = 0; - - $Value = $Data["Series"][$SerieName]["Data"][$Index]; - if ( $Value == VOID ) { $Value = "NaN"; } - - if ( $ForceLabels != NULL ) - $Caption = isset($ForceLabels[$Key]) ? $ForceLabels[$Key] : "Not set"; - else - $Caption = $this->scaleFormat($Value,$AxisMode,$AxisFormat,$AxisUnit); - - if ( $this->LastChartLayout == CHART_LAST_LAYOUT_STACKED ) - { - if ( $Value >=0 ) { $LookFor = "+"; } else { $LookFor = "-"; } - - $Value = 0; $Done = FALSE; - foreach($Data["Series"] as $Name => $SerieLookup) - { - if ( $SerieLookup["isDrawable"] == TRUE && $Name != $Data["Abscissa"] && !$Done ) - { - if ( isset($Data["Series"][$Name]["Data"][$Index]) && $Data["Series"][$Name]["Data"][$Index] != VOID ) - { - if ($Data["Series"][$Name]["Data"][$Index] >= 0 && $LookFor == "+" ) { $Value = $Value + $Data["Series"][$Name]["Data"][$Index]; } - if ($Data["Series"][$Name]["Data"][$Index] < 0 && $LookFor == "-" ) { $Value = $Value - $Data["Series"][$Name]["Data"][$Index]; } - if ($Name == $SerieName ) { $Done = TRUE; } - } - } - } - } - - $X = floor($this->GraphAreaX1 + $XMargin + $Index * $XStep + $SerieOffset); - $Y = floor($this->scaleComputeY($Value,array("AxisID"=>$AxisID))); - - if ($Y < $MinY) { $MinY = $Y; } - - if ( $DrawPoint == LABEL_POINT_CIRCLE ) - $this->drawFilledCircle($X,$Y,3,array("R"=>255,"G"=>255,"B"=>255,"BorderR"=>0,"BorderG"=>0,"BorderB"=>0)); - elseif ( $DrawPoint == LABEL_POINT_BOX ) - $this->drawFilledRectangle($X-2,$Y-2,$X+2,$Y+2,array("R"=>255,"G"=>255,"B"=>255,"BorderR"=>0,"BorderG"=>0,"BorderB"=>0)); - - $Series[] = array("Format"=>$Serie,"Caption"=>$Caption); - } - } - $this->drawLabelBox($X,$MinY-3,$Description,$Series,$Format); - - } - else - { - if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $XStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } - $Y = $this->GraphAreaY1 + $XMargin + $Index * $XStep; - - if ( $DrawVerticalLine ) { $this->drawLine($this->GraphAreaX1+$Data["YMargin"],$Y,$this->GraphAreaX2-$Data["YMargin"],$Y,array("R"=>$VerticalLineR,"G"=>$VerticalLineG,"B"=>$VerticalLineB,"Alpha"=>$VerticalLineAlpha,"Ticks"=>$VerticalLineTicks)); } - - $MinX = $this->GraphAreaX2; - foreach ($SeriesName as $Key => $SerieName) - { - if ( isset($Data["Series"][$SerieName]["Data"][$Index]) ) - { - $AxisID = $Data["Series"][$SerieName]["Axis"]; - - if ( $OverrideTitle != NULL) - $Description = $OverrideTitle; - elseif ( count($SeriesName) == 1 ) - { - if ( isset($Data["Abscissa"]) && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) ) - $Description = $Data["Series"][$SerieName]["Description"]." - ".$Data["Series"][$Data["Abscissa"]]["Data"][$Index]; - else - $Description = $Data["Series"][$SerieName]["Description"]; - } - elseif ( isset($Data["Abscissa"]) && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) ) - $Description = $Data["Series"][$Data["Abscissa"]]["Data"][$Index]; - - $AxisMode = $Data["Axis"][$AxisID]["Display"]; - $AxisFormat = $Data["Axis"][$AxisID]["Format"]; - $AxisUnit = $Data["Axis"][$AxisID]["Unit"]; - - $Serie = ""; - if ( isset($Data["Extended"]["Palette"][$Index] ) ) - { - $Serie["R"] = $Data["Extended"]["Palette"][$Index]["R"]; - $Serie["G"] = $Data["Extended"]["Palette"][$Index]["G"]; - $Serie["B"] = $Data["Extended"]["Palette"][$Index]["B"]; - $Serie["Alpha"] = $Data["Extended"]["Palette"][$Index]["Alpha"]; - } - else - { - $Serie["R"] = $Data["Series"][$SerieName]["Color"]["R"]; - $Serie["G"] = $Data["Series"][$SerieName]["Color"]["G"]; - $Serie["B"] = $Data["Series"][$SerieName]["Color"]["B"]; - $Serie["Alpha"] = $Data["Series"][$SerieName]["Color"]["Alpha"]; - } - - if ( count($SeriesName) == 1 && isset($Data["Series"][$SerieName]["XOffset"]) ) - $SerieOffset = $Data["Series"][$SerieName]["XOffset"]; - else - $SerieOffset = 0; - - $Value = $Data["Series"][$SerieName]["Data"][$Index]; - if ( $ForceLabels != NULL ) - $Caption = isset($ForceLabels[$Key]) ? $ForceLabels[$Key] : "Not set"; - else - $Caption = $this->scaleFormat($Value,$AxisMode,$AxisFormat,$AxisUnit); - if ( $Value == VOID ) { $Value = "NaN"; } - - if ( $this->LastChartLayout == CHART_LAST_LAYOUT_STACKED ) - { - if ( $Value >=0 ) { $LookFor = "+"; } else { $LookFor = "-"; } - - $Value = 0; $Done = FALSE; - foreach($Data["Series"] as $Name => $SerieLookup) - { - if ( $SerieLookup["isDrawable"] == TRUE && $Name != $Data["Abscissa"] && !$Done ) - { - if ( isset($Data["Series"][$Name]["Data"][$Index]) && $Data["Series"][$Name]["Data"][$Index] != VOID ) - { - if ($Data["Series"][$Name]["Data"][$Index] >= 0 && $LookFor == "+" ) { $Value = $Value + $Data["Series"][$Name]["Data"][$Index]; } - if ($Data["Series"][$Name]["Data"][$Index] < 0 && $LookFor == "-" ) { $Value = $Value - $Data["Series"][$Name]["Data"][$Index]; } - if ($Name == $SerieName ) { $Done = TRUE; } - } - } - } - } - - $X = floor($this->scaleComputeY($Value,array("AxisID"=>$AxisID))); - $Y = floor($this->GraphAreaY1 + $XMargin + $Index * $XStep + $SerieOffset); - - if ($X < $MinX) { $MinX = $X; } - - if ( $DrawPoint == LABEL_POINT_CIRCLE ) - $this->drawFilledCircle($X,$Y,3,array("R"=>255,"G"=>255,"B"=>255,"BorderR"=>0,"BorderG"=>0,"BorderB"=>0)); - elseif ( $DrawPoint == LABEL_POINT_BOX ) - $this->drawFilledRectangle($X-2,$Y-2,$X+2,$Y+2,array("R"=>255,"G"=>255,"B"=>255,"BorderR"=>0,"BorderG"=>0,"BorderB"=>0)); - - $Series[] = array("Format"=>$Serie,"Caption"=>$Caption); - } - } - $this->drawLabelBox($MinX,$Y-3,$Description,$Series,$Format); - - } - } - } - - /* Draw a label box */ - function drawLabelBox($X,$Y,$Title,$Captions,$Format="") - { - $NoTitle = isset($Format["NoTitle"]) ? $Format["NoTitle"] : NULL; - $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 50; - $DrawSerieColor = isset($Format["DrawSerieColor"]) ? $Format["DrawSerieColor"] : TRUE; - $SerieR = isset($Format["SerieR"]) ? $Format["SerieR"] : NULL; - $SerieG = isset($Format["SerieG"]) ? $Format["SerieG"] : NULL; - $SerieB = isset($Format["SerieB"]) ? $Format["SerieB"] : NULL; - $SerieAlpha = isset($Format["SerieAlpha"]) ? $Format["SerieAlpha"] : NULL; - $SerieBoxSize = isset($Format["SerieBoxSize"]) ? $Format["SerieBoxSize"] : 6; - $SerieBoxSpacing = isset($Format["SerieBoxSpacing"]) ? $Format["SerieBoxSpacing"] : 4; - $VerticalMargin = isset($Format["VerticalMargin"]) ? $Format["VerticalMargin"] : 10; - $HorizontalMargin = isset($Format["HorizontalMargin"]) ? $Format["HorizontalMargin"] : 8; - $R = isset($Format["R"]) ? $Format["R"] : $this->FontColorR; - $G = isset($Format["G"]) ? $Format["G"] : $this->FontColorG; - $B = isset($Format["B"]) ? $Format["B"] : $this->FontColorB; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : $this->FontColorA; - $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->FontName; - $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize; - $TitleMode = isset($Format["TitleMode"]) ? $Format["TitleMode"] : LABEL_TITLE_NOBACKGROUND; - $TitleR = isset($Format["TitleR"]) ? $Format["TitleR"] : $R; - $TitleG = isset($Format["TitleG"]) ? $Format["TitleG"] : $G; - $TitleB = isset($Format["TitleB"]) ? $Format["TitleB"] : $B; - $TitleBackgroundR = isset($Format["TitleBackgroundR"]) ? $Format["TitleBackgroundR"] : 0; - $TitleBackgroundG = isset($Format["TitleBackgroundG"]) ? $Format["TitleBackgroundG"] : 0; - $TitleBackgroundB = isset($Format["TitleBackgroundB"]) ? $Format["TitleBackgroundB"] : 0; - $GradientStartR = isset($Format["GradientStartR"]) ? $Format["GradientStartR"] : 255; - $GradientStartG = isset($Format["GradientStartG"]) ? $Format["GradientStartG"] : 255; - $GradientStartB = isset($Format["GradientStartB"]) ? $Format["GradientStartB"] : 255; - $GradientEndR = isset($Format["GradientEndR"]) ? $Format["GradientEndR"] : 220; - $GradientEndG = isset($Format["GradientEndG"]) ? $Format["GradientEndG"] : 220; - $GradientEndB = isset($Format["GradientEndB"]) ? $Format["GradientEndB"] : 220; - - if ( !$DrawSerieColor ) { $SerieBoxSize = 0; $SerieBoxSpacing = 0; } - - $TxtPos = $this->getTextBox($X,$Y,$FontName,$FontSize,0,$Title); - $TitleWidth = ($TxtPos[1]["X"] - $TxtPos[0]["X"])+$VerticalMargin*2; - $TitleHeight = ($TxtPos[0]["Y"] - $TxtPos[2]["Y"]); - - if ( $NoTitle ) { $TitleWidth = 0; $TitleHeight = 0; } - - $CaptionWidth = 0; $CaptionHeight = -$HorizontalMargin; - foreach($Captions as $Key =>$Caption) - { - $TxtPos = $this->getTextBox($X,$Y,$FontName,$FontSize,0,$Caption["Caption"]); - $CaptionWidth = max($CaptionWidth,($TxtPos[1]["X"] - $TxtPos[0]["X"])+$VerticalMargin*2); - $CaptionHeight = $CaptionHeight + max(($TxtPos[0]["Y"] - $TxtPos[2]["Y"]),($SerieBoxSize+2)) + $HorizontalMargin; - } - - if ( $CaptionHeight <= 5 ) { $CaptionHeight = $CaptionHeight + $HorizontalMargin/2; } - - if ( $DrawSerieColor ) { $CaptionWidth = $CaptionWidth + $SerieBoxSize + $SerieBoxSpacing; } - - $BoxWidth = max($BoxWidth,$TitleWidth,$CaptionWidth); - - $XMin = $X - 5 - floor(($BoxWidth-10) / 2); - $XMax = $X + 5 + floor(($BoxWidth-10) / 2); - - $RestoreShadow = $this->Shadow; - if ( $this->Shadow == TRUE ) - { - $this->Shadow = FALSE; - - $Poly = ""; - $Poly[] = $X+$this->ShadowX; $Poly[] = $Y+$this->ShadowX; - $Poly[] = $X+5+$this->ShadowX; $Poly[] = $Y-5+$this->ShadowX; - $Poly[] = $XMax+$this->ShadowX; $Poly[] = $Y-5+$this->ShadowX; - - if ( $NoTitle ) - { - $Poly[] = $XMax+$this->ShadowX; $Poly[] = $Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*2+$this->ShadowX; - $Poly[] = $XMin+$this->ShadowX; $Poly[] = $Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*2+$this->ShadowX; - } - else - { - $Poly[] = $XMax+$this->ShadowX; $Poly[] = $Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3+$this->ShadowX; - $Poly[] = $XMin+$this->ShadowX; $Poly[] = $Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3+$this->ShadowX; - } - - $Poly[] = $XMin+$this->ShadowX; $Poly[] = $Y-5+$this->ShadowX; - $Poly[] = $X-5+$this->ShadowX; $Poly[] = $Y-5+$this->ShadowX; - $this->drawPolygon($Poly,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa)); - } - - /* Draw the background */ - $GradientSettings = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB); - if ( $NoTitle ) - $this->drawGradientArea($XMin,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$XMax,$Y-6,DIRECTION_VERTICAL,$GradientSettings); - else - $this->drawGradientArea($XMin,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMax,$Y-6,DIRECTION_VERTICAL,$GradientSettings); - $Poly = ""; $Poly[] = $X; $Poly[] = $Y; $Poly[] = $X-5; $Poly[] = $Y-5; $Poly[] = $X+5; $Poly[] = $Y-5; - $this->drawPolygon($Poly,array("R"=>$GradientEndR,"G"=>$GradientEndG,"B"=>$GradientEndB,"NoBorder"=>TRUE)); - - /* Outer border */ - $OuterBorderColor = $this->allocateColor($this->Picture,100,100,100,100); - imageline($this->Picture,$XMin,$Y-5,$X-5,$Y-5,$OuterBorderColor); - imageline($this->Picture,$X,$Y,$X-5,$Y-5,$OuterBorderColor); - imageline($this->Picture,$X,$Y,$X+5,$Y-5,$OuterBorderColor); - imageline($this->Picture,$X+5,$Y-5,$XMax,$Y-5,$OuterBorderColor); - if ( $NoTitle ) - { - imageline($this->Picture,$XMin,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$XMin,$Y-5,$OuterBorderColor); - imageline($this->Picture,$XMax,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$XMax,$Y-5,$OuterBorderColor); - imageline($this->Picture,$XMin,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$XMax,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$OuterBorderColor); - } - else - { - imageline($this->Picture,$XMin,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMin,$Y-5,$OuterBorderColor); - imageline($this->Picture,$XMax,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMax,$Y-5,$OuterBorderColor); - imageline($this->Picture,$XMin,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMax,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$OuterBorderColor); - } - - /* Inner border */ - $InnerBorderColor = $this->allocateColor($this->Picture,255,255,255,100); - imageline($this->Picture,$XMin+1,$Y-6,$X-5,$Y-6,$InnerBorderColor); - imageline($this->Picture,$X,$Y-1,$X-5,$Y-6,$InnerBorderColor); - imageline($this->Picture,$X,$Y-1,$X+5,$Y-6,$InnerBorderColor); - imageline($this->Picture,$X+5,$Y-6,$XMax-1,$Y-6,$InnerBorderColor); - if ( $NoTitle ) - { - imageline($this->Picture,$XMin+1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$XMin+1,$Y-6,$InnerBorderColor); - imageline($this->Picture,$XMax-1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$XMax-1,$Y-6,$InnerBorderColor); - imageline($this->Picture,$XMin+1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$XMax-1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$InnerBorderColor); - } - else - { - imageline($this->Picture,$XMin+1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMin+1,$Y-6,$InnerBorderColor); - imageline($this->Picture,$XMax-1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMax-1,$Y-6,$InnerBorderColor); - imageline($this->Picture,$XMin+1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMax-1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$InnerBorderColor); - } - - /* Draw the separator line */ - if ( $TitleMode == LABEL_TITLE_NOBACKGROUND && !$NoTitle ) - { - $YPos = $Y-7-$CaptionHeight-$HorizontalMargin-$HorizontalMargin/2; - $XMargin = $VerticalMargin / 2; - $this->drawLine($XMin+$XMargin,$YPos+1,$XMax-$XMargin,$YPos+1,array("R"=>$GradientEndR,"G"=>$GradientEndG,"B"=>$GradientEndB)); - $this->drawLine($XMin+$XMargin,$YPos,$XMax-$XMargin,$YPos,array("R"=>$GradientStartR,"G"=>$GradientStartG,"B"=>$GradientStartB)); - } - elseif ( $TitleMode == LABEL_TITLE_BACKGROUND ) - { - $this->drawFilledRectangle($XMin,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMax,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin+$HorizontalMargin/2,array("R"=>$TitleBackgroundR,"G"=>$TitleBackgroundG,"B"=>$TitleBackgroundB)); - imageline($this->Picture,$XMin+1,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin+$HorizontalMargin/2+1,$XMax-1,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin+$HorizontalMargin/2+1,$InnerBorderColor); - } - - /* Write the description */ - if ( !$NoTitle ) - $this->drawText($XMin+$VerticalMargin,$Y-7-$CaptionHeight-$HorizontalMargin*2,$Title,array("Align"=>TEXT_ALIGN_BOTTOMLEFT,"R"=>$TitleR,"G"=>$TitleG,"B"=>$TitleB)); - - /* Write the value */ - $YPos = $Y-5-$HorizontalMargin; $XPos = $XMin+$VerticalMargin+$SerieBoxSize+$SerieBoxSpacing; - foreach($Captions as $Key => $Caption) - { - $CaptionTxt = $Caption["Caption"]; - $TxtPos = $this->getTextBox($XPos,$YPos,$FontName,$FontSize,0,$CaptionTxt); - $CaptionHeight = ($TxtPos[0]["Y"] - $TxtPos[2]["Y"]); - - /* Write the serie color if needed */ - if ( $DrawSerieColor ) - { - $BoxSettings = array("R"=>$Caption["Format"]["R"],"G"=>$Caption["Format"]["G"],"B"=>$Caption["Format"]["B"],"Alpha"=>$Caption["Format"]["Alpha"],"BorderR"=>0,"BorderG"=>0,"BorderB"=>0); - $this->drawFilledRectangle($XMin+$VerticalMargin,$YPos-$SerieBoxSize,$XMin+$VerticalMargin+$SerieBoxSize,$YPos,$BoxSettings); - } - - $this->drawText($XPos,$YPos,$CaptionTxt,array("Align"=>TEXT_ALIGN_BOTTOMLEFT)); - - $YPos = $YPos - $CaptionHeight - $HorizontalMargin; - } - - $this->Shadow = $RestoreShadow; - } - - /* Draw a basic shape */ - function drawShape($X,$Y,$Shape,$PlotSize,$PlotBorder,$BorderSize,$R,$G,$B,$Alpha,$BorderR,$BorderG,$BorderB,$BorderAlpha) - { - if ( $Shape == SERIE_SHAPE_FILLEDCIRCLE ) - { - if ( $PlotBorder ) { $this->drawFilledCircle($X,$Y,$PlotSize+$BorderSize,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha)); } - $this->drawFilledCircle($X,$Y,$PlotSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); - } - elseif ( $Shape == SERIE_SHAPE_FILLEDSQUARE ) - { - if ( $PlotBorder ) { $this->drawFilledRectangle($X-$PlotSize-$BorderSize,$Y-$PlotSize-$BorderSize,$X+$PlotSize+$BorderSize,$Y+$PlotSize+$BorderSize,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha)); } - $this->drawFilledRectangle($X-$PlotSize,$Y-$PlotSize,$X+$PlotSize,$Y+$PlotSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); - } - elseif ( $Shape == SERIE_SHAPE_FILLEDTRIANGLE ) - { - if ( $PlotBorder ) - { - $Pos = ""; $Pos[]=$X; $Pos[]=$Y-$PlotSize-$BorderSize; $Pos[]=$X-$PlotSize-$BorderSize; $Pos[]=$Y+$PlotSize+$BorderSize; $Pos[]=$X+$PlotSize+$BorderSize; $Pos[]=$Y+$PlotSize+$BorderSize; - $this->drawPolygon($Pos,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha)); - } - - $Pos = ""; $Pos[]=$X; $Pos[]=$Y-$PlotSize; $Pos[]=$X-$PlotSize; $Pos[]=$Y+$PlotSize; $Pos[]=$X+$PlotSize; $Pos[]=$Y+$PlotSize; - $this->drawPolygon($Pos,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); - } - elseif ( $Shape == SERIE_SHAPE_TRIANGLE ) - { - $this->drawLine($X,$Y-$PlotSize,$X-$PlotSize,$Y+$PlotSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); - $this->drawLine($X-$PlotSize,$Y+$PlotSize,$X+$PlotSize,$Y+$PlotSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); - $this->drawLine($X+$PlotSize,$Y+$PlotSize,$X,$Y-$PlotSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); - } - elseif ( $Shape == SERIE_SHAPE_SQUARE ) - $this->drawRectangle($X-$PlotSize,$Y-$PlotSize,$X+$PlotSize,$Y+$PlotSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); - elseif ( $Shape == SERIE_SHAPE_CIRCLE ) - $this->drawCircle($X,$Y,$PlotSize,$PlotSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); - elseif ( $Shape == SERIE_SHAPE_DIAMOND ) - { - $Pos = ""; $Pos[]=$X-$PlotSize; $Pos[]=$Y; $Pos[]=$X; $Pos[]=$Y-$PlotSize; $Pos[]=$X+$PlotSize; $Pos[]=$Y; $Pos[]=$X; $Pos[]=$Y+$PlotSize; - $this->drawPolygon($Pos,array("NoFill"=>TRUE,"BorderR"=>$R,"BorderG"=>$G,"BorderB"=>$B,"BorderAlpha"=>$Alpha)); - } - elseif ( $Shape == SERIE_SHAPE_FILLEDDIAMOND ) - { - if ( $PlotBorder ) - { - $Pos = ""; $Pos[]=$X-$PlotSize-$BorderSize; $Pos[]=$Y; $Pos[]=$X; $Pos[]=$Y-$PlotSize-$BorderSize; $Pos[]=$X+$PlotSize+$BorderSize; $Pos[]=$Y; $Pos[]=$X; $Pos[]=$Y+$PlotSize+$BorderSize; - $this->drawPolygon($Pos,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha)); - } - - $Pos = ""; $Pos[]=$X-$PlotSize; $Pos[]=$Y; $Pos[]=$X; $Pos[]=$Y-$PlotSize; $Pos[]=$X+$PlotSize; $Pos[]=$Y; $Pos[]=$X; $Pos[]=$Y+$PlotSize; - $this->drawPolygon($Pos,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); - } - } - - function drawPolygonChart($Points,$Format="") - { - $R = isset($Format["R"]) ? $Format["R"] : 0; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $NoFill = isset($Format["NoFill"]) ? $Format["NoFill"] : FALSE; - $NoBorder = isset($Format["NoBorder"]) ? $Format["NoBorder"] : FALSE; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B; - $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha / 2; - $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; - $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : NULL; - - if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; } - - $RestoreShadow = $this->Shadow; - $this->Shadow = FALSE; - - $AllIntegers = TRUE; - for($i=0;$i<=count($Points)-2;$i=$i+2) - { if ( $this->getFirstDecimal($Points[$i+1]) != 0 ) { $AllIntegers = FALSE; } } - - /* Convert polygon to segments */ - $Segments = ""; - for($i=2;$i<=count($Points)-2;$i=$i+2) - { $Segments[] = array("X1"=>$Points[$i-2],"Y1"=>$Points[$i-1],"X2"=>$Points[$i],"Y2"=>$Points[$i+1]); } - $Segments[] = array("X1"=>$Points[$i-2],"Y1"=>$Points[$i-1],"X2"=>$Points[0],"Y2"=>$Points[1]); - - /* Simplify straight lines */ - $Result = ""; $inHorizon = FALSE; $LastX = VOID; - foreach($Segments as $Key => $Pos) - { - if ( $Pos["Y1"] != $Pos["Y2"] ) - { - if ( $inHorizon ) { $inHorizon = FALSE; $Result[] = array("X1"=>$LastX,"Y1"=>$Pos["Y1"],"X2"=>$Pos["X1"],"Y2"=>$Pos["Y1"]); } - - $Result[] = array("X1"=>$Pos["X1"],"Y1"=>$Pos["Y1"],"X2"=>$Pos["X2"],"Y2"=>$Pos["Y2"]); - } - else { if ( !$inHorizon ) { $inHorizon = TRUE; $LastX = $Pos["X1"];} } - } - $Segments = $Result; - - /* Do we have something to draw */ - if ( $Segments == "" ) { return(0); } - - /* For segments debugging purpose */ - //foreach($Segments as $Key => $Pos) - // echo $Pos["X1"].",".$Pos["Y1"].",".$Pos["X2"].",".$Pos["Y2"]."\r\n"; - - /* Find out the min & max Y boundaries */ - $MinY = OUT_OF_SIGHT; $MaxY = OUT_OF_SIGHT; - foreach($Segments as $Key => $Coords) - { - if ( $MinY == OUT_OF_SIGHT || $MinY > min($Coords["Y1"],$Coords["Y2"]) ) { $MinY = min($Coords["Y1"],$Coords["Y2"]); } - if ( $MaxY == OUT_OF_SIGHT || $MaxY < max($Coords["Y1"],$Coords["Y2"]) ) { $MaxY = max($Coords["Y1"],$Coords["Y2"]); } - } - - if ( $AllIntegers ) { $YStep = 1; } else { $YStep = .5; } - - $MinY = floor($MinY); $MaxY = floor($MaxY); - - /* Scan each Y lines */ - $DefaultColor = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); - $DebugLine = 0; $DebugColor = $this->allocateColor($this->Picture,255,0,0,100); - - $MinY = floor($MinY); $MaxY = floor($MaxY); $YStep = 1; - - if ( !$NoFill ) - { - //if ( $DebugLine ) { $MinY = $DebugLine; $MaxY = $DebugLine; } - for($Y=$MinY;$Y<=$MaxY;$Y=$Y+$YStep) - { - $Intersections = ""; $LastSlope = NULL; $RestoreLast = "-"; - foreach($Segments as $Key => $Coords) - { - $X1 = $Coords["X1"]; $X2 = $Coords["X2"]; $Y1 = $Coords["Y1"]; $Y2 = $Coords["Y2"]; - - if ( min($Y1,$Y2) <= $Y && max($Y1,$Y2) >= $Y ) - { - if ( $Y1 == $Y2 ) - { $X = $X1; } - else - { $X = $X1 + ( ($Y-$Y1)*$X2 - ($Y-$Y1)*$X1 ) / ($Y2-$Y1); } - - $X = floor($X); - - if ( $X2 == $X1 ) - { $Slope = "!"; } - else - { - $SlopeC = ($Y2 - $Y1) / ($X2 - $X1); - if( $SlopeC == 0 ) - { $Slope = "="; } - elseif( $SlopeC > 0 ) - { $Slope = "+"; } - elseif ( $SlopeC < 0 ) - { $Slope = "-"; } - } - - if ( !is_array($Intersections) ) - { $Intersections[] = $X; } - elseif( !in_array($X,$Intersections) ) - { $Intersections[] = $X; } - elseif( in_array($X,$Intersections) ) - { - if ($Y == $DebugLine) { echo $Slope."/".$LastSlope."(".$X.") "; } - - if ( $Slope == "=" && $LastSlope == "-" ) { $Intersections[] = $X; } - if ( $Slope != $LastSlope && $LastSlope != "!" && $LastSlope != "=" ) { $Intersections[] = $X; } - if ( $Slope != $LastSlope && $LastSlope == "!" && $Slope == "+" ) { $Intersections[] = $X; } - } - - if ( is_array($Intersections) && in_array($X,$Intersections) && $LastSlope == "=" && ($Slope == "-" )) { $Intersections[] = $X; } - - $LastSlope = $Slope; - } - } - if ( $RestoreLast != "-" ) { $Intersections[] = $RestoreLast; echo "@".$Y."\r\n"; } - - if ( is_array($Intersections) ) - { - sort($Intersections); - - if ($Y == $DebugLine) { print_r($Intersections); } - - /* Remove NULL plots */ - $Result = ""; - for($i=0;$i<=count($Intersections)-1;$i=$i+2) - { - if ( isset($Intersections[$i+1]) ) - { if ( $Intersections[$i] != $Intersections[$i+1] ) { $Result[] = $Intersections[$i]; $Result[] = $Intersections[$i+1]; } } - } - - if ( is_array($Result) ) - { - $Intersections = $Result; - - $LastX = OUT_OF_SIGHT; - foreach($Intersections as $Key => $X) - { - if ( $LastX == OUT_OF_SIGHT ) - $LastX = $X; - elseif ( $LastX != OUT_OF_SIGHT ) - { - if ( $this->getFirstDecimal($LastX) > 1 ) { $LastX++; } - - $Color = $DefaultColor; - if ( $Threshold != NULL ) - { - foreach($Threshold as $Key => $Parameters) - { - if ( $Y <= $Parameters["MinX"] && $Y >= $Parameters["MaxX"]) - { - if ( isset($Parameters["R"]) ) { $R = $Parameters["R"]; } else { $R = 0; } - if ( isset($Parameters["G"]) ) { $G = $Parameters["G"]; } else { $G = 0; } - if ( isset($Parameters["B"]) ) { $B = $Parameters["B"]; } else { $B = 0; } - if ( isset($Parameters["Alpha"]) ) { $Alpha = $Parameters["Alpha"]; } else { $Alpha = 100; } - $Color = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); - } - } - } - - imageline($this->Picture,$LastX,$Y,$X,$Y,$Color); - - if ( $Y == $DebugLine) { imageline($this->Picture,$LastX,$Y,$X,$Y,$DebugColor); } - - $LastX = OUT_OF_SIGHT; - } - } - } - } - } - } - - /* Draw the polygon border, if required */ - if ( !$NoBorder) - { - foreach($Segments as $Key => $Coords) - $this->drawLine($Coords["X1"],$Coords["Y1"],$Coords["X2"],$Coords["Y2"],array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Threshold"=>$Threshold)); - } - - $this->Shadow = $RestoreShadow; - } - - /* Return the abscissa margin */ - function getAbscissaMargin($Data) - { - foreach($Data["Axis"] as $AxisID => $Values) { if ( $Values["Identity"] == AXIS_X ) { return($Values["Margin"]); } } - return(0); - } - - } -?> +DataSet->getData(); + + foreach($Data["Series"] as $SerieName => $Serie) + { if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) { $Results++; } } + + return($Results); + } + + /* Fix box coordinates */ + function fixBoxCoordinates($Xa,$Ya,$Xb,$Yb) + { + $X1 = min($Xa,$Xb); $Y1 = min($Ya,$Yb); + $X2 = max($Xa,$Xb); $Y2 = max($Ya,$Yb); + + return(array($X1,$Y1,$X2,$Y2)); + } + + /* Draw a polygon */ + function drawPolygon($Points,$Format="") + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $NoFill = isset($Format["NoFill"]) ? $Format["NoFill"] : FALSE; + $NoBorder = isset($Format["NoBorder"]) ? $Format["NoBorder"] : FALSE; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B; + $BorderAlpha = isset($Format["Alpha"]) ? $Format["Alpha"] : $Alpha / 2; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; + $SkipX = isset($Format["SkipX"]) ? $Format["SkipX"] : OUT_OF_SIGHT; + $SkipY = isset($Format["SkipY"]) ? $Format["SkipY"] : OUT_OF_SIGHT; + + /* Calling the ImageFilledPolygon() function over the $Points array will round it */ + $Backup = $Points; + + if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; } + + if ( $SkipX != OUT_OF_SIGHT ) { $SkipX = floor($SkipX); } + if ( $SkipY != OUT_OF_SIGHT ) { $SkipY = floor($SkipY); } + + $RestoreShadow = $this->Shadow; + if ( !$NoFill ) + { + if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) + { + $this->Shadow = FALSE; + for($i=0;$i<=count($Points)-1;$i=$i+2) + { $Shadow[] = $Points[$i] + $this->ShadowX; $Shadow[] = $Points[$i+1] + $this->ShadowY; } + $this->drawPolygon($Shadow,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa,"NoBorder"=>TRUE)); + } + + $FillColor = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); + + if ( count($Points) >= 6 ) + { ImageFilledPolygon($this->Picture,$Points,count($Points)/2,$FillColor); } + } + + if ( !$NoBorder ) + { + $Points = $Backup; + + if ( $NoFill ) + $BorderSettings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); + else + $BorderSettings = array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha); + + for($i=0;$i<=count($Points)-1;$i=$i+2) + { + if ( isset($Points[$i+2]) ) + { + if ( !($Points[$i] == $Points[$i+2] && $Points[$i] == $SkipX ) && !($Points[$i+1] == $Points[$i+3] && $Points[$i+1] == $SkipY ) ) + $this->drawLine($Points[$i],$Points[$i+1],$Points[$i+2],$Points[$i+3],$BorderSettings); + } + else + { + if ( !($Points[$i] == $Points[0] && $Points[$i] == $SkipX ) && !($Points[$i+1] == $Points[1] && $Points[$i+1] == $SkipY ) ) + $this->drawLine($Points[$i],$Points[$i+1],$Points[0],$Points[1],$BorderSettings); + } + } + } + + $this->Shadow = $RestoreShadow; + } + + /* Apply AALias correction to the rounded box boundaries */ + function offsetCorrection($Value,$Mode) + { + $Value = round($Value,1); + + if ( $Value == 0 && $Mode == 1 ) { return(.9); } + if ( $Value == 0 ) { return(0); } + + if ( $Mode == 1) + { if ( $Value == 1 ) { return(.9); }; if ( $Value == .1 ) { return(.9); }; if ( $Value == .2 ) { return(.8); }; if ( $Value == .3 ) { return(.8); }; if ( $Value == .4 ) { return(.7); }; if ( $Value == .5 ) { return(.5); }; if ( $Value == .6 ) { return(.8); }; if ( $Value == .7 ) { return(.7); }; if ( $Value == .8 ) { return(.6); }; if ( $Value == .9 ) { return(.9); }; } + + if ( $Mode == 2) + { if ( $Value == 1 ) { return(.9); }; if ( $Value == .1 ) { return(.1); }; if ( $Value == .2 ) { return(.2); }; if ( $Value == .3 ) { return(.3); }; if ( $Value == .4 ) { return(.4); }; if ( $Value == .5 ) { return(.5); }; if ( $Value == .6 ) { return(.8); }; if ( $Value == .7 ) { return(.7); }; if ( $Value == .8 ) { return(.8); }; if ( $Value == .9 ) { return(.9); }; } + + if ( $Mode == 3) + { if ( $Value == 1 ) { return(.1); }; if ( $Value == .1 ) { return(.1); }; if ( $Value == .2 ) { return(.2); }; if ( $Value == .3 ) { return(.3); }; if ( $Value == .4 ) { return(.4); }; if ( $Value == .5 ) { return(.9); }; if ( $Value == .6 ) { return(.6); }; if ( $Value == .7 ) { return(.7); }; if ( $Value == .8 ) { return(.4); }; if ( $Value == .9 ) { return(.5); }; } + + if ( $Mode == 4) + { if ( $Value == 1 ) { return(-1); }; if ( $Value == .1 ) { return(.1); }; if ( $Value == .2 ) { return(.2); }; if ( $Value == .3 ) { return(.3); }; if ( $Value == .4 ) { return(.1); }; if ( $Value == .5 ) { return(-.1); }; if ( $Value == .6 ) { return(.8); }; if ( $Value == .7 ) { return(.1); }; if ( $Value == .8 ) { return(.1); }; if ( $Value == .9 ) { return(.1); }; } + } + + /* Draw a rectangle with rounded corners */ + function drawRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,$Format="") + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + + list($X1,$Y1,$X2,$Y2) = $this->fixBoxCoordinates($X1,$Y1,$X2,$Y2); + + if ( $X2 - $X1 < $Radius ) { $Radius = floor((($X2-$X1))/2); } + if ( $Y2 - $Y1 < $Radius ) { $Radius = floor((($Y2-$Y1))/2); } + + $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"NoBorder"=>TRUE); + + if ( $Radius <= 0 ) { $this->drawRectangle($X1,$Y1,$X2,$Y2,$Color); return(0); } + + if ( $this->Antialias ) + { + $this->drawLine($X1+$Radius,$Y1,$X2-$Radius,$Y1,$Color); + $this->drawLine($X2,$Y1+$Radius,$X2,$Y2-$Radius,$Color); + $this->drawLine($X2-$Radius,$Y2,$X1+$Radius,$Y2,$Color); + $this->drawLine($X1,$Y1+$Radius,$X1,$Y2-$Radius,$Color); + } + else + { + $Color = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); + imageline($this->Picture,$X1+$Radius,$Y1,$X2-$Radius,$Y1,$Color); + imageline($this->Picture,$X2,$Y1+$Radius,$X2,$Y2-$Radius,$Color); + imageline($this->Picture,$X2-$Radius,$Y2,$X1+$Radius,$Y2,$Color); + imageline($this->Picture,$X1,$Y1+$Radius,$X1,$Y2-$Radius,$Color); + } + + $Step = 360 / (2 * PI * $Radius); + for($i=0;$i<=90;$i=$i+$Step) + { + $X = cos(($i+180)*PI/180) * $Radius + $X1 + $Radius; + $Y = sin(($i+180)*PI/180) * $Radius + $Y1 + $Radius; + $this->drawAntialiasPixel($X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); + + $X = cos(($i+90)*PI/180) * $Radius + $X1 + $Radius; + $Y = sin(($i+90)*PI/180) * $Radius + $Y2 - $Radius; + $this->drawAntialiasPixel($X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); + + $X = cos($i*PI/180) * $Radius + $X2 - $Radius; + $Y = sin($i*PI/180) * $Radius + $Y2 - $Radius; + $this->drawAntialiasPixel($X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); + + $X = cos(($i+270)*PI/180) * $Radius + $X2 - $Radius; + $Y = sin(($i+270)*PI/180) * $Radius + $Y1 + $Radius; + $this->drawAntialiasPixel($X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); + } + } + + /* Draw a rectangle with rounded corners */ + function drawRoundedFilledRectangle($X1,$Y1,$X2,$Y2,$Radius,$Format="") + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; + + /* Temporary fix for AA issue */ + $Y1 = floor($Y1); $Y2 = floor($Y2); $X1 = floor($X1); $X2 = floor($X2); + + if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; } + if ( $BorderR == -1 ) { $BorderR = $R; $BorderG = $G; $BorderB = $B; } + + list($X1,$Y1,$X2,$Y2) = $this->fixBoxCoordinates($X1,$Y1,$X2,$Y2); + + if ( $X2 - $X1 < $Radius*2 ) { $Radius = floor((($X2-$X1))/4); } + if ( $Y2 - $Y1 < $Radius*2 ) { $Radius = floor((($Y2-$Y1))/4); } + + $RestoreShadow = $this->Shadow; + if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) + { + $this->Shadow = FALSE; + $this->drawRoundedFilledRectangle($X1+$this->ShadowX,$Y1+$this->ShadowY,$X2+$this->ShadowX,$Y2+$this->ShadowY,$Radius,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa)); + } + + $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"NoBorder"=>TRUE); + + if ( $Radius <= 0 ) { $this->drawFilledRectangle($X1,$Y1,$X2,$Y2,$Color); return(0); } + + $YTop = $Y1+$Radius; + $YBottom = $Y2-$Radius; + + $Step = 360 / (2 * PI * $Radius); + $Positions = ""; $Radius--; $MinY = ""; $MaxY = ""; + for($i=0;$i<=90;$i=$i+$Step) + { + $Xp1 = cos(($i+180)*PI/180) * $Radius + $X1 + $Radius; + $Xp2 = cos(((90-$i)+270)*PI/180) * $Radius + $X2 - $Radius; + $Yp = floor(sin(($i+180)*PI/180) * $Radius + $YTop); + if ( $MinY == "" || $Yp > $MinY ) { $MinY = $Yp; } + + if ( $Xp1 <= floor($X1) ) { $Xp1++; } + if ( $Xp2 >= floor($X2) ) { $Xp2--; } + $Xp1++; + + if ( !isset($Positions[$Yp]) ) + { $Positions[$Yp]["X1"] = $Xp1; $Positions[$Yp]["X2"] = $Xp2; } + else + { $Positions[$Yp]["X1"] = ($Positions[$Yp]["X1"]+$Xp1)/2; $Positions[$Yp]["X2"] = ($Positions[$Yp]["X2"]+$Xp2)/2; } + + $Xp1 = cos(($i+90)*PI/180) * $Radius + $X1 + $Radius; + $Xp2 = cos((90-$i)*PI/180) * $Radius + $X2 - $Radius; + $Yp = floor(sin(($i+90)*PI/180) * $Radius + $YBottom); + if ( $MaxY == "" || $Yp < $MaxY ) { $MaxY = $Yp; } + + if ( $Xp1 <= floor($X1) ) { $Xp1++; } + if ( $Xp2 >= floor($X2) ) { $Xp2--; } + $Xp1++; + + if ( !isset($Positions[$Yp]) ) + { $Positions[$Yp]["X1"] = $Xp1; $Positions[$Yp]["X2"] = $Xp2; } + else + { $Positions[$Yp]["X1"] = ($Positions[$Yp]["X1"]+$Xp1)/2; $Positions[$Yp]["X2"] = ($Positions[$Yp]["X2"]+$Xp2)/2; } + } + + $ManualColor = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); + foreach($Positions as $Yp => $Bounds) + { + $X1 = $Bounds["X1"]; $X1Dec = $this->getFirstDecimal($X1); if ( $X1Dec != 0 ) { $X1 = floor($X1)+1; } + $X2 = $Bounds["X2"]; $X2Dec = $this->getFirstDecimal($X2); if ( $X2Dec != 0 ) { $X2 = floor($X2)-1; } + imageline($this->Picture,$X1,$Yp,$X2,$Yp,$ManualColor); + } + $this->drawFilledRectangle($X1,$MinY+1,floor($X2),$MaxY-1,$Color); + + $Radius++; + $this->drawRoundedRectangle($X1,$Y1,$X2+1,$Y2-1,$Radius,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha)); + + $this->Shadow = $RestoreShadow; + } + + /* Draw a rectangle with rounded corners */ + function drawRoundedFilledRectangle_deprecated($X1,$Y1,$X2,$Y2,$Radius,$Format="") + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; + + if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; } + if ( $BorderR == -1 ) { $BorderR = $R; $BorderG = $G; $BorderB = $B; } + + list($X1,$Y1,$X2,$Y2) = $this->fixBoxCoordinates($X1,$Y1,$X2,$Y2); + + if ( $X2 - $X1 < $Radius ) { $Radius = floor((($X2-$X1)+2)/2); } + if ( $Y2 - $Y1 < $Radius ) { $Radius = floor((($Y2-$Y1)+2)/2); } + + $RestoreShadow = $this->Shadow; + if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) + { + $this->Shadow = FALSE; + $this->drawRoundedFilledRectangle($X1+$this->ShadowX,$Y1+$this->ShadowY,$X2+$this->ShadowX,$Y2+$this->ShadowY,$Radius,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa)); + } + + if ( $this->getFirstDecimal($X2) >= 5 ) { $XOffset2 = 1; } else { $XOffset2 = 0; } + if ( $this->getFirstDecimal($X1) <= 5 ) { $XOffset1 = 1; } else { $XOffset1 = 0; } + + if ( !$this->Antialias ) { $XOffset1 = 1; $XOffset2 = 1; } + + $YTop = floor($Y1+$Radius); + $YBottom = floor($Y2-$Radius); + + $this->drawFilledRectangle($X1-$XOffset1,$YTop,$X2+$XOffset2,$YBottom,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"NoBorder"=>TRUE)); + + $Step = 360 / (2 * PI * $Radius); + $Color = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); + $Color2 = $this->allocateColor($this->Picture,255,0,0,$Alpha); + $Drawn = ""; + + if ( $Alpha < 100 ) { $Drawn[$YTop] = FALSE; } + if ( $Alpha < 100 ) { $Drawn[$YBottom] = TRUE; } + + for($i=0;$i<=90;$i=$i+$Step) + { + $Xp1 = cos(($i+180)*PI/180) * $Radius + $X1 + $Radius; + $Xp2 = cos(((90-$i)+270)*PI/180) * $Radius + $X2 - $Radius; + $Yp = sin(($i+180)*PI/180) * $Radius + $YTop; + + if ( $this->getFirstDecimal($Xp1) > 5 ) { $XOffset1 = 1; } else { $XOffset1 = 0; } + if ( $this->getFirstDecimal($Xp2) > 5 ) { $XOffset2 = 1; } else { $XOffset2 = 0; } + if ( $this->getFirstDecimal($Yp) > 5 ) { $YOffset = 1; } else { $YOffset = 0; } + + if ( !isset($Drawn[$Yp+$YOffset]) || $Alpha == 100 ) + imageline($this->Picture,$Xp1+$XOffset1,$Yp+$YOffset,$Xp2+$XOffset2,$Yp+$YOffset,$Color); + + $Drawn[$Yp+$YOffset] = $Xp2; + + $Xp1 = cos(($i+90)*PI/180) * $Radius + $X1 + $Radius; + $Xp2 = cos((90-$i)*PI/180) * $Radius + $X2 - $Radius; + $Yp = sin(($i+90)*PI/180) * $Radius + $YBottom; + + if ( $this->getFirstDecimal($Xp1) > 7 ) { $XOffset1 = 1; } else { $XOffset1 = 0; } + if ( $this->getFirstDecimal($Xp2) > 7 ) { $XOffset2 = 1; } else { $XOffset2 = 0; } + if ( $this->getFirstDecimal($Yp) > 5 ) { $YOffset = 1; } else { $YOffset = 0; } + + if ( !isset($Drawn[$Yp+$YOffset]) || $Alpha == 100 ) + imageline($this->Picture,$Xp1+$XOffset1,$Yp+$YOffset,$Xp2+$XOffset2,$Yp+$YOffset,$Color); + + $Drawn[$Yp+$YOffset] = $Xp2; + } + + $this->drawRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha)); + + $this->Shadow = $RestoreShadow; + } + + /* Draw a rectangle */ + function drawRectangle($X1,$Y1,$X2,$Y2,$Format="") + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL; + $NoAngle = isset($Format["NoAngle"]) ? $Format["NoAngle"] : FALSE; + + if ($X1 > $X2) { list($X1, $X2) = array($X2, $X1); } + if ($Y1 > $Y2) { list($Y1, $Y2) = array($Y2, $Y1); } + + if ( $this->Antialias ) + { + if ( $NoAngle ) + { + $this->drawLine($X1+1,$Y1,$X2-1,$Y1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); + $this->drawLine($X2,$Y1+1,$X2,$Y2-1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); + $this->drawLine($X2-1,$Y2,$X1+1,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); + $this->drawLine($X1,$Y1+1,$X1,$Y2-1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); + } + else + { + $this->drawLine($X1+1,$Y1,$X2-1,$Y1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); + $this->drawLine($X2,$Y1,$X2,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); + $this->drawLine($X2-1,$Y2,$X1+1,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); + $this->drawLine($X1,$Y1,$X1,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); + } + } + else + { + $Color = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); + imagerectangle($this->Picture,$X1,$Y1,$X2,$Y2,$Color); + } + } + + /* Draw a filled rectangle */ + function drawFilledRectangle($X1,$Y1,$X2,$Y2,$Format="") + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL; + $NoAngle = isset($Format["NoAngle"]) ? $Format["NoAngle"] : NULL; + $Dash = isset($Format["Dash"]) ? $Format["Dash"] : FALSE; + $DashStep = isset($Format["DashStep"]) ? $Format["DashStep"] : 4; + $DashR = isset($Format["DashR"]) ? $Format["DashR"] : 0; + $DashG = isset($Format["DashG"]) ? $Format["DashG"] : 0; + $DashB = isset($Format["DashB"]) ? $Format["DashB"] : 0; + $NoBorder = isset($Format["NoBorder"]) ? $Format["NoBorder"] : FALSE; + + if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; } + + if ($X1 > $X2) { list($X1, $X2) = array($X2, $X1); } + if ($Y1 > $Y2) { list($Y1, $Y2) = array($Y2, $Y1); } + + $RestoreShadow = $this->Shadow; + if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) + { + $this->Shadow = FALSE; + $this->drawFilledRectangle($X1+$this->ShadowX,$Y1+$this->ShadowY,$X2+$this->ShadowX,$Y2+$this->ShadowY,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa,"Ticks"=>$Ticks,"NoAngle"=>$NoAngle)); + } + + $Color = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); + if ( $NoAngle ) + { + imagefilledrectangle($this->Picture,ceil($X1)+1,ceil($Y1),floor($X2)-1,floor($Y2),$Color); + imageline($this->Picture,ceil($X1),ceil($Y1)+1,ceil($X1),floor($Y2)-1,$Color); + imageline($this->Picture,floor($X2),ceil($Y1)+1,floor($X2),floor($Y2)-1,$Color); + } + else + imagefilledrectangle($this->Picture,ceil($X1),ceil($Y1),floor($X2),floor($Y2),$Color); + + if ( $Dash ) + { + if ( $BorderR != -1 ) { $iX1=$X1+1; $iY1=$Y1+1; $iX2=$X2-1; $iY2=$Y2-1; } else { $iX1=$X1; $iY1=$Y1; $iX2=$X2; $iY2=$Y2; } + + $Color = $this->allocateColor($this->Picture,$DashR,$DashG,$DashB,$Alpha); + $Y=$iY1-$DashStep; + for($X=$iX1; $X<=$iX2+($iY2-$iY1); $X=$X+$DashStep) + { + $Y=$Y+$DashStep; + if ( $X > $iX2 ) { $Xa = $X-($X-$iX2); $Ya = $iY1+($X-$iX2); } else { $Xa = $X; $Ya = $iY1; } + if ( $Y > $iY2 ) { $Xb = $iX1+($Y-$iY2); $Yb = $Y-($Y-$iY2); } else { $Xb = $iX1; $Yb = $Y; } + imageline($this->Picture,$Xa,$Ya,$Xb,$Yb,$Color); + } + } + + if ( $this->Antialias && !$NoBorder ) + { + if ( $X1 < ceil($X1) ) + { + $AlphaA = $Alpha * (ceil($X1) - $X1); + $Color = $this->allocateColor($this->Picture,$R,$G,$B,$AlphaA); + imageline($this->Picture,ceil($X1)-1,ceil($Y1),ceil($X1)-1,floor($Y2),$Color); + } + + if ( $Y1 < ceil($Y1) ) + { + $AlphaA = $Alpha * (ceil($Y1) - $Y1); + $Color = $this->allocateColor($this->Picture,$R,$G,$B,$AlphaA); + imageline($this->Picture,ceil($X1),ceil($Y1)-1,floor($X2),ceil($Y1)-1,$Color); + } + + if ( $X2 > floor($X2) ) + { + $AlphaA = $Alpha * (.5-($X2 - floor($X2))); + $Color = $this->allocateColor($this->Picture,$R,$G,$B,$AlphaA); + imageline($this->Picture,floor($X2)+1,ceil($Y1),floor($X2)+1,floor($Y2),$Color); + } + + if ( $Y2 > floor($Y2) ) + { + $AlphaA = $Alpha * (.5-($Y2 - floor($Y2))); + $Color = $this->allocateColor($this->Picture,$R,$G,$B,$AlphaA); + imageline($this->Picture,ceil($X1),floor($Y2)+1,floor($X2),floor($Y2)+1,$Color); + } + } + + if ( $BorderR != -1 ) + $this->drawRectangle($X1,$Y1,$X2,$Y2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$Ticks,"NoAngle"=>$NoAngle)); + + $this->Shadow = $RestoreShadow; + } + + /* Draw a rectangular marker of the specified size */ + function drawRectangleMarker($X,$Y,$Format="") + { + $Size = isset($Format["Size"]) ? $Format["Size"] : 4; + + $HalfSize = floor($Size/2); + $this->drawFilledRectangle($X-$HalfSize,$Y-$HalfSize,$X+$HalfSize,$Y+$HalfSize,$Format); + } + + /* Drawn a spline based on the bezier function */ + function drawSpline($Coordinates,$Format="") + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Force = isset($Format["Force"]) ? $Format["Force"] : 30; + $Forces = isset($Format["Forces"]) ? $Format["Forces"] : NULL; + $ShowC = isset($Format["ShowControl"]) ? $Format["ShowControl"] : FALSE; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL; + $PathOnly = isset($Format["PathOnly"]) ? $Format["PathOnly"] : FALSE; + $Weight = isset($Format["Weight"]) ? $Format["Weight"] : NULL; + + $Cpt = NULL; $Mode = NULL; $Result = ""; + for($i=1;$i<=count($Coordinates)-1;$i++) + { + $X1 = $Coordinates[$i-1][0]; $Y1 = $Coordinates[$i-1][1]; + $X2 = $Coordinates[$i][0]; $Y2 = $Coordinates[$i][1]; + + if ( $Forces != NULL ) { $Force = $Forces[$i]; } + + /* First segment */ + if ( $i == 1 ) + { $Xv1 = $X1; $Yv1 = $Y1; } + else + { + $Angle1 = $this->getAngle($XLast,$YLast,$X1,$Y1); + $Angle2 = $this->getAngle($X1,$Y1,$X2,$Y2); + $XOff = cos($Angle2 * PI / 180) * $Force + $X1; + $YOff = sin($Angle2 * PI / 180) * $Force + $Y1; + + $Xv1 = cos($Angle1 * PI / 180) * $Force + $XOff; + $Yv1 = sin($Angle1 * PI / 180) * $Force + $YOff; + } + + /* Last segment */ + if ( $i == count($Coordinates)-1 ) + { $Xv2 = $X2; $Yv2 = $Y2; } + else + { + $Angle1 = $this->getAngle($X2,$Y2,$Coordinates[$i+1][0],$Coordinates[$i+1][1]); + $Angle2 = $this->getAngle($X1,$Y1,$X2,$Y2); + $XOff = cos(($Angle2+180) * PI / 180) * $Force + $X2; + $YOff = sin(($Angle2+180) * PI / 180) * $Force + $Y2; + + $Xv2 = cos(($Angle1+180) * PI / 180) * $Force + $XOff; + $Yv2 = sin(($Angle1+180) * PI / 180) * $Force + $YOff; + } + + $Path = $this->drawBezier($X1,$Y1,$X2,$Y2,$Xv1,$Yv1,$Xv2,$Yv2,$Format); + if ($PathOnly) { $Result[] = $Path; } + + $XLast = $X1; $YLast = $Y1; + } + + return($Result); + } + + /* Draw a bezier curve with two controls points */ + function drawBezier($X1,$Y1,$X2,$Y2,$Xv1,$Yv1,$Xv2,$Yv2,$Format="") + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $ShowC = isset($Format["ShowControl"]) ? $Format["ShowControl"] : FALSE; + $Segments = isset($Format["Segments"]) ? $Format["Segments"] : NULL; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL; + $NoDraw = isset($Format["NoDraw"]) ? $Format["NoDraw"] : FALSE; + $PathOnly = isset($Format["PathOnly"]) ? $Format["PathOnly"] : FALSE; + $Weight = isset($Format["Weight"]) ? $Format["Weight"] : NULL; + $DrawArrow = isset($Format["DrawArrow"]) ? $Format["DrawArrow"] : FALSE; + $ArrowSize = isset($Format["ArrowSize"]) ? $Format["ArrowSize"] : 10; + $ArrowRatio = isset($Format["ArrowRatio"]) ? $Format["ArrowRatio"] : .5; + $ArrowTwoHeads = isset($Format["ArrowTwoHeads"]) ? $Format["ArrowTwoHeads"] : FALSE; + + if ( $Segments == NULL ) + { + $Length = $this->getLength($X1,$Y1,$X2,$Y2); + $Precision = ($Length*125)/1000; + } + else + $Precision = $Segments; + + $P[0]["X"] = $X1; $P[0]["Y"] = $Y1; + $P[1]["X"] = $Xv1; $P[1]["Y"] = $Yv1; + $P[2]["X"] = $Xv2; $P[2]["Y"] = $Yv2; + $P[3]["X"] = $X2; $P[3]["Y"] = $Y2; + + /* Compute the bezier points */ + $Q = ""; $ID = 0; $Path = ""; + for($i=0;$i<=$Precision;$i=$i+1) + { + $u = $i / $Precision; + + $C = ""; + $C[0] = (1 - $u) * (1 - $u) * (1 - $u); + $C[1] = ($u * 3) * (1 - $u) * (1 - $u); + $C[2] = 3 * $u * $u * (1 - $u); + $C[3] = $u * $u * $u; + + for($j=0;$j<=3;$j++) + { + if ( !isset($Q[$ID]) ) { $Q[$ID] = ""; } + if ( !isset($Q[$ID]["X"]) ) { $Q[$ID]["X"] = 0; } + if ( !isset($Q[$ID]["Y"]) ) { $Q[$ID]["Y"] = 0; } + + $Q[$ID]["X"] = $Q[$ID]["X"] + $P[$j]["X"] * $C[$j]; + $Q[$ID]["Y"] = $Q[$ID]["Y"] + $P[$j]["Y"] * $C[$j]; + } + $ID++; + } + $Q[$ID]["X"] = $X2; $Q[$ID]["Y"] = $Y2; + + if ( !$NoDraw ) + { + /* Display the control points */ + if ( $ShowC && !$PathOnly ) + { + $Xv1 = floor($Xv1); $Yv1 = floor($Yv1); $Xv2 = floor($Xv2); $Yv2 = floor($Yv2); + + $this->drawLine($X1,$Y1,$X2,$Y2,array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>30)); + + $MyMarkerSettings = array("R"=>255,"G"=>0,"B"=>0,"BorderR"=>255,"BorderB"=>255,"BorderG"=>255,"Size"=>4); + $this->drawRectangleMarker($Xv1,$Yv1,$MyMarkerSettings); + $this->drawText($Xv1+4,$Yv1,"v1"); + $MyMarkerSettings = array("R"=>0,"G"=>0,"B"=>255,"BorderR"=>255,"BorderB"=>255,"BorderG"=>255,"Size"=>4); + $this->drawRectangleMarker($Xv2,$Yv2,$MyMarkerSettings); + $this->drawText($Xv2+4,$Yv2,"v2"); + } + + /* Draw the bezier */ + $LastX = NULL; $LastY = NULL; $Cpt = NULL; $Mode = NULL; $ArrowS = NULL; + foreach ($Q as $Key => $Point) + { + $X = $Point["X"]; $Y = $Point["Y"]; + + /* Get the first segment */ + if ( $ArrowS == NULL && $LastX != NULL && $LastY != NULL ) + { $ArrowS["X2"] = $LastX; $ArrowS["Y2"] = $LastY; $ArrowS["X1"] = $X; $ArrowS["Y1"] = $Y; } + + if ( $LastX != NULL && $LastY != NULL && !$PathOnly) + list($Cpt,$Mode) = $this->drawLine($LastX,$LastY,$X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Cpt"=>$Cpt,"Mode"=>$Mode,"Weight"=>$Weight)); + + /* Get the last segment */ + $ArrowE["X1"] = $LastX; $ArrowE["Y1"] = $LastY; $ArrowE["X2"] = $X; $ArrowE["Y2"] = $Y; + + $LastX = $X; $LastY = $Y; + } + + if ( $DrawArrow && !$PathOnly ) + { + $ArrowSettings = array("FillR"=>$R,"FillG"=>$G,"FillB"=>$B,"Alpha"=>$Alpha,"Size"=>$ArrowSize,"Ratio"=>$ArrowRatio); + if ( $ArrowTwoHeads ) + $this->drawArrow($ArrowS["X1"],$ArrowS["Y1"],$ArrowS["X2"],$ArrowS["Y2"],$ArrowSettings); + + $this->drawArrow($ArrowE["X1"],$ArrowE["Y1"],$ArrowE["X2"],$ArrowE["Y2"],$ArrowSettings); + } + } + return($Q); + } + + /* Draw a line between two points */ + function drawLine($X1,$Y1,$X2,$Y2,$Format="") + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL; + $Cpt = isset($Format["Cpt"]) ? $Format["Cpt"] : 1; + $Mode = isset($Format["Mode"]) ? $Format["Mode"] : 1; + $Weight = isset($Format["Weight"]) ? $Format["Weight"] : NULL; + $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : NULL; + + if ( $this->Antialias == FALSE && $Ticks == NULL ) + { + if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) + { + $ShadowColor = $this->allocateColor($this->Picture,$this->ShadowR,$this->ShadowG,$this->ShadowB,$this->Shadowa); + imageline($this->Picture,$X1+$this->ShadowX,$Y1+$this->ShadowY,$X2+$this->ShadowX,$Y2+$this->ShadowY,$ShadowColor); + } + + $Color = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); + imageline($this->Picture,$X1,$Y1,$X2,$Y2,$Color); + return(0); + } + + $Distance = sqrt(($X2-$X1)*($X2-$X1)+($Y2-$Y1)*($Y2-$Y1)); + if ( $Distance == 0 ) { return(-1); } + + /* Derivative algorithm for overweighted lines, re-route to polygons primitives */ + if ( $Weight != NULL ) + { + $Angle = $this->getAngle($X1,$Y1,$X2,$Y2); + $PolySettings = array ("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderAlpha"=>$Alpha); + + if ( $Ticks == NULL ) + { + $Points = ""; + $Points[] = cos(deg2rad($Angle-90)) * $Weight + $X1; $Points[] = sin(deg2rad($Angle-90)) * $Weight + $Y1; + $Points[] = cos(deg2rad($Angle+90)) * $Weight + $X1; $Points[] = sin(deg2rad($Angle+90)) * $Weight + $Y1; + $Points[] = cos(deg2rad($Angle+90)) * $Weight + $X2; $Points[] = sin(deg2rad($Angle+90)) * $Weight + $Y2; + $Points[] = cos(deg2rad($Angle-90)) * $Weight + $X2; $Points[] = sin(deg2rad($Angle-90)) * $Weight + $Y2; + + $this->drawPolygon($Points,$PolySettings); + } + else + { + for($i=0;$i<=$Distance;$i=$i+$Ticks*2) + { + $Xa = (($X2-$X1)/$Distance) * $i + $X1; $Ya = (($Y2-$Y1)/$Distance) * $i + $Y1; + $Xb = (($X2-$X1)/$Distance) * ($i+$Ticks) + $X1; $Yb = (($Y2-$Y1)/$Distance) * ($i+$Ticks) + $Y1; + + $Points = ""; + $Points[] = cos(deg2rad($Angle-90)) * $Weight + $Xa; $Points[] = sin(deg2rad($Angle-90)) * $Weight + $Ya; + $Points[] = cos(deg2rad($Angle+90)) * $Weight + $Xa; $Points[] = sin(deg2rad($Angle+90)) * $Weight + $Ya; + $Points[] = cos(deg2rad($Angle+90)) * $Weight + $Xb; $Points[] = sin(deg2rad($Angle+90)) * $Weight + $Yb; + $Points[] = cos(deg2rad($Angle-90)) * $Weight + $Xb; $Points[] = sin(deg2rad($Angle-90)) * $Weight + $Yb; + + $this->drawPolygon($Points,$PolySettings); + } + } + + return(1); + } + + $XStep = ($X2-$X1) / $Distance; + $YStep = ($Y2-$Y1) / $Distance; + + for($i=0;$i<=$Distance;$i++) + { + $X = $i * $XStep + $X1; + $Y = $i * $YStep + $Y1; + + $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); + + if ( $Threshold != NULL ) + { + foreach($Threshold as $Key => $Parameters) + { + if ( $Y <= $Parameters["MinX"] && $Y >= $Parameters["MaxX"]) + { + if ( isset($Parameters["R"]) ) { $RT = $Parameters["R"]; } else { $RT = 0; } + if ( isset($Parameters["G"]) ) { $GT = $Parameters["G"]; } else { $GT = 0; } + if ( isset($Parameters["B"]) ) { $BT = $Parameters["B"]; } else { $BT = 0; } + if ( isset($Parameters["Alpha"]) ) { $AlphaT = $Parameters["Alpha"]; } else { $AlphaT = 0; } + $Color = array("R"=>$RT,"G"=>$GT,"B"=>$BT,"Alpha"=>$AlphaT); + } + } + } + + if ( $Ticks != NULL ) + { + if ( $Cpt % $Ticks == 0 ) + { $Cpt = 0; if ( $Mode == 1 ) { $Mode = 0; } else { $Mode = 1; } } + + if ( $Mode == 1 ) + $this->drawAntialiasPixel($X,$Y,$Color); + + $Cpt++; + } + else + $this->drawAntialiasPixel($X,$Y,$Color); + } + + return(array($Cpt,$Mode)); + } + + /* Draw a circle */ + function drawCircle($Xc,$Yc,$Height,$Width,$Format="") + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL; + + $Height = abs($Height); + $Width = abs($Width); + + if ( $Height == 0 ) { $Height = 1; } + if ( $Width == 0 ) { $Width = 1; } + $Xc = floor($Xc); $Yc = floor($Yc); + + $RestoreShadow = $this->Shadow; + if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) + { + $this->Shadow = FALSE; + $this->drawCircle($Xc+$this->ShadowX,$Yc+$this->ShadowY,$Height,$Width,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa,"Ticks"=>$Ticks)); + } + + if ( $Width == 0 ) { $Width = $Height; } + if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } + if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } + if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } + + $Step = 360 / (2 * PI * max($Width,$Height)); + $Mode = 1; $Cpt = 1; + for($i=0;$i<=360;$i=$i+$Step) + { + $X = cos($i*PI/180) * $Height + $Xc; + $Y = sin($i*PI/180) * $Width + $Yc; + + if ( $Ticks != NULL ) + { + if ( $Cpt % $Ticks == 0 ) + { $Cpt = 0; if ( $Mode == 1 ) { $Mode = 0; } else { $Mode = 1; } } + + if ( $Mode == 1 ) + $this->drawAntialiasPixel($X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); + + $Cpt++; + } + else + $this->drawAntialiasPixel($X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); + + } + $this->Shadow = $RestoreShadow; + } + + /* Draw a filled circle */ + function drawFilledCircle($X,$Y,$Radius,$Format="") + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; + + if ( $Radius == 0 ) { $Radius = 1; } + if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; } + $X = floor($X); $Y = floor($Y); + + $Radius = abs($Radius); + + $RestoreShadow = $this->Shadow; + if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) + { + $this->Shadow = FALSE; + $this->drawFilledCircle($X+$this->ShadowX,$Y+$this->ShadowY,$Radius,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa,"Ticks"=>$Ticks)); + } + + $this->Mask = ""; + $Color = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); + for ($i=0; $i<=$Radius*2; $i++) + { + $Slice = sqrt($Radius * $Radius - ($Radius - $i) * ($Radius - $i)); + $XPos = floor($Slice); + $YPos = $Y + $i - $Radius; + $AAlias = $Slice - floor($Slice); + + $this->Mask[$X-$XPos][$YPos] = TRUE; + $this->Mask[$X+$XPos][$YPos] = TRUE; + imageline($this->Picture,$X-$XPos,$YPos,$X+$XPos,$YPos,$Color); + } + if ( $this->Antialias ) + $this->drawCircle($X,$Y,$Radius,$Radius,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); + + $this->Mask = ""; + + if ( $BorderR != -1 ) + $this->drawCircle($X,$Y,$Radius,$Radius,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$Ticks)); + + $this->Shadow = $RestoreShadow; + } + + /* Write text */ + function drawText($X,$Y,$Text,$Format="") + { + $R = isset($Format["R"]) ? $Format["R"] : $this->FontColorR; + $G = isset($Format["G"]) ? $Format["G"] : $this->FontColorG; + $B = isset($Format["B"]) ? $Format["B"] : $this->FontColorB; + $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0; + $Align = isset($Format["Align"]) ? $Format["Align"] : TEXT_ALIGN_BOTTOMLEFT; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : $this->FontColorA; + $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->FontName; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize; + $ShowOrigine = isset($Format["ShowOrigine"]) ? $Format["ShowOrigine"] : FALSE; + $TOffset = isset($Format["TOffset"]) ? $Format["TOffset"] : 2; + $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : FALSE; + $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : TRUE; + $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 6; + $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : FALSE; + $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 6; + $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 255; + $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 255; + $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 255; + $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 50; + $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : ""; + $BoxBorderR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0; + $BoxBorderG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0; + $BoxBorderB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0; + $BoxBorderAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 50; + $NoShadow = isset($Format["NoShadow"]) ? $Format["NoShadow"] : FALSE; + + $Shadow = $this->Shadow; + if ( $NoShadow ) { $this->Shadow = FALSE; } + + if ( $BoxSurrounding != "" ) { $BoxBorderR = $BoxR - $BoxSurrounding; $BoxBorderG = $BoxG - $BoxSurrounding; $BoxBorderB = $BoxB - $BoxSurrounding; $BoxBorderAlpha = $BoxAlpha; } + + if ( $ShowOrigine ) + { + $MyMarkerSettings = array("R"=>255,"G"=>0,"B"=>0,"BorderR"=>255,"BorderB"=>255,"BorderG"=>255,"Size"=>4); + $this->drawRectangleMarker($X,$Y,$MyMarkerSettings); + } + + $TxtPos = $this->getTextBox($X,$Y,$FontName,$FontSize,$Angle,$Text); + + if ( $DrawBox && ($Angle == 0 || $Angle == 90 || $Angle == 180 || $Angle == 270)) + { + $T[0]["X"]=0;$T[0]["Y"]=0;$T[1]["X"]=0;$T[1]["Y"]=0;$T[2]["X"]=0;$T[2]["Y"]=0;$T[3]["X"]=0;$T[3]["Y"]=0; + if ( $Angle == 0 ) { $T[0]["X"]=-$TOffset;$T[0]["Y"]=$TOffset;$T[1]["X"]=$TOffset;$T[1]["Y"]=$TOffset;$T[2]["X"]=$TOffset;$T[2]["Y"]=-$TOffset;$T[3]["X"]=-$TOffset;$T[3]["Y"]=-$TOffset; } + + $X1 = min($TxtPos[0]["X"],$TxtPos[1]["X"],$TxtPos[2]["X"],$TxtPos[3]["X"]) - $BorderOffset + 3; + $Y1 = min($TxtPos[0]["Y"],$TxtPos[1]["Y"],$TxtPos[2]["Y"],$TxtPos[3]["Y"]) - $BorderOffset; + $X2 = max($TxtPos[0]["X"],$TxtPos[1]["X"],$TxtPos[2]["X"],$TxtPos[3]["X"]) + $BorderOffset + 3; + $Y2 = max($TxtPos[0]["Y"],$TxtPos[1]["Y"],$TxtPos[2]["Y"],$TxtPos[3]["Y"]) + $BorderOffset - 3; + + $X1 = $X1 - $TxtPos[$Align]["X"] + $X + $T[0]["X"]; + $Y1 = $Y1 - $TxtPos[$Align]["Y"] + $Y + $T[0]["Y"]; + $X2 = $X2 - $TxtPos[$Align]["X"] + $X + $T[0]["X"]; + $Y2 = $Y2 - $TxtPos[$Align]["Y"] + $Y + $T[0]["Y"]; + + $Settings = array("R"=>$BoxR,"G"=>$BoxG,"B"=>$BoxB,"Alpha"=>$BoxAlpha,"BorderR"=>$BoxBorderR,"BorderG"=>$BoxBorderG,"BorderB"=>$BoxBorderB,"BorderAlpha"=>$BoxBorderAlpha); + + if ( $BoxRounded ) + { $this->drawRoundedFilledRectangle($X1,$Y1,$X2,$Y2,$RoundedRadius,$Settings); } + else + { $this->drawFilledRectangle($X1,$Y1,$X2,$Y2,$Settings); } + } + + $X = $X - $TxtPos[$Align]["X"] + $X; + $Y = $Y - $TxtPos[$Align]["Y"] + $Y; + + if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) + { + $C_ShadowColor = $this->allocateColor($this->Picture,$this->ShadowR,$this->ShadowG,$this->ShadowB,$this->Shadowa); + imagettftext($this->Picture,$FontSize,$Angle,$X+$this->ShadowX,$Y+$this->ShadowY,$C_ShadowColor,$FontName,$Text); + } + + $C_TextColor = $this->AllocateColor($this->Picture,$R,$G,$B,$Alpha); + imagettftext($this->Picture,$FontSize,$Angle,$X,$Y,$C_TextColor,$FontName,$Text); + + $this->Shadow = $Shadow; + + return($TxtPos); + } + + /* Draw a gradient within a defined area */ + function drawGradientArea($X1,$Y1,$X2,$Y2,$Direction,$Format="") + { + $StartR = isset($Format["StartR"]) ? $Format["StartR"] : 90; + $StartG = isset($Format["StartG"]) ? $Format["StartG"] : 90; + $StartB = isset($Format["StartB"]) ? $Format["StartB"] : 90; + $EndR = isset($Format["EndR"]) ? $Format["EndR"] : 0; + $EndG = isset($Format["EndG"]) ? $Format["EndG"] : 0; + $EndB = isset($Format["EndB"]) ? $Format["EndB"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Levels = isset($Format["Levels"]) ? $Format["Levels"] : NULL; + + $Shadow = $this->Shadow; + $this->Shadow = FALSE; + + if ( $StartR == $EndR && $StartG == $EndG && $StartB == $EndB ) + { + $this->drawFilledRectangle($X1,$Y1,$X2,$Y2,array("R"=>$StartR,"G"=>$StartG,"B"=>$StartB,"Alpha"=>$Alpha)); + return(0); + } + + if ( $Levels != NULL ) + { $EndR=$StartR+$Levels; $EndG=$StartG+$Levels; $EndB=$StartB+$Levels; } + + if ($X1 > $X2) { list($X1, $X2) = array($X2, $X1); } + if ($Y1 > $Y2) { list($Y1, $Y2) = array($Y2, $Y1); } + + if ( $Direction == DIRECTION_VERTICAL ) { $Width = abs($Y2-$Y1); } + if ( $Direction == DIRECTION_HORIZONTAL ) { $Width = abs($X2-$X1); } + + $Step = max(abs($EndR-$StartR),abs($EndG-$StartG),abs($EndB-$StartB)); + $StepSize = $Width/$Step; + $RStep = ($EndR-$StartR)/$Step; + $GStep = ($EndG-$StartG)/$Step; + $BStep = ($EndB-$StartB)/$Step; + + $R=$StartR;$G=$StartG;$B=$StartB; + switch($Direction) + { + case DIRECTION_VERTICAL: + $StartY = $Y1; $EndY = floor($Y2)+1; $LastY2 = $StartY; + for($i=0;$i<=$Step;$i++) + { + $Y2 = floor($StartY + ($i * $StepSize)); + + if ($Y2 > $EndY) { $Y2 = $EndY; } + if (($Y1 != $Y2 && $Y1 < $Y2) || $Y2 == $EndY) + { + $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); + $this->drawFilledRectangle($X1,$Y1,$X2,$Y2,$Color); + $LastY2 = max($LastY2,$Y2); + $Y1 = $Y2+1; + } + $R = $R + $RStep; $G = $G + $GStep; $B = $B + $BStep; + } + if ( $LastY2 < $EndY && isset($Color)) { for ($i=$LastY2+1;$i<=$EndY;$i++) { $this->drawLine($X1,$i,$X2,$i,$Color); } } + break; + + case DIRECTION_HORIZONTAL: + $StartX = $X1; $EndX = $X2; + for($i=0;$i<=$Step;$i++) + { + $X2 = floor($StartX + ($i * $StepSize)); + + if ($X2 > $EndX) { $X2 = $EndX; } + if (($X1 != $X2 && $X1 < $X2) || $X2 == $EndX) + { + $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); + $this->drawFilledRectangle($X1,$Y1,$X2,$Y2,$Color); + $X1 = $X2+1; + } + $R = $R + $RStep; $G = $G + $GStep; $B = $B + $BStep; + } + if ( $X2 < $EndX && isset($Color)) { $this->drawFilledRectangle($X2,$Y1,$EndX,$Y2,$Color); } + break; + } + + $this->Shadow = $Shadow; + + } + + /* Draw an aliased pixel */ + function drawAntialiasPixel($X,$Y,$Format="") + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + + if ( $X < 0 || $Y < 0 || $X >= $this->XSize || $Y >= $this->YSize ) + return(-1); + + if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } + if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } + if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } + + if ( !$this->Antialias ) + { + if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) + { + $ShadowColor = $this->allocateColor($this->Picture,$this->ShadowR,$this->ShadowG,$this->ShadowB,$this->Shadowa); + imagesetpixel($this->Picture,$X+$this->ShadowX,$Y+$this->ShadowY,$ShadowColor); + } + + $PlotColor = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); + imagesetpixel($this->Picture,$X,$Y,$PlotColor); + + return(0); + } + + $Plot = ""; + $Xi = floor($X); + $Yi = floor($Y); + + if ( $Xi == $X && $Yi == $Y) + { + if ( $Alpha == 100 ) + $this->drawAlphaPixel($X,$Y,100,$R,$G,$B); + else + $this->drawAlphaPixel($X,$Y,$Alpha,$R,$G,$B); + } + else + { + $Alpha1 = (((1 - ($X - floor($X))) * (1 - ($Y - floor($Y))) * 100) / 100) * $Alpha; + if ( $Alpha1 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi,$Yi,$Alpha1,$R,$G,$B); } + + $Alpha2 = ((($X - floor($X)) * (1 - ($Y - floor($Y))) * 100) / 100) * $Alpha; + if ( $Alpha2 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi+1,$Yi,$Alpha2,$R,$G,$B); } + + $Alpha3 = (((1 - ($X - floor($X))) * ($Y - floor($Y)) * 100) / 100) * $Alpha; + if ( $Alpha3 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi,$Yi+1,$Alpha3,$R,$G,$B); } + + $Alpha4 = ((($X - floor($X)) * ($Y - floor($Y)) * 100) / 100) * $Alpha; + if ( $Alpha4 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi+1,$Yi+1,$Alpha4,$R,$G,$B); } + } + } + + /* Draw a semi-transparent pixel */ + function drawAlphaPixel($X,$Y,$Alpha,$R,$G,$B) + { + if ( isset($this->Mask[$X])) { if ( isset($this->Mask[$X][$Y]) ) { return(0); } } + + if ( $X < 0 || $Y < 0 || $X >= $this->XSize || $Y >= $this->YSize ) + return(-1); + + if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } + if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } + if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } + + if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) + { + $AlphaFactor = floor(($Alpha / 100) * $this->Shadowa); + $ShadowColor = $this->allocateColor($this->Picture,$this->ShadowR,$this->ShadowG,$this->ShadowB,$AlphaFactor); + imagesetpixel($this->Picture,$X+$this->ShadowX,$Y+$this->ShadowY,$ShadowColor); + } + + $C_Aliased = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); + imagesetpixel($this->Picture,$X,$Y,$C_Aliased); + } + + /* Convert apha to base 10 */ + function convertAlpha($AlphaValue) + { return((127/100)*(100-$AlphaValue)); } + + /* Allocate a color with transparency */ + function allocateColor($Picture,$R,$G,$B,$Alpha=100) + { + if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } + if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } + if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } + if ( $Alpha < 0 ) { $Alpha = 0; } + if ( $Alpha > 100) { $Alpha = 100; } + + $Alpha = $this->convertAlpha($Alpha); + return(imagecolorallocatealpha($Picture,$R,$G,$B,$Alpha)); + } + + /* Load a PNG file and draw it over the chart */ + function drawFromPNG($X,$Y,$FileName) + { $this->drawFromPicture(1,$FileName,$X,$Y); } + + /* Load a GIF file and draw it over the chart */ + function drawFromGIF($X,$Y,$FileName) + { $this->drawFromPicture(2,$FileName,$X,$Y); } + + /* Load a JPEG file and draw it over the chart */ + function drawFromJPG($X,$Y,$FileName) + { $this->drawFromPicture(3,$FileName,$X,$Y); } + + function getPicInfo($FileName) + { + $Infos = getimagesize($FileName); + $Width = $Infos[0]; + $Height = $Infos[1]; + $Type = $Infos["mime"]; + + if ( $Type == "image/png") { $Type = 1; } + if ( $Type == "image/gif") { $Type = 2; } + if ( $Type == "image/jpeg ") { $Type = 3; } + + return(array($Width,$Height,$Type)); + } + + /* Generic loader function for external pictures */ + function drawFromPicture($PicType,$FileName,$X,$Y) + { + if ( file_exists($FileName)) + { + list($Width,$Height) = $this->getPicInfo($FileName); + + if ( $PicType == 1 ) + { $Raster = imagecreatefrompng($FileName); } + elseif ( $PicType == 2 ) + { $Raster = imagecreatefromgif($FileName); } + elseif ( $PicType == 3 ) + { $Raster = imagecreatefromjpeg($FileName); } + else + { return(0); } + + + $RestoreShadow = $this->Shadow; + if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) + { + $this->Shadow = FALSE; + if ( $PicType == 3 ) + $this->drawFilledRectangle($X+$this->ShadowX,$Y+$this->ShadowY,$X+$Width+$this->ShadowX,$Y+$Height+$this->ShadowY,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa)); + else + { + $TranparentID = imagecolortransparent($Raster); + for ($Xc=0;$Xc<=$Width-1;$Xc++) + { + for ($Yc=0;$Yc<=$Height-1;$Yc++) + { + $RGBa = imagecolorat($Raster,$Xc,$Yc); + $Values = imagecolorsforindex($Raster,$RGBa); + if ( $Values["alpha"] < 120 ) + { + $AlphaFactor = floor(($this->Shadowa / 100) * ((100 / 127) * (127-$Values["alpha"]))); + $this->drawAlphaPixel($X+$Xc+$this->ShadowX,$Y+$Yc+$this->ShadowY,$AlphaFactor,$this->ShadowR,$this->ShadowG,$this->ShadowB); + } + } + } + } + } + $this->Shadow = $RestoreShadow; + + imagecopy($this->Picture,$Raster,$X,$Y,0,0,$Width,$Height); + imagedestroy($Raster); + } + } + + /* Draw an arrow */ + function drawArrow($X1,$Y1,$X2,$Y2,$Format="") + { + $FillR = isset($Format["FillR"]) ? $Format["FillR"] : 0; + $FillG = isset($Format["FillG"]) ? $Format["FillG"] : 0; + $FillB = isset($Format["FillB"]) ? $Format["FillB"] : 0; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $FillR; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $FillG; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $FillB; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Size = isset($Format["Size"]) ? $Format["Size"] : 10; + $Ratio = isset($Format["Ratio"]) ? $Format["Ratio"] : .5; + $TwoHeads = isset($Format["TwoHeads"]) ? $Format["TwoHeads"] : FALSE; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : FALSE; + + /* Calculate the line angle */ + $Angle = $this->getAngle($X1,$Y1,$X2,$Y2); + + /* Override Shadow support, this will be managed internally */ + $RestoreShadow = $this->Shadow; + if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 ) + { + $this->Shadow = FALSE; + $this->drawArrow($X1+$this->ShadowX,$Y1+$this->ShadowY,$X2+$this->ShadowX,$Y2+$this->ShadowY,array("FillR"=>$this->ShadowR,"FillG"=>$this->ShadowG,"FillB"=>$this->ShadowB,"Alpha"=>$this->Shadowa,"Size"=>$Size,"Ratio"=>$Ratio,"TwoHeads"=>$TwoHeads,"Ticks"=>$Ticks)); + } + + /* Draw the 1st Head */ + $TailX = cos(($Angle-180)*PI/180)*$Size+$X2; + $TailY = sin(($Angle-180)*PI/180)*$Size+$Y2; + + $Points = ""; + $Points[] = $X2; $Points[] = $Y2; + $Points[] = cos(($Angle-90)*PI/180)*$Size*$Ratio+$TailX; $Points[] = sin(($Angle-90)*PI/180)*$Size*$Ratio+$TailY; + $Points[] = cos(($Angle-270)*PI/180)*$Size*$Ratio+$TailX; $Points[] = sin(($Angle-270)*PI/180)*$Size*$Ratio+$TailY; + $Points[] = $X2; $Points[] = $Y2; + + /* Visual correction */ + if ($Angle == 180 || $Angle == 360 ) { $Points[4] = $Points[2]; } + if ($Angle == 90 || $Angle == 270 ) { $Points[5] = $Points[3]; } + + $ArrowColor = $this->allocateColor($this->Picture,$FillR,$FillG,$FillB,$Alpha); + ImageFilledPolygon($this->Picture,$Points,4,$ArrowColor); + + $this->drawLine($Points[0],$Points[1],$Points[2],$Points[3],array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha)); + $this->drawLine($Points[2],$Points[3],$Points[4],$Points[5],array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha)); + $this->drawLine($Points[0],$Points[1],$Points[4],$Points[5],array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha)); + + /* Draw the second head */ + if ( $TwoHeads ) + { + $Angle = $this->getAngle($X2,$Y2,$X1,$Y1); + + $TailX2 = cos(($Angle-180)*PI/180)*$Size+$X1; + $TailY2 = sin(($Angle-180)*PI/180)*$Size+$Y1; + + $Points = ""; + $Points[] = $X1; $Points[] = $Y1; + $Points[] = cos(($Angle-90)*PI/180)*$Size*$Ratio+$TailX2; $Points[] = sin(($Angle-90)*PI/180)*$Size*$Ratio+$TailY2; + $Points[] = cos(($Angle-270)*PI/180)*$Size*$Ratio+$TailX2; $Points[] = sin(($Angle-270)*PI/180)*$Size*$Ratio+$TailY2; + $Points[] = $X1; $Points[] = $Y1; + + /* Visual correction */ + if ($Angle == 180 || $Angle == 360 ) { $Points[4] = $Points[2]; } + if ($Angle == 90 || $Angle == 270 ) { $Points[5] = $Points[3]; } + + $ArrowColor = $this->allocateColor($this->Picture,$FillR,$FillG,$FillB,$Alpha); + ImageFilledPolygon($this->Picture,$Points,4,$ArrowColor); + + $this->drawLine($Points[0],$Points[1],$Points[2],$Points[3],array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha)); + $this->drawLine($Points[2],$Points[3],$Points[4],$Points[5],array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha)); + $this->drawLine($Points[0],$Points[1],$Points[4],$Points[5],array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha)); + + $this->drawLine($TailX,$TailY,$TailX2,$TailY2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); + } + else + $this->drawLine($X1,$Y1,$TailX,$TailY,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); + + /* Re-enable shadows */ + $this->Shadow = $RestoreShadow; + } + + /* Draw a label with associated arrow */ + function drawArrowLabel($X1,$Y1,$Text,$Format="") + { + $FillR = isset($Format["FillR"]) ? $Format["FillR"] : 0; + $FillG = isset($Format["FillG"]) ? $Format["FillG"] : 0; + $FillB = isset($Format["FillB"]) ? $Format["FillB"] : 0; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $FillR; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $FillG; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $FillB; + $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->FontName; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Length = isset($Format["Length"]) ? $Format["Length"] : 50; + $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 315; + $Size = isset($Format["Size"]) ? $Format["Size"] : 10; + $Position = isset($Format["Position"]) ? $Format["Position"] : POSITION_TOP; + $RoundPos = isset($Format["RoundPos"]) ? $Format["RoundPos"] : FALSE; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL; + + $Angle = $Angle % 360; + + $X2 = sin(($Angle+180)*PI/180)*$Length+$X1; + $Y2 = cos(($Angle+180)*PI/180)*$Length+$Y1; + + if ( $RoundPos && $Angle > 0 && $Angle < 180 ) { $Y2 = ceil($Y2); } + if ( $RoundPos && $Angle > 180 ) { $Y2 = floor($Y2); } + + $this->drawArrow($X2,$Y2,$X1,$Y1,$Format); + + $Size = imagettfbbox($FontSize,0,$FontName,$Text); + $TxtWidth = max(abs($Size[2]-$Size[0]),abs($Size[0]-$Size[6])); + $TxtHeight = max(abs($Size[1]-$Size[7]),abs($Size[3]-$Size[1])); + + if ( $Angle > 0 && $Angle < 180 ) + { + $this->drawLine($X2,$Y2,$X2-$TxtWidth,$Y2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); + if ( $Position == POSITION_TOP ) + $this->drawText($X2,$Y2-2,$Text,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha,"Align"=>TEXT_ALIGN_BOTTOMRIGHT)); + else + $this->drawText($X2,$Y2+4,$Text,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha,"Align"=>TEXT_ALIGN_TOPRIGHT)); + } + else + { + $this->drawLine($X2,$Y2,$X2+$TxtWidth,$Y2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); + if ( $Position == POSITION_TOP ) + $this->drawText($X2,$Y2-2,$Text,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha)); + else + $this->drawText($X2,$Y2+4,$Text,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha,"Align"=>TEXT_ALIGN_TOPLEFT)); + } + } + + /* Draw a progress bar filled with specified % */ + function drawProgress($X,$Y,$Percent,$Format="") + { + if ( $Percent > 100 ) { $Percent = 100; } + if ( $Percent < 0 ) { $Percent = 0; } + + $Width = isset($Format["Width"]) ? $Format["Width"] : 200; + $Height = isset($Format["Height"]) ? $Format["Height"] : 20; + $Orientation = isset($Format["Orientation"]) ? $Format["Orientation"] : ORIENTATION_HORIZONTAL; + $ShowLabel = isset($Format["ShowLabel"]) ? $Format["ShowLabel"] : FALSE; + $LabelPos = isset($Format["LabelPos"]) ? $Format["LabelPos"] : LABEL_POS_INSIDE; + $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 10; + $R = isset($Format["R"]) ? $Format["R"] : 130; + $G = isset($Format["G"]) ? $Format["G"] : 130; + $B = isset($Format["B"]) ? $Format["B"] : 130; + $RFade = isset($Format["RFade"]) ? $Format["RFade"] : -1; + $GFade = isset($Format["GFade"]) ? $Format["GFade"] : -1; + $BFade = isset($Format["BFade"]) ? $Format["BFade"] : -1; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B; + $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 0; + $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 0; + $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 0; + $BoxBackR = isset($Format["BoxBackR"]) ? $Format["BoxBackR"] : 255; + $BoxBackG = isset($Format["BoxBackG"]) ? $Format["BoxBackG"] : 255; + $BoxBackB = isset($Format["BoxBackB"]) ? $Format["BoxBackB"] : 255; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; + $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : NULL; + $NoAngle = isset($Format["NoAngle"]) ? $Format["NoAngle"] : FALSE; + + if ( $RFade != -1 && $GFade != -1 && $BFade != -1 ) + { + $RFade = (($RFade-$R)/100)*$Percent+$R; + $GFade = (($GFade-$G)/100)*$Percent+$G; + $BFade = (($BFade-$B)/100)*$Percent+$B; + } + + if ( $Surrounding != NULL ) { $BorderR = $R + $Surrounding; $BorderG = $G + $Surrounding; $BorderB = $B + $Surrounding; } + if ( $BoxSurrounding != NULL ) { $BoxBorderR = $BoxBackR + $Surrounding; $BoxBorderG = $BoxBackG + $Surrounding; $BoxBorderB = $BoxBackB + $Surrounding; } + + if ( $Orientation == ORIENTATION_VERTICAL ) + { + $InnerHeight = (($Height-2)/100)*$Percent; + $this->drawFilledRectangle($X,$Y,$X+$Width,$Y-$Height,array("R"=>$BoxBackR,"G"=>$BoxBackG,"B"=>$BoxBackB,"BorderR"=>$BoxBorderR,"BorderG"=>$BoxBorderG,"BorderB"=>$BoxBorderB,"NoAngle"=>$NoAngle)); + + $RestoreShadow = $this->Shadow; $this->Shadow = FALSE; + if ( $RFade != -1 && $GFade != -1 && $BFade != -1 ) + { + $GradientOptions = array("StartR"=>$RFade,"StartG"=>$GFade,"StartB"=>$BFade,"EndR"=>$R,"EndG"=>$G,"EndB"=>$B); + $this->drawGradientArea($X+1,$Y-1,$X+$Width-1,$Y-$InnerHeight,DIRECTION_VERTICAL,$GradientOptions); + + if ( $Surrounding ) + $this->drawRectangle($X+1,$Y-1,$X+$Width-1,$Y-$InnerHeight,array("R"=>255,"G"=>255,"B"=>255,"Alpha"=>$Surrounding)); + } + else + $this->drawFilledRectangle($X+1,$Y-1,$X+$Width-1,$Y-$InnerHeight,array("R"=>$R,"G"=>$G,"B"=>$B,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB)); + + $this->Shadow = $RestoreShadow; + + if ( $ShowLabel && $LabelPos == LABEL_POS_BOTTOM ) { $this->drawText($X+($Width/2),$Y+$Margin,$Percent."%",array("Align"=>TEXT_ALIGN_TOPMIDDLE)); } + if ( $ShowLabel && $LabelPos == LABEL_POS_TOP ) { $this->drawText($X+($Width/2),$Y-$Height-$Margin,$Percent."%",array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); } + if ( $ShowLabel && $LabelPos == LABEL_POS_INSIDE ) { $this->drawText($X+($Width/2),$Y-$InnerHeight-$Margin,$Percent."%",array("Align"=>TEXT_ALIGN_MIDDLELEFT,"Angle"=>90)); } + if ( $ShowLabel && $LabelPos == LABEL_POS_CENTER ) { $this->drawText($X+($Width/2),$Y-($Height/2),$Percent."%",array("Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"Angle"=>90)); } + } + else + { + if ( $Percent == 100 ) + $InnerWidth = $Width-1; + else + $InnerWidth = (($Width-2)/100)*$Percent; + + $this->drawFilledRectangle($X,$Y,$X+$Width,$Y+$Height,array("R"=>$BoxBackR,"G"=>$BoxBackG,"B"=>$BoxBackB,"BorderR"=>$BoxBorderR,"BorderG"=>$BoxBorderG,"BorderB"=>$BoxBorderB,"NoAngle"=>$NoAngle)); + + $RestoreShadow = $this->Shadow; $this->Shadow = FALSE; + if ( $RFade != -1 && $GFade != -1 && $BFade != -1 ) + { + $GradientOptions = array("StartR"=>$R,"StartG"=>$G,"StartB"=>$B,"EndR"=>$RFade,"EndG"=>$GFade,"EndB"=>$BFade); + $this->drawGradientArea($X+1,$Y+1,$X+$InnerWidth,$Y+$Height-1,DIRECTION_HORIZONTAL,$GradientOptions); + + if ( $Surrounding ) + $this->drawRectangle($X+1,$Y+1,$X+$InnerWidth,$Y+$Height-1,array("R"=>255,"G"=>255,"B"=>255,"Alpha"=>$Surrounding)); + } + else + $this->drawFilledRectangle($X+1,$Y+1,$X+$InnerWidth,$Y+$Height-1,array("R"=>$R,"G"=>$G,"B"=>$B,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB)); + + $this->Shadow = $RestoreShadow; + + if ( $ShowLabel && $LabelPos == LABEL_POS_LEFT ) { $this->drawText($X-$Margin,$Y+($Height/2),$Percent."%",array("Align"=>TEXT_ALIGN_MIDDLERIGHT)); } + if ( $ShowLabel && $LabelPos == LABEL_POS_RIGHT ) { $this->drawText($X+$Width+$Margin,$Y+($Height/2),$Percent."%",array("Align"=>TEXT_ALIGN_MIDDLELEFT)); } + if ( $ShowLabel && $LabelPos == LABEL_POS_CENTER ) { $this->drawText($X+($Width/2),$Y+($Height/2),$Percent."%",array("Align"=>TEXT_ALIGN_MIDDLEMIDDLE)); } + if ( $ShowLabel && $LabelPos == LABEL_POS_INSIDE ) { $this->drawText($X+$InnerWidth+$Margin,$Y+($Height/2),$Percent."%",array("Align"=>TEXT_ALIGN_MIDDLELEFT)); } + } + } + + /* Get the legend box size */ + function getLegendSize($Format="") + { + $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->FontName; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize; + $BoxSize = isset($Format["BoxSize"]) ? $Format["BoxSize"] : 5; + $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5; + $Style = isset($Format["Style"]) ? $Format["Style"] : LEGEND_ROUND; + $Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL; + $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 5; + $BoxHeight = isset($Format["BoxHeight"]) ? $Format["BoxHeight"] : 5; + $IconAreaWidth = isset($Format["IconAreaWidth"]) ? $Format["IconAreaWidth"] : $BoxWidth; + $IconAreaHeight = isset($Format["IconAreaHeight"]) ? $Format["IconAreaHeight"] : $BoxHeight; + $XSpacing = isset($Format["XSpacing"]) ? $Format["XSpacing"] : 5; + + $Data = $this->DataSet->getData(); + + foreach($Data["Series"] as $SerieName => $Serie) + { + if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] && isset($Serie["Picture"])) + { + list($PicWidth,$PicHeight) = $this->getPicInfo($Serie["Picture"]); + if ( $IconAreaWidth < $PicWidth ) { $IconAreaWidth = $PicWidth; } + if ( $IconAreaHeight < $PicHeight ) { $IconAreaHeight = $PicHeight; } + } + } + + $YStep = max($this->FontSize,$IconAreaHeight) + 5; + $XStep = $IconAreaWidth + 5; + $XStep = $XSpacing; + + $X=100; $Y=100; + + $Boundaries = ""; $Boundaries["L"] = $X; $Boundaries["T"] = $Y; $Boundaries["R"] = 0; $Boundaries["B"] = 0; $vY = $Y; $vX = $X; + foreach($Data["Series"] as $SerieName => $Serie) + { + if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) + { + if ( $Mode == LEGEND_VERTICAL ) + { + $BoxArray = $this->getTextBox($vX+$IconAreaWidth+4,$vY+$IconAreaHeight/2,$FontName,$FontSize,0,$Serie["Description"]); + + if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$IconAreaHeight/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$IconAreaHeight/2; } + if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; } + if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$IconAreaHeight/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$IconAreaHeight/2; } + + $Lines = preg_split("/\n/",$Serie["Description"]); + $vY = $vY + max($this->FontSize*count($Lines),$IconAreaHeight) + 5; + } + elseif ( $Mode == LEGEND_HORIZONTAL ) + { + $Lines = preg_split("/\n/",$Serie["Description"]); + $Width = ""; + foreach($Lines as $Key => $Value) + { + $BoxArray = $this->getTextBox($vX+$IconAreaWidth+6,$Y+$IconAreaHeight/2+(($this->FontSize+3)*$Key),$FontName,$FontSize,0,$Value); + + if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$IconAreaHeight/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$IconAreaHeight/2; } + if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; } + if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$IconAreaHeight/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$IconAreaHeight/2; } + + $Width[] = $BoxArray[1]["X"]; + } + + $vX=max($Width)+$XStep; + } + } + } + $vY=$vY-$YStep; $vX=$vX-$XStep; + + $TopOffset = $Y - $Boundaries["T"]; + if ( $Boundaries["B"]-($vY+$IconAreaHeight) < $TopOffset ) { $Boundaries["B"] = $vY+$IconAreaHeight+$TopOffset; } + + $Width = ($Boundaries["R"]+$Margin) - ($Boundaries["L"]-$Margin); + $Height = ($Boundaries["B"]+$Margin) - ($Boundaries["T"]-$Margin); + + return(array("Width"=>$Width,"Height"=>$Height)); + } + + /* Draw the legend of the active series */ + function drawLegend($X,$Y,$Format="") + { + $Family = isset($Format["Family"]) ? $Format["Family"] : LEGEND_FAMILY_BOX; + $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->FontName; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize; + $FontR = isset($Format["FontR"]) ? $Format["FontR"] : $this->FontColorR; + $FontG = isset($Format["FontG"]) ? $Format["FontG"] : $this->FontColorG; + $FontB = isset($Format["FontB"]) ? $Format["FontB"] : $this->FontColorB; + $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 5; + $BoxHeight = isset($Format["BoxHeight"]) ? $Format["BoxHeight"] : 5; + $IconAreaWidth = isset($Format["IconAreaWidth"]) ? $Format["IconAreaWidth"] : $BoxWidth; + $IconAreaHeight = isset($Format["IconAreaHeight"]) ? $Format["IconAreaHeight"] : $BoxHeight; + $XSpacing = isset($Format["XSpacing"]) ? $Format["XSpacing"] : 5; + $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5; + $R = isset($Format["R"]) ? $Format["R"] : 200; + $G = isset($Format["G"]) ? $Format["G"] : 200; + $B = isset($Format["B"]) ? $Format["B"] : 200; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; + $Style = isset($Format["Style"]) ? $Format["Style"] : LEGEND_ROUND; + $Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL; + + if ( $Surrounding != NULL ) { $BorderR = $R + $Surrounding; $BorderG = $G + $Surrounding; $BorderB = $B + $Surrounding; } + + $Data = $this->DataSet->getData(); + + foreach($Data["Series"] as $SerieName => $Serie) + { + if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] && isset($Serie["Picture"])) + { + list($PicWidth,$PicHeight) = $this->getPicInfo($Serie["Picture"]); + if ( $IconAreaWidth < $PicWidth ) { $IconAreaWidth = $PicWidth; } + if ( $IconAreaHeight < $PicHeight ) { $IconAreaHeight = $PicHeight; } + } + } + + $YStep = max($this->FontSize,$IconAreaHeight) + 5; + $XStep = $IconAreaWidth + 5; + $XStep = $XSpacing; + + $Boundaries = ""; $Boundaries["L"] = $X; $Boundaries["T"] = $Y; $Boundaries["R"] = 0; $Boundaries["B"] = 0; $vY = $Y; $vX = $X; + foreach($Data["Series"] as $SerieName => $Serie) + { + if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) + { + if ( $Mode == LEGEND_VERTICAL ) + { + $BoxArray = $this->getTextBox($vX+$IconAreaWidth+4,$vY+$IconAreaHeight/2,$FontName,$FontSize,0,$Serie["Description"]); + + if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$IconAreaHeight/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$IconAreaHeight/2; } + if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; } + if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$IconAreaHeight/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$IconAreaHeight/2; } + + $Lines = preg_split("/\n/",$Serie["Description"]); + $vY = $vY + max($this->FontSize*count($Lines),$IconAreaHeight) + 5; + } + elseif ( $Mode == LEGEND_HORIZONTAL ) + { + $Lines = preg_split("/\n/",$Serie["Description"]); + $Width = ""; + foreach($Lines as $Key => $Value) + { + $BoxArray = $this->getTextBox($vX+$IconAreaWidth+6,$Y+$IconAreaHeight/2+(($this->FontSize+3)*$Key),$FontName,$FontSize,0,$Value); + + if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$IconAreaHeight/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$IconAreaHeight/2; } + if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; } + if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$IconAreaHeight/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$IconAreaHeight/2; } + + $Width[] = $BoxArray[1]["X"]; + } + + $vX=max($Width)+$XStep; + } + } + } + $vY=$vY-$YStep; $vX=$vX-$XStep; + + $TopOffset = $Y - $Boundaries["T"]; + if ( $Boundaries["B"]-($vY+$IconAreaHeight) < $TopOffset ) { $Boundaries["B"] = $vY+$IconAreaHeight+$TopOffset; } + + if ( $Style == LEGEND_ROUND ) + $this->drawRoundedFilledRectangle($Boundaries["L"]-$Margin,$Boundaries["T"]-$Margin,$Boundaries["R"]+$Margin,$Boundaries["B"]+$Margin,$Margin,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB)); + elseif ( $Style == LEGEND_BOX ) + $this->drawFilledRectangle($Boundaries["L"]-$Margin,$Boundaries["T"]-$Margin,$Boundaries["R"]+$Margin,$Boundaries["B"]+$Margin,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB)); + + $RestoreShadow = $this->Shadow; $this->Shadow = FALSE; + foreach($Data["Series"] as $SerieName => $Serie) + { + if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) + { + $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; + $Ticks = $Serie["Ticks"]; $Weight = $Serie["Weight"]; + + if ( isset($Serie["Picture"]) ) + { + $Picture = $Serie["Picture"]; + list($PicWidth,$PicHeight) = $this->getPicInfo($Picture); + $PicX = $X+$IconAreaWidth/2; $PicY = $Y+$IconAreaHeight/2; + + $this->drawFromPNG($PicX-$PicWidth/2,$PicY-$PicHeight/2,$Picture); + } + else + { + if ( $Family == LEGEND_FAMILY_BOX ) + { + if ( $BoxWidth != $IconAreaWidth ) { $XOffset = floor(($IconAreaWidth-$BoxWidth)/2); } else { $XOffset = 0; } + if ( $BoxHeight != $IconAreaHeight ) { $YOffset = floor(($IconAreaHeight-$BoxHeight)/2); } else { $YOffset = 0; } + + $this->drawFilledRectangle($X+1+$XOffset,$Y+1+$YOffset,$X+$BoxWidth+$XOffset+1,$Y+$BoxHeight+1+$YOffset,array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20)); + $this->drawFilledRectangle($X+$XOffset,$Y+$YOffset,$X+$BoxWidth+$XOffset,$Y+$BoxHeight+$YOffset,array("R"=>$R,"G"=>$G,"B"=>$B,"Surrounding"=>20)); + } + elseif ( $Family == LEGEND_FAMILY_CIRCLE ) + { + $this->drawFilledCircle($X+1+$IconAreaWidth/2,$Y+1+$IconAreaHeight/2,min($IconAreaHeight/2,$IconAreaWidth/2),array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20)); + $this->drawFilledCircle($X+$IconAreaWidth/2,$Y+$IconAreaHeight/2,min($IconAreaHeight/2,$IconAreaWidth/2),array("R"=>$R,"G"=>$G,"B"=>$B,"Surrounding"=>20)); + } + elseif ( $Family == LEGEND_FAMILY_LINE ) + { + $this->drawLine($X+1,$Y+1+$IconAreaHeight/2,$X+1+$IconAreaWidth,$Y+1+$IconAreaHeight/2,array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20,"Ticks"=>$Ticks,"Weight"=>$Weight)); + $this->drawLine($X,$Y+$IconAreaHeight/2,$X+$IconAreaWidth,$Y+$IconAreaHeight/2,array("R"=>$R,"G"=>$G,"B"=>$B,"Ticks"=>$Ticks,"Weight"=>$Weight)); + } + } + + if ( $Mode == LEGEND_VERTICAL ) + { + $Lines = preg_split("/\n/",$Serie["Description"]); + foreach($Lines as $Key => $Value) + $this->drawText($X+$IconAreaWidth+4,$Y+$IconAreaHeight/2+(($this->FontSize+3)*$Key),$Value,array("R"=>$FontR,"G"=>$FontG,"B"=>$FontB,"Align"=>TEXT_ALIGN_MIDDLELEFT,"FontSize"=>$FontSize,"FontName"=>$FontName)); + + $Y=$Y+max($this->FontSize*count($Lines),$IconAreaHeight) + 5; + } + elseif ( $Mode == LEGEND_HORIZONTAL ) + { + $Lines = preg_split("/\n/",$Serie["Description"]); + $Width = ""; + foreach($Lines as $Key => $Value) + { + $BoxArray = $this->drawText($X+$IconAreaWidth+4,$Y+$IconAreaHeight/2+(($this->FontSize+3)*$Key),$Value,array("R"=>$FontR,"G"=>$FontG,"B"=>$FontB,"Align"=>TEXT_ALIGN_MIDDLELEFT,"FontSize"=>$FontSize,"FontName"=>$FontName)); + $Width[] = $BoxArray[1]["X"]; + } + $X=max($Width)+2+$XStep; + } + } + } + + + $this->Shadow = $RestoreShadow; + } + + function drawScale($Format="") + { + $Pos = isset($Format["Pos"]) ? $Format["Pos"] : SCALE_POS_LEFTRIGHT; + $Floating = isset($Format["Floating"]) ? $Format["Floating"] : FALSE; + $Mode = isset($Format["Mode"]) ? $Format["Mode"] : SCALE_MODE_FLOATING; + $RemoveXAxis = isset($Format["RemoveXAxis"]) ? $Format["RemoveXAxis"] : FALSE; + $MinDivHeight = isset($Format["MinDivHeight"]) ? $Format["MinDivHeight"] : 20; + $Factors = isset($Format["Factors"]) ? $Format["Factors"] : array(1,2,5); + $ManualScale = isset($Format["ManualScale"]) ? $Format["ManualScale"] : array("0"=>array("Min"=>-100,"Max"=>100)); + $XMargin = isset($Format["XMargin"]) ? $Format["XMargin"] : AUTO; + $YMargin = isset($Format["YMargin"]) ? $Format["YMargin"] : 0; + $ScaleSpacing = isset($Format["ScaleSpacing"]) ? $Format["ScaleSpacing"] : 15; + $InnerTickWidth = isset($Format["InnerTickWidth"]) ? $Format["InnerTickWidth"] : 2; + $OuterTickWidth = isset($Format["OuterTickWidth"]) ? $Format["OuterTickWidth"] : 2; + $DrawXLines = isset($Format["DrawXLines"]) ? $Format["DrawXLines"] : TRUE; + $DrawYLines = isset($Format["DrawYLines"]) ? $Format["DrawYLines"] : ALL; + $GridTicks = isset($Format["GridTicks"]) ? $Format["GridTicks"] : 4; + $GridR = isset($Format["GridR"]) ? $Format["GridR"] : 255; + $GridG = isset($Format["GridG"]) ? $Format["GridG"] : 255; + $GridB = isset($Format["GridB"]) ? $Format["GridB"] : 255; + $GridAlpha = isset($Format["GridAlpha"]) ? $Format["GridAlpha"] : 40; + $AxisRo = isset($Format["AxisR"]) ? $Format["AxisR"] : 0; + $AxisGo = isset($Format["AxisG"]) ? $Format["AxisG"] : 0; + $AxisBo = isset($Format["AxisB"]) ? $Format["AxisB"] : 0; + $AxisAlpha = isset($Format["AxisAlpha"]) ? $Format["AxisAlpha"] : 100; + $TickRo = isset($Format["TickR"]) ? $Format["TickR"] : 0; + $TickGo = isset($Format["TickG"]) ? $Format["TickG"] : 0; + $TickBo = isset($Format["TickB"]) ? $Format["TickB"] : 0; + $TickAlpha = isset($Format["TickAlpha"]) ? $Format["TickAlpha"] : 100; + $DrawSubTicks = isset($Format["DrawSubTicks"]) ? $Format["DrawSubTicks"] : FALSE; + $InnerSubTickWidth = isset($Format["InnerSubTickWidth"]) ? $Format["InnerSubTickWidth"] : 0; + $OuterSubTickWidth = isset($Format["OuterSubTickWidth"]) ? $Format["OuterSubTickWidth"] : 2; + $SubTickR = isset($Format["SubTickR"]) ? $Format["SubTickR"] : 255; + $SubTickG = isset($Format["SubTickG"]) ? $Format["SubTickG"] : 0; + $SubTickB = isset($Format["SubTickB"]) ? $Format["SubTickB"] : 0; + $SubTickAlpha = isset($Format["SubTickAlpha"]) ? $Format["SubTickAlpha"] : 100; + $AutoAxisLabels = isset($Format["AutoAxisLabels"]) ? $Format["AutoAxisLabels"] : TRUE; + $XReleasePercent = isset($Format["XReleasePercent"]) ? $Format["XReleasePercent"] : 1; + $DrawArrows = isset($Format["DrawArrows"]) ? $Format["DrawArrows"] : FALSE; + $ArrowSize = isset($Format["ArrowSize"]) ? $Format["ArrowSize"] : 8; + $CycleBackground = isset($Format["CycleBackground"]) ? $Format["CycleBackground"] : FALSE; + $BackgroundR1 = isset($Format["BackgroundR1"]) ? $Format["BackgroundR1"] : 255; + $BackgroundG1 = isset($Format["BackgroundG1"]) ? $Format["BackgroundG1"] : 255; + $BackgroundB1 = isset($Format["BackgroundB1"]) ? $Format["BackgroundB1"] : 255; + $BackgroundAlpha1 = isset($Format["BackgroundAlpha1"]) ? $Format["BackgroundAlpha1"] : 20; + $BackgroundR2 = isset($Format["BackgroundR2"]) ? $Format["BackgroundR2"] : 230; + $BackgroundG2 = isset($Format["BackgroundG2"]) ? $Format["BackgroundG2"] : 230; + $BackgroundB2 = isset($Format["BackgroundB2"]) ? $Format["BackgroundB2"] : 230; + $BackgroundAlpha2 = isset($Format["BackgroundAlpha2"]) ? $Format["BackgroundAlpha2"] : 20; + $LabelingMethod = isset($Format["LabelingMethod"]) ? $Format["LabelingMethod"] : LABELING_ALL; + $LabelSkip = isset($Format["LabelSkip"]) ? $Format["LabelSkip"] : 0; + $LabelRotation = isset($Format["LabelRotation"]) ? $Format["LabelRotation"] : 0; + $RemoveSkippedAxis = isset($Format["RemoveSkippedAxis"]) ? $Format["RemoveSkippedAxis"] : FALSE; + $SkippedAxisTicks = isset($Format["SkippedAxisTicks"]) ? $Format["SkippedAxisTicks"] : $GridTicks+2; + $SkippedAxisR = isset($Format["SkippedAxisR"]) ? $Format["SkippedAxisR"] : $GridR; + $SkippedAxisG = isset($Format["SkippedAxisG"]) ? $Format["SkippedAxisG"] : $GridG; + $SkippedAxisB = isset($Format["SkippedAxisB"]) ? $Format["SkippedAxisB"] : $GridB; + $SkippedAxisAlpha = isset($Format["SkippedAxisAlpha"]) ? $Format["SkippedAxisAlpha"] : $GridAlpha-30; + $SkippedTickR = isset($Format["SkippedTickR"]) ? $Format["SkippedTickR"] : $TickRo; + $SkippedTickG = isset($Format["SkippedTickG"]) ? $Format["SkippedTickG"] : $TickGo; + $SkippedTickB = isset($Format["SkippedTicksB"]) ? $Format["SkippedTickB"] : $TickBo; + $SkippedTickAlpha = isset($Format["SkippedTickAlpha"]) ? $Format["SkippedTickAlpha"] : $TickAlpha-80; + $SkippedInnerTickWidth = isset($Format["SkippedInnerTickWidth"]) ? $Format["SkippedInnerTickWidth"] : 0; + $SkippedOuterTickWidth = isset($Format["SkippedOuterTickWidth"]) ? $Format["SkippedOuterTickWidth"] : 2; + + /* Floating scale require X & Y margins to be set manually */ + if ( $Floating && ( $XMargin == AUTO || $YMargin == 0 ) ) { $Floating = FALSE; } + + /* Skip a NOTICE event in case of an empty array */ + if ( $DrawYLines == NONE || $DrawYLines == FALSE ) { $DrawYLines = array("zarma"=>"31"); } + + /* Define the color for the skipped elements */ + $SkippedAxisColor = array("R"=>$SkippedAxisR,"G"=>$SkippedAxisG,"B"=>$SkippedAxisB,"Alpha"=>$SkippedAxisAlpha,"Ticks"=>$SkippedAxisTicks); + $SkippedTickColor = array("R"=>$SkippedTickR,"G"=>$SkippedTickG,"B"=>$SkippedTickB,"Alpha"=>$SkippedTickAlpha); + + $Data = $this->DataSet->getData(); + if ( isset($Data["Abscissa"]) ) { $Abscissa = $Data["Abscissa"]; } else { $Abscissa = NULL; } + + /* Unset the abscissa axis, needed if we display multiple charts on the same picture */ + if ( $Abscissa != NULL ) + { + foreach($Data["Axis"] as $AxisID => $Parameters) + { if ($Parameters["Identity"] == AXIS_X) { unset($Data["Axis"][$AxisID]); } } + } + + /* Build the scale settings */ + $GotXAxis = FALSE; + foreach($Data["Axis"] as $AxisID => $AxisParameter) + { + if ( $AxisParameter["Identity"] == AXIS_X ) { $GotXAxis = TRUE; } + + if ( $Pos == SCALE_POS_LEFTRIGHT && $AxisParameter["Identity"] == AXIS_Y) + { $Height = $this->GraphAreaY2-$this->GraphAreaY1 - $YMargin*2; } + elseif ( $Pos == SCALE_POS_LEFTRIGHT && $AxisParameter["Identity"] == AXIS_X) + { $Height = $this->GraphAreaX2-$this->GraphAreaX1; } + elseif ( $Pos == SCALE_POS_TOPBOTTOM && $AxisParameter["Identity"] == AXIS_Y) + { $Height = $this->GraphAreaX2-$this->GraphAreaX1 - $YMargin*2;; } + else + { $Height = $this->GraphAreaY2-$this->GraphAreaY1; } + + $AxisMin = ABSOLUTE_MAX; $AxisMax = OUT_OF_SIGHT; + if ( $Mode == SCALE_MODE_FLOATING || $Mode == SCALE_MODE_START0 ) + { + foreach($Data["Series"] as $SerieID => $SerieParameter) + { + if ( $SerieParameter["Axis"] == $AxisID && $Data["Series"][$SerieID]["isDrawable"] && $Data["Abscissa"] != $SerieID) + { + $AxisMax = max($AxisMax,$Data["Series"][$SerieID]["Max"]); + $AxisMin = min($AxisMin,$Data["Series"][$SerieID]["Min"]); + } + } + $AutoMargin = (($AxisMax-$AxisMin)/100)*$XReleasePercent; + + $Data["Axis"][$AxisID]["Min"] = $AxisMin-$AutoMargin; $Data["Axis"][$AxisID]["Max"] = $AxisMax+$AutoMargin; + if ( $Mode == SCALE_MODE_START0 ) { $Data["Axis"][$AxisID]["Min"] = 0; } + } + elseif ( $Mode == SCALE_MODE_MANUAL ) + { + if ( isset($ManualScale[$AxisID]["Min"]) && isset($ManualScale[$AxisID]["Max"]) ) + { + $Data["Axis"][$AxisID]["Min"] = $ManualScale[$AxisID]["Min"]; + $Data["Axis"][$AxisID]["Max"] = $ManualScale[$AxisID]["Max"]; + } + else + { echo "Manual scale boundaries not set."; exit(); } + } + elseif ( $Mode == SCALE_MODE_ADDALL || $Mode == SCALE_MODE_ADDALL_START0 ) + { + $Series = ""; + foreach($Data["Series"] as $SerieID => $SerieParameter) + { if ( $SerieParameter["Axis"] == $AxisID && $SerieParameter["isDrawable"] && $Data["Abscissa"] != $SerieID ) { $Series[$SerieID] = count($Data["Series"][$SerieID]["Data"]); } } + + for ($ID=0;$ID<=max($Series)-1;$ID++) + { + $PointMin = 0; $PointMax = 0; + foreach($Series as $SerieID => $ValuesCount ) + { + if (isset($Data["Series"][$SerieID]["Data"][$ID]) && $Data["Series"][$SerieID]["Data"][$ID] != NULL ) + { + $Value = $Data["Series"][$SerieID]["Data"][$ID]; + if ( $Value > 0 ) { $PointMax = $PointMax + $Value; } else { $PointMin = $PointMin + $Value; } + } + } + $AxisMax = max($AxisMax,$PointMax); + $AxisMin = min($AxisMin,$PointMin); + } + $AutoMargin = (($AxisMax-$AxisMin)/100)*$XReleasePercent; + $Data["Axis"][$AxisID]["Min"] = $AxisMin-$AutoMargin; $Data["Axis"][$AxisID]["Max"] = $AxisMax+$AutoMargin; + } + $MaxDivs = floor($Height/$MinDivHeight); + + if ( $Mode == SCALE_MODE_ADDALL_START0 ) { $Data["Axis"][$AxisID]["Min"] = 0; } + + $Scale = $this->computeScale($Data["Axis"][$AxisID]["Min"],$Data["Axis"][$AxisID]["Max"],$MaxDivs,$Factors,$AxisID); + + $Data["Axis"][$AxisID]["Margin"] = $AxisParameter["Identity"] == AXIS_X ? $XMargin : $YMargin; + $Data["Axis"][$AxisID]["ScaleMin"] = $Scale["XMin"]; + $Data["Axis"][$AxisID]["ScaleMax"] = $Scale["XMax"]; + $Data["Axis"][$AxisID]["Rows"] = $Scale["Rows"]; + $Data["Axis"][$AxisID]["RowHeight"] = $Scale["RowHeight"]; + + if ( isset($Scale["Format"]) ) { $Data["Axis"][$AxisID]["Format"] = $Scale["Format"]; } + + if ( !isset($Data["Axis"][$AxisID]["Display"]) ) { $Data["Axis"][$AxisID]["Display"] = NULL; } + if ( !isset($Data["Axis"][$AxisID]["Format"]) ) { $Data["Axis"][$AxisID]["Format"] = NULL; } + if ( !isset($Data["Axis"][$AxisID]["Unit"]) ) { $Data["Axis"][$AxisID]["Unit"] = NULL; } + } + + /* Still no X axis */ + if ( $GotXAxis == FALSE ) + { + if ( $Abscissa != NULL ) + { + $Points = count($Data["Series"][$Abscissa]["Data"]); + if ( $AutoAxisLabels ) + $AxisName = isset($Data["Series"][$Abscissa]["Description"]) ? $Data["Series"][$Abscissa]["Description"] : NULL; + else + $AxisName = NULL; + } + else + { + $Points = 0; + $AxisName = isset($Data["XAxisName"]) ? $Data["XAxisName"] : NULL; + foreach($Data["Series"] as $SerieID => $SerieParameter) + { if ( $SerieParameter["isDrawable"] ) { $Points = max($Points,count($SerieParameter["Data"])); } } + } + + $AxisID = count($Data["Axis"]); + $Data["Axis"][$AxisID]["Identity"] = AXIS_X; + if ( $Pos == SCALE_POS_LEFTRIGHT ) { $Data["Axis"][$AxisID]["Position"] = AXIS_POSITION_BOTTOM; } else { $Data["Axis"][$AxisID]["Position"] = AXIS_POSITION_LEFT; } + if ( isset($Data["AbscissaName"]) ) { $Data["Axis"][$AxisID]["Name"] = $Data["AbscissaName"]; } + if ( $XMargin == AUTO ) + { + if ( $Pos == SCALE_POS_LEFTRIGHT ) + { $Height = $this->GraphAreaX2-$this->GraphAreaX1; } + else + { $Height = $this->GraphAreaY2-$this->GraphAreaY1; } + + if ( $Points == 1 ) + $Data["Axis"][$AxisID]["Margin"] = $Height / 2; + else + $Data["Axis"][$AxisID]["Margin"] = ($Height/$Points) / 2; + } + else + { $Data["Axis"][$AxisID]["Margin"] = $XMargin; } + $Data["Axis"][$AxisID]["Rows"] = $Points-1; + if ( !isset($Data["Axis"][$AxisID]["Display"]) ) { $Data["Axis"][$AxisID]["Display"] = NULL; } + if ( !isset($Data["Axis"][$AxisID]["Format"]) ) { $Data["Axis"][$AxisID]["Format"] = NULL; } + if ( !isset($Data["Axis"][$AxisID]["Unit"]) ) { $Data["Axis"][$AxisID]["Unit"] = NULL; } + } + + /* Do we need to reverse the abscissa position? */ + if ( $Pos != SCALE_POS_LEFTRIGHT ) + { + if ( $Data["AbsicssaPosition"] == AXIS_POSITION_BOTTOM ) + { $Data["AbsicssaPosition"] = AXIS_POSITION_LEFT; } + else + { $Data["AbsicssaPosition"] = AXIS_POSITION_RIGHT; } + } + $Data["Axis"][$AxisID]["Position"] = $Data["AbsicssaPosition"]; + + $this->DataSet->saveOrientation($Pos); + $this->DataSet->saveAxisConfig($Data["Axis"]); + $this->DataSet->saveYMargin($YMargin); + + $FontColorRo = $this->FontColorR; $FontColorGo = $this->FontColorG; $FontColorBo = $this->FontColorB; + + $AxisPos["L"] = $this->GraphAreaX1; $AxisPos["R"] = $this->GraphAreaX2; $AxisPos["T"] = $this->GraphAreaY1; $AxisPos["B"] = $this->GraphAreaY2; + foreach($Data["Axis"] as $AxisID => $Parameters) + { + if ( isset($Parameters["Color"]) ) + { + $AxisR = $Parameters["Color"]["R"]; $AxisG = $Parameters["Color"]["G"]; $AxisB = $Parameters["Color"]["B"]; + $TickR = $Parameters["Color"]["R"]; $TickG = $Parameters["Color"]["G"]; $TickB = $Parameters["Color"]["B"]; + $this->setFontProperties(array("R"=>$Parameters["Color"]["R"],"G"=>$Parameters["Color"]["G"],"B"=>$Parameters["Color"]["B"])); + } + else + { + $AxisR = $AxisRo; $AxisG = $AxisGo; $AxisB = $AxisBo; + $TickR = $TickRo; $TickG = $TickGo; $TickB = $TickBo; + $this->setFontProperties(array("R"=>$FontColorRo,"G"=>$FontColorGo,"B"=>$FontColorBo)); + } + + $LastValue = "w00t"; $ID = 1; + if ( $Parameters["Identity"] == AXIS_X ) + { + if ( $Pos == SCALE_POS_LEFTRIGHT ) + { + if ( $Parameters["Position"] == AXIS_POSITION_BOTTOM ) + { + if ( $LabelRotation == 0 ) { $LabelAlign = TEXT_ALIGN_TOPMIDDLE; $YLabelOffset = 2; } + if ( $LabelRotation > 0 && $LabelRotation < 190 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $YLabelOffset = 5; } + if ( $LabelRotation == 180 ) { $LabelAlign = TEXT_ALIGN_BOTTOMMIDDLE; $YLabelOffset = 5; } + if ( $LabelRotation > 180 && $LabelRotation < 360 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $YLabelOffset = 2; } + + if ( !$RemoveXAxis ) + { + if ( $Floating ) + { $FloatingOffset = $YMargin; $this->drawLine($this->GraphAreaX1+$Parameters["Margin"],$AxisPos["B"],$this->GraphAreaX2-$Parameters["Margin"],$AxisPos["B"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + else + { $FloatingOffset = 0; $this->drawLine($this->GraphAreaX1,$AxisPos["B"],$this->GraphAreaX2,$AxisPos["B"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + + if ( $DrawArrows ) { $this->drawArrow($this->GraphAreaX2-$Parameters["Margin"],$AxisPos["B"],$this->GraphAreaX2+($ArrowSize*2),$AxisPos["B"],array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } + } + + $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"]*2; + + if ($Parameters["Rows"] == 0 ) { $Step = $Width; } else { $Step = $Width / ($Parameters["Rows"]); } + + $MaxBottom = $AxisPos["B"]; + for($i=0;$i<=$Parameters["Rows"];$i++) + { + $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step*$i; + $YPos = $AxisPos["B"]; + + if ( $Abscissa != NULL ) + { if ( isset($Data["Series"][$Abscissa]["Data"][$i]) ) { $Value = $this->scaleFormat($Data["Series"][$Abscissa]["Data"][$i],$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]); } else { $Value = ""; } } + else + { + if ( isset($Parameters["ScaleMin"]) && isset ($Parameters["RowHeight"]) ) + $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]); + else + $Value = $i; + } + + $ID++; $Skipped = TRUE; + if ( $this->isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip) && !$RemoveXAxis) + { + $Bounds = $this->drawText($XPos,$YPos+$OuterTickWidth+$YLabelOffset,$Value,array("Angle"=>$LabelRotation,"Align"=>$LabelAlign)); + $TxtBottom = $YPos+$OuterTickWidth+2+($Bounds[0]["Y"]-$Bounds[2]["Y"]); + $MaxBottom = max($MaxBottom,$TxtBottom); + $LastValue = $Value; + $Skipped = FALSE; + } + + if ( $RemoveXAxis ) { $Skipped = FALSE; } + + if ( $Skipped ) + { + if ( $DrawXLines && !$RemoveSkippedAxis ) { $this->drawLine($XPos,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,$SkippedAxisColor); } + if ( ($SkippedInnerTickWidth !=0 || $SkippedOuterTickWidth != 0) && !$RemoveXAxis && !$RemoveSkippedAxis) { $this->drawLine($XPos,$YPos-$SkippedInnerTickWidth,$XPos,$YPos+$SkippedOuterTickWidth,$SkippedTickColor); } + } + else + { + if ( $DrawXLines && ($XPos != $this->GraphAreaX1 && $XPos != $this->GraphAreaX2) ) { $this->drawLine($XPos,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } + if ( ($InnerTickWidth !=0 || $OuterTickWidth != 0) && !$RemoveXAxis ) { $this->drawLine($XPos,$YPos-$InnerTickWidth,$XPos,$YPos+$OuterTickWidth,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); } + } + } + + if ( isset($Parameters["Name"]) && !$RemoveXAxis) + { + $YPos = $MaxBottom+2; + $XPos = $this->GraphAreaX1+($this->GraphAreaX2-$this->GraphAreaX1)/2; + $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_TOPMIDDLE)); + $MaxBottom = $Bounds[0]["Y"]; + + $this->DataSet->Data["GraphArea"]["Y2"] = $MaxBottom + $this->FontSize; + } + + $AxisPos["B"] = $MaxBottom + $ScaleSpacing; + } + elseif ( $Parameters["Position"] == AXIS_POSITION_TOP ) + { + if ( $LabelRotation == 0 ) { $LabelAlign = TEXT_ALIGN_BOTTOMMIDDLE; $YLabelOffset = 2; } + if ( $LabelRotation > 0 && $LabelRotation < 190 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $YLabelOffset = 2; } + if ( $LabelRotation == 180 ) { $LabelAlign = TEXT_ALIGN_TOPMIDDLE; $YLabelOffset = 5; } + if ( $LabelRotation > 180 && $LabelRotation < 360 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $YLabelOffset = 5; } + + if ( !$RemoveXAxis ) + { + if ( $Floating ) + { $FloatingOffset = $YMargin; $this->drawLine($this->GraphAreaX1+$Parameters["Margin"],$AxisPos["T"],$this->GraphAreaX2-$Parameters["Margin"],$AxisPos["T"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + else + { $FloatingOffset = 0; $this->drawLine($this->GraphAreaX1,$AxisPos["T"],$this->GraphAreaX2,$AxisPos["T"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + + if ( $DrawArrows ) { $this->drawArrow($this->GraphAreaX2-$Parameters["Margin"],$AxisPos["T"],$this->GraphAreaX2+($ArrowSize*2),$AxisPos["T"],array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } + } + + $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"]*2; + + if ($Parameters["Rows"] == 0 ) { $Step = $Width; } else { $Step = $Width / $Parameters["Rows"]; } + + $MinTop = $AxisPos["T"]; + for($i=0;$i<=$Parameters["Rows"];$i++) + { + $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step*$i; + $YPos = $AxisPos["T"]; + + if ( $Abscissa != NULL ) + { if ( isset($Data["Series"][$Abscissa]["Data"][$i]) ) { $Value = $this->scaleFormat($Data["Series"][$Abscissa]["Data"][$i],$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]); } else { $Value = ""; } } + else + { + if ( isset($Parameters["ScaleMin"]) && isset ($Parameters["RowHeight"]) ) + $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]); + else + $Value = $i; + } + + $ID++; $Skipped = TRUE; + if ( $this->isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip) && !$RemoveXAxis) + { + $Bounds = $this->drawText($XPos,$YPos-$OuterTickWidth-$YLabelOffset,$Value,array("Angle"=>$LabelRotation,"Align"=>$LabelAlign)); + $TxtBox = $YPos-$OuterTickWidth-2-($Bounds[0]["Y"]-$Bounds[2]["Y"]); + $MinTop = min($MinTop,$TxtBox); + $LastValue = $Value; + $Skipped = FALSE; + } + + if ( $RemoveXAxis ) { $Skipped = FALSE; } + + if ( $Skipped ) + { + if ( $DrawXLines && !$RemoveSkippedAxis ) { $this->drawLine($XPos,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,$SkippedAxisColor); } + if ( ($SkippedInnerTickWidth !=0 || $SkippedOuterTickWidth != 0) && !$RemoveXAxis && !$RemoveSkippedAxis ) { $this->drawLine($XPos,$YPos+$SkippedInnerTickWidth,$XPos,$YPos-$SkippedOuterTickWidth,$SkippedTickColor); } + } + else + { + if ( $DrawXLines ) { $this->drawLine($XPos,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } + if ( ($InnerTickWidth !=0 || $OuterTickWidth != 0) && !$RemoveXAxis ) { $this->drawLine($XPos,$YPos+$InnerTickWidth,$XPos,$YPos-$OuterTickWidth,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); } + } + + } + + if ( isset($Parameters["Name"]) && !$RemoveXAxis ) + { + $YPos = $MinTop-2; + $XPos = $this->GraphAreaX1+($this->GraphAreaX2-$this->GraphAreaX1)/2; + $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); + $MinTop = $Bounds[2]["Y"]; + + $this->DataSet->Data["GraphArea"]["Y1"] = $MinTop; + } + + $AxisPos["T"] = $MinTop - $ScaleSpacing; + } + } + elseif ( $Pos == SCALE_POS_TOPBOTTOM ) + { + if ( $Parameters["Position"] == AXIS_POSITION_LEFT ) + { + if ( $LabelRotation == 0 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $XLabelOffset = -2; } + if ( $LabelRotation > 0 && $LabelRotation < 190 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $XLabelOffset = -6; } + if ( $LabelRotation == 180 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $XLabelOffset = -2; } + if ( $LabelRotation > 180 && $LabelRotation < 360 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $XLabelOffset = -5; } + + if ( !$RemoveXAxis ) + { + if ( $Floating ) + { $FloatingOffset = $YMargin; $this->drawLine($AxisPos["L"],$this->GraphAreaY1+$Parameters["Margin"],$AxisPos["L"],$this->GraphAreaY2-$Parameters["Margin"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + else + { $FloatingOffset = 0; $this->drawLine($AxisPos["L"],$this->GraphAreaY1,$AxisPos["L"],$this->GraphAreaY2,array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + + if ( $DrawArrows ) { $this->drawArrow($AxisPos["L"],$this->GraphAreaY2-$Parameters["Margin"],$AxisPos["L"],$this->GraphAreaY2+($ArrowSize*2),array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } + } + + $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"]*2; + + if ($Parameters["Rows"] == 0 ) { $Step = $Height; } else { $Step = $Height / $Parameters["Rows"]; } + + $MinLeft = $AxisPos["L"]; + for($i=0;$i<=$Parameters["Rows"];$i++) + { + $YPos = $this->GraphAreaY1 + $Parameters["Margin"] + $Step*$i; + $XPos = $AxisPos["L"]; + + if ( $Abscissa != NULL ) + { if ( isset($Data["Series"][$Abscissa]["Data"][$i]) ) { $Value = $this->scaleFormat($Data["Series"][$Abscissa]["Data"][$i],$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]); } else { $Value = ""; } } + else + { + if ( isset($Parameters["ScaleMin"]) && isset ($Parameters["RowHeight"]) ) + $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]); + else + $Value = $i; + } + + $ID++; $Skipped = TRUE; + if ( $this->isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip) && !$RemoveXAxis) + { + $Bounds = $this->drawText($XPos-$OuterTickWidth+$XLabelOffset,$YPos,$Value,array("Angle"=>$LabelRotation,"Align"=>$LabelAlign)); + $TxtBox = $XPos-$OuterTickWidth-2-($Bounds[1]["X"]-$Bounds[0]["X"]); + $MinLeft = min($MinLeft,$TxtBox); + $LastValue = $Value; + $Skipped = FALSE; + } + + if ( $RemoveXAxis ) { $Skipped = FALSE; } + + if ( $Skipped ) + { + if ( $DrawXLines && !$RemoveSkippedAxis ) { $this->drawLine($this->GraphAreaX1+$FloatingOffset,$YPos,$this->GraphAreaX2-$FloatingOffset,$YPos,$SkippedAxisColor); } + if ( ($SkippedInnerTickWidth !=0 || $SkippedOuterTickWidth != 0) && !$RemoveXAxis && !$RemoveSkippedAxis ) { $this->drawLine($XPos-$SkippedOuterTickWidth,$YPos,$XPos+$SkippedInnerTickWidth,$YPos,$SkippedTickColor); } + } + else + { + if ( $DrawXLines && ($YPos != $this->GraphAreaY1 && $YPos != $this->GraphAreaY2) ) { $this->drawLine($this->GraphAreaX1+$FloatingOffset,$YPos,$this->GraphAreaX2-$FloatingOffset,$YPos,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } + if ( ($InnerTickWidth !=0 || $OuterTickWidth != 0) && !$RemoveXAxis ) { $this->drawLine($XPos-$OuterTickWidth,$YPos,$XPos+$InnerTickWidth,$YPos,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); } + } + + } + if ( isset($Parameters["Name"]) && !$RemoveXAxis ) + { + $XPos = $MinLeft-2; + $YPos = $this->GraphAreaY1+($this->GraphAreaY2-$this->GraphAreaY1)/2; + $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE,"Angle"=>90)); + $MinLeft = $Bounds[0]["X"]; + + $this->DataSet->Data["GraphArea"]["X1"] = $MinLeft; + } + + $AxisPos["L"] = $MinLeft - $ScaleSpacing; + } + elseif ( $Parameters["Position"] == AXIS_POSITION_RIGHT ) + { + if ( $LabelRotation == 0 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $XLabelOffset = 2; } + if ( $LabelRotation > 0 && $LabelRotation < 190 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $XLabelOffset = 6; } + if ( $LabelRotation == 180 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $XLabelOffset = 5; } + if ( $LabelRotation > 180 && $LabelRotation < 360 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $XLabelOffset = 7; } + + if ( !$RemoveXAxis ) + { + if ( $Floating ) + { $FloatingOffset = $YMargin; $this->drawLine($AxisPos["R"],$this->GraphAreaY1+$Parameters["Margin"],$AxisPos["R"],$this->GraphAreaY2-$Parameters["Margin"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + else + { $FloatingOffset = 0; $this->drawLine($AxisPos["R"],$this->GraphAreaY1,$AxisPos["R"],$this->GraphAreaY2,array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + + if ( $DrawArrows ) { $this->drawArrow($AxisPos["R"],$this->GraphAreaY2-$Parameters["Margin"],$AxisPos["R"],$this->GraphAreaY2+($ArrowSize*2),array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } + } + + $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"]*2; + + if ($Parameters["Rows"] == 0 ) { $Step = $Height; } else { $Step = $Height / $Parameters["Rows"]; } + + $MaxRight = $AxisPos["R"]; + for($i=0;$i<=$Parameters["Rows"];$i++) + { + $YPos = $this->GraphAreaY1 + $Parameters["Margin"] + $Step*$i; + $XPos = $AxisPos["R"]; + + if ( $Abscissa != NULL ) + { if ( isset($Data["Series"][$Abscissa]["Data"][$i]) ) { $Value = $this->scaleFormat($Data["Series"][$Abscissa]["Data"][$i],$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]); } else { $Value = ""; } } + else + { + if ( isset($Parameters["ScaleMin"]) && isset ($Parameters["RowHeight"]) ) + $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]); + else + $Value = $i; + } + + $ID++; $Skipped = TRUE; + if ( $this->isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip) && !$RemoveXAxis) + { + $Bounds = $this->drawText($XPos+$OuterTickWidth+$XLabelOffset,$YPos,$Value,array("Angle"=>$LabelRotation,"Align"=>$LabelAlign)); + $TxtBox = $XPos+$OuterTickWidth+2+($Bounds[1]["X"]-$Bounds[0]["X"]); + $MaxRight = max($MaxRight,$TxtBox); + $LastValue = $Value; + $Skipped = FALSE; + } + + if ( $RemoveXAxis ) { $Skipped = FALSE; } + + if ( $Skipped ) + { + if ( $DrawXLines && !$RemoveSkippedAxis ) { $this->drawLine($this->GraphAreaX1+$FloatingOffset,$YPos,$this->GraphAreaX2-$FloatingOffset,$YPos,$SkippedAxisColor); } + if ( ($SkippedInnerTickWidth != 0 || $SkippedOuterTickWidth != 0) && !$RemoveXAxis && !$RemoveSkippedAxis ) { $this->drawLine($XPos+$SkippedOuterTickWidth,$YPos,$XPos-$SkippedInnerTickWidth,$YPos,$SkippedTickColor); } + } + else + { + if ( $DrawXLines ) { $this->drawLine($this->GraphAreaX1+$FloatingOffset,$YPos,$this->GraphAreaX2-$FloatingOffset,$YPos,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } + if ( ($InnerTickWidth != 0 || $OuterTickWidth != 0) && !$RemoveXAxis ) { $this->drawLine($XPos+$OuterTickWidth,$YPos,$XPos-$InnerTickWidth,$YPos,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); } + } + + } + + if ( isset($Parameters["Name"]) && !$RemoveXAxis) + { + $XPos = $MaxRight+4; + $YPos = $this->GraphAreaY1+($this->GraphAreaY2-$this->GraphAreaY1)/2; + $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE,"Angle"=>270)); + $MaxRight = $Bounds[1]["X"]; + + $this->DataSet->Data["GraphArea"]["X2"] = $MaxRight + $this->FontSize; + } + + $AxisPos["R"] = $MaxRight + $ScaleSpacing; + } + } + } + + + + if ( $Parameters["Identity"] == AXIS_Y ) + { + if ( $Pos == SCALE_POS_LEFTRIGHT ) + { + if ( $Parameters["Position"] == AXIS_POSITION_LEFT ) + { + + if ( $Floating ) + { $FloatingOffset = $XMargin; $this->drawLine($AxisPos["L"],$this->GraphAreaY1+$Parameters["Margin"],$AxisPos["L"],$this->GraphAreaY2-$Parameters["Margin"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + else + { $FloatingOffset = 0; $this->drawLine($AxisPos["L"],$this->GraphAreaY1,$AxisPos["L"],$this->GraphAreaY2,array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + + if ( $DrawArrows ) { $this->drawArrow($AxisPos["L"],$this->GraphAreaY1+$Parameters["Margin"],$AxisPos["L"],$this->GraphAreaY1-($ArrowSize*2),array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } + + $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"]*2; + $Step = $Height / $Parameters["Rows"]; $SubTicksSize = $Step /2; $MinLeft = $AxisPos["L"]; + $LastY = NULL; + for($i=0;$i<=$Parameters["Rows"];$i++) + { + $YPos = $this->GraphAreaY2 - $Parameters["Margin"] - $Step*$i; + $XPos = $AxisPos["L"]; + $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Parameters["Display"],$Parameters["Format"],$Parameters["Unit"]); + + if ( $i%2 == 1 ) { $BGColor = array("R"=>$BackgroundR1,"G"=>$BackgroundG1,"B"=>$BackgroundB1,"Alpha"=>$BackgroundAlpha1); } else { $BGColor = array("R"=>$BackgroundR2,"G"=>$BackgroundG2,"B"=>$BackgroundB2,"Alpha"=>$BackgroundAlpha2); } + if ( $LastY != NULL && $CycleBackground && ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) )) { $this->drawFilledRectangle($this->GraphAreaX1+$FloatingOffset,$LastY,$this->GraphAreaX2-$FloatingOffset,$YPos,$BGColor); } + + if ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) ) { $this->drawLine($this->GraphAreaX1+$FloatingOffset,$YPos,$this->GraphAreaX2-$FloatingOffset,$YPos,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } + + if ( $DrawSubTicks && $i != $Parameters["Rows"] ) + $this->drawLine($XPos-$OuterSubTickWidth,$YPos-$SubTicksSize,$XPos+$InnerSubTickWidth,$YPos-$SubTicksSize,array("R"=>$SubTickR,"G"=>$SubTickG,"B"=>$SubTickB,"Alpha"=>$SubTickAlpha)); + + $this->drawLine($XPos-$OuterTickWidth,$YPos,$XPos+$InnerTickWidth,$YPos,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); + $Bounds = $this->drawText($XPos-$OuterTickWidth-2,$YPos,$Value,array("Align"=>TEXT_ALIGN_MIDDLERIGHT)); + $TxtLeft = $XPos-$OuterTickWidth-2-($Bounds[1]["X"]-$Bounds[0]["X"]); + $MinLeft = min($MinLeft,$TxtLeft); + + $LastY = $YPos; + } + + if ( isset($Parameters["Name"]) ) + { + $XPos = $MinLeft-2; + $YPos = $this->GraphAreaY1+($this->GraphAreaY2-$this->GraphAreaY1)/2; + $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE,"Angle"=>90)); + $MinLeft = $Bounds[2]["X"]; + + $this->DataSet->Data["GraphArea"]["X1"] = $MinLeft; + } + + $AxisPos["L"] = $MinLeft - $ScaleSpacing; + } + elseif ( $Parameters["Position"] == AXIS_POSITION_RIGHT ) + { + if ( $Floating ) + { $FloatingOffset = $XMargin; $this->drawLine($AxisPos["R"],$this->GraphAreaY1+$Parameters["Margin"],$AxisPos["R"],$this->GraphAreaY2-$Parameters["Margin"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + else + { $FloatingOffset = 0; $this->drawLine($AxisPos["R"],$this->GraphAreaY1,$AxisPos["R"],$this->GraphAreaY2,array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + + if ( $DrawArrows ) { $this->drawArrow($AxisPos["R"],$this->GraphAreaY1+$Parameters["Margin"],$AxisPos["R"],$this->GraphAreaY1-($ArrowSize*2),array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } + + $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"]*2; + $Step = $Height / $Parameters["Rows"]; $SubTicksSize = $Step /2; $MaxLeft = $AxisPos["R"]; + $LastY = NULL; + for($i=0;$i<=$Parameters["Rows"];$i++) + { + $YPos = $this->GraphAreaY2 - $Parameters["Margin"] - $Step*$i; + $XPos = $AxisPos["R"]; + $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Parameters["Display"],$Parameters["Format"],$Parameters["Unit"]); + + if ( $i%2 == 1 ) { $BGColor = array("R"=>$BackgroundR1,"G"=>$BackgroundG1,"B"=>$BackgroundB1,"Alpha"=>$BackgroundAlpha1); } else { $BGColor = array("R"=>$BackgroundR2,"G"=>$BackgroundG2,"B"=>$BackgroundB2,"Alpha"=>$BackgroundAlpha2); } + if ( $LastY != NULL && $CycleBackground && ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) )) { $this->drawFilledRectangle($this->GraphAreaX1+$FloatingOffset,$LastY,$this->GraphAreaX2-$FloatingOffset,$YPos,$BGColor); } + + if ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) ) { $this->drawLine($this->GraphAreaX1+$FloatingOffset,$YPos,$this->GraphAreaX2-$FloatingOffset,$YPos,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } + + if ( $DrawSubTicks && $i != $Parameters["Rows"] ) + $this->drawLine($XPos-$OuterSubTickWidth,$YPos-$SubTicksSize,$XPos+$InnerSubTickWidth,$YPos-$SubTicksSize,array("R"=>$SubTickR,"G"=>$SubTickG,"B"=>$SubTickB,"Alpha"=>$SubTickAlpha)); + + $this->drawLine($XPos-$InnerTickWidth,$YPos,$XPos+$OuterTickWidth,$YPos,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); + $Bounds = $this->drawText($XPos+$OuterTickWidth+2,$YPos,$Value,array("Align"=>TEXT_ALIGN_MIDDLELEFT)); + $TxtLeft = $XPos+$OuterTickWidth+2+($Bounds[1]["X"]-$Bounds[0]["X"]); + $MaxLeft = max($MaxLeft,$TxtLeft); + + $LastY = $YPos; + } + + if ( isset($Parameters["Name"]) ) + { + $XPos = $MaxLeft+6; + $YPos = $this->GraphAreaY1+($this->GraphAreaY2-$this->GraphAreaY1)/2; + $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE,"Angle"=>270)); + $MaxLeft = $Bounds[2]["X"]; + + $this->DataSet->Data["GraphArea"]["X2"] = $MaxLeft + $this->FontSize; + } + $AxisPos["R"] = $MaxLeft + $ScaleSpacing; + } + } + elseif ( $Pos == SCALE_POS_TOPBOTTOM ) + { + if ( $Parameters["Position"] == AXIS_POSITION_TOP ) + { + if ( $Floating ) + { $FloatingOffset = $XMargin; $this->drawLine($this->GraphAreaX1+$Parameters["Margin"],$AxisPos["T"],$this->GraphAreaX2-$Parameters["Margin"],$AxisPos["T"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + else + { $FloatingOffset = 0; $this->drawLine($this->GraphAreaX1,$AxisPos["T"],$this->GraphAreaX2,$AxisPos["T"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + + if ( $DrawArrows ) { $this->drawArrow($this->GraphAreaX2-$Parameters["Margin"],$AxisPos["T"],$this->GraphAreaX2+($ArrowSize*2),$AxisPos["T"],array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } + + $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"]*2; + $Step = $Width / $Parameters["Rows"]; $SubTicksSize = $Step /2; $MinTop = $AxisPos["T"]; + $LastX = NULL; + for($i=0;$i<=$Parameters["Rows"];$i++) + { + $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step*$i; + $YPos = $AxisPos["T"]; + $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Parameters["Display"],$Parameters["Format"],$Parameters["Unit"]); + + if ( $i%2 == 1 ) { $BGColor = array("R"=>$BackgroundR1,"G"=>$BackgroundG1,"B"=>$BackgroundB1,"Alpha"=>$BackgroundAlpha1); } else { $BGColor = array("R"=>$BackgroundR2,"G"=>$BackgroundG2,"B"=>$BackgroundB2,"Alpha"=>$BackgroundAlpha2); } + if ( $LastX != NULL && $CycleBackground && ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) )) { $this->drawFilledRectangle($LastX,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,$BGColor); } + + if ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) ) { $this->drawLine($XPos,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } + + if ( $DrawSubTicks && $i != $Parameters["Rows"] ) + $this->drawLine($XPos+$SubTicksSize,$YPos-$OuterSubTickWidth,$XPos+$SubTicksSize,$YPos+$InnerSubTickWidth,array("R"=>$SubTickR,"G"=>$SubTickG,"B"=>$SubTickB,"Alpha"=>$SubTickAlpha)); + + $this->drawLine($XPos,$YPos-$OuterTickWidth,$XPos,$YPos+$InnerTickWidth,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); + $Bounds = $this->drawText($XPos,$YPos-$OuterTickWidth-2,$Value,array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); + $TxtHeight = $YPos-$OuterTickWidth-2-($Bounds[1]["Y"]-$Bounds[2]["Y"]); + $MinTop = min($MinTop,$TxtHeight); + + $LastX = $XPos; + } + + if ( isset($Parameters["Name"]) ) + { + $YPos = $MinTop-2; + $XPos = $this->GraphAreaX1+($this->GraphAreaX2-$this->GraphAreaX1)/2; + $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); + $MinTop = $Bounds[2]["Y"]; + + $this->DataSet->Data["GraphArea"]["Y1"] = $MinTop; + } + + $AxisPos["T"] = $MinTop - $ScaleSpacing; + } + elseif ( $Parameters["Position"] == AXIS_POSITION_BOTTOM ) + { + if ( $Floating ) + { $FloatingOffset = $XMargin; $this->drawLine($this->GraphAreaX1+$Parameters["Margin"],$AxisPos["B"],$this->GraphAreaX2-$Parameters["Margin"],$AxisPos["B"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + else + { $FloatingOffset = 0; $this->drawLine($this->GraphAreaX1,$AxisPos["B"],$this->GraphAreaX2,$AxisPos["B"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + + if ( $DrawArrows ) { $this->drawArrow($this->GraphAreaX2-$Parameters["Margin"],$AxisPos["B"],$this->GraphAreaX2+($ArrowSize*2),$AxisPos["B"],array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } + + $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"]*2; + $Step = $Width / $Parameters["Rows"]; $SubTicksSize = $Step /2; $MaxBottom = $AxisPos["B"]; + $LastX = NULL; + for($i=0;$i<=$Parameters["Rows"];$i++) + { + $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step*$i; + $YPos = $AxisPos["B"]; + $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Parameters["Display"],$Parameters["Format"],$Parameters["Unit"]); + + if ( $i%2 == 1 ) { $BGColor = array("R"=>$BackgroundR1,"G"=>$BackgroundG1,"B"=>$BackgroundB1,"Alpha"=>$BackgroundAlpha1); } else { $BGColor = array("R"=>$BackgroundR2,"G"=>$BackgroundG2,"B"=>$BackgroundB2,"Alpha"=>$BackgroundAlpha2); } + if ( $LastX != NULL && $CycleBackground && ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) )) { $this->drawFilledRectangle($LastX,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,$BGColor); } + + if ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) ) { $this->drawLine($XPos,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } + + if ( $DrawSubTicks && $i != $Parameters["Rows"] ) + $this->drawLine($XPos+$SubTicksSize,$YPos-$OuterSubTickWidth,$XPos+$SubTicksSize,$YPos+$InnerSubTickWidth,array("R"=>$SubTickR,"G"=>$SubTickG,"B"=>$SubTickB,"Alpha"=>$SubTickAlpha)); + + $this->drawLine($XPos,$YPos-$OuterTickWidth,$XPos,$YPos+$InnerTickWidth,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); + $Bounds = $this->drawText($XPos,$YPos+$OuterTickWidth+2,$Value,array("Align"=>TEXT_ALIGN_TOPMIDDLE)); + $TxtHeight = $YPos+$OuterTickWidth+2+($Bounds[1]["Y"]-$Bounds[2]["Y"]); + $MaxBottom = max($MaxBottom,$TxtHeight); + + $LastX = $XPos; + } + + if ( isset($Parameters["Name"]) ) + { + $YPos = $MaxBottom+2; + $XPos = $this->GraphAreaX1+($this->GraphAreaX2-$this->GraphAreaX1)/2; + $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_TOPMIDDLE)); + $MaxBottom = $Bounds[0]["Y"]; + + $this->DataSet->Data["GraphArea"]["Y2"] = $MaxBottom + $this->FontSize; + } + + $AxisPos["B"] = $MaxBottom + $ScaleSpacing; + } + } + } + } + } + + function isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip) + { + if ( $LabelingMethod == LABELING_DIFFERENT && $Value != $LastValue ) { return(TRUE); } + if ( $LabelingMethod == LABELING_DIFFERENT && $Value == $LastValue ) { return(FALSE); } + if ( $LabelingMethod == LABELING_ALL && $LabelSkip == 0 ) { return(TRUE); } + if ( $LabelingMethod == LABELING_ALL && ($ID+$LabelSkip) % ($LabelSkip+1) != 1 ) { return(FALSE); } + + return(TRUE); + } + + /* Compute the scale, check for the best visual factors */ + function computeScale($XMin,$XMax,$MaxDivs,$Factors,$AxisID=0) + { + /* Compute each factors */ + $Results = ""; + foreach ($Factors as $Key => $Factor) + $Results[$Factor] = $this->processScale($XMin,$XMax,$MaxDivs,array($Factor),$AxisID); + + /* Remove scales that are creating to much decimals */ + $GoodScaleFactors = ""; + foreach ($Results as $Key => $Result) + { + $Decimals = preg_split("/\./",$Result["RowHeight"]); + if ( (!isset($Decimals[1])) || (strlen($Decimals[1]) < 6) ) { $GoodScaleFactors[] = $Key; } + } + + /* Found no correct scale, shame,... returns the 1st one as default */ + if ( $GoodScaleFactors == "" ) { return($Results[$Factors[0]]); } + + /* Find the factor that cause the maximum number of Rows */ + $MaxRows = 0; $BestFactor = 0; + foreach($GoodScaleFactors as $Key => $Factor) + { if ( $Results[$Factor]["Rows"] > $MaxRows ) { $MaxRows = $Results[$Factor]["Rows"]; $BestFactor = $Factor; } } + + /* Return the best visual scale */ + return($Results[$BestFactor]); + } + + /* Compute the best matching scale based on size & factors */ + function processScale($XMin,$XMax,$MaxDivs,$Factors,$AxisID) + { + $ScaleHeight = abs(ceil($XMax)-floor($XMin)); + + if ( isset($this->DataSet->Data["Axis"][$AxisID]["Format"]) ) + $Format = $this->DataSet->Data["Axis"][$AxisID]["Format"]; + else + $Format = NULL; + + if ( isset($this->DataSet->Data["Axis"][$AxisID]["Display"]) ) + $Mode = $this->DataSet->Data["Axis"][$AxisID]["Display"]; + else + $Mode = AXIS_FORMAT_DEFAULT; + + $Scale = ""; + if ( $XMin != $XMax ) + { + $Found = FALSE; $Rescaled = FALSE; $Scaled10Factor = .0001; $Result = 0; + while(!$Found) + { + foreach($Factors as $Key => $Factor) + { + if ( !$Found ) + { + if ( !($this->modulo($XMin,$Factor*$Scaled10Factor) == 0) || ($XMin != floor($XMin))) { $XMinRescaled = floor($XMin/($Factor*$Scaled10Factor))*$Factor*$Scaled10Factor; } else { $XMinRescaled = $XMin; } + if ( !($this->modulo($XMax,$Factor*$Scaled10Factor) == 0) || ($XMax != floor($XMax))) { $XMaxRescaled = floor($XMax/($Factor*$Scaled10Factor))*$Factor*$Scaled10Factor+($Factor*$Scaled10Factor); } else { $XMaxRescaled = $XMax; } + $ScaleHeightRescaled = abs($XMaxRescaled-$XMinRescaled); + + if ( !$Found && floor($ScaleHeightRescaled/($Factor*$Scaled10Factor)) <= $MaxDivs ) { $Found = TRUE; $Rescaled = TRUE; $Result = $Factor * $Scaled10Factor; } + } + } + $Scaled10Factor = $Scaled10Factor * 10; + } + + /* ReCall Min / Max / Height */ + if ( $Rescaled ) { $XMin = $XMinRescaled; $XMax = $XMaxRescaled; $ScaleHeight = $ScaleHeightRescaled; } + + /* Compute rows size */ + $Rows = floor($ScaleHeight / $Result); if ( $Rows == 0 ) { $Rows = 1; } + $RowHeight = $ScaleHeight / $Rows; + + /* Return the results */ + $Scale["Rows"] = $Rows; $Scale["RowHeight"] = $RowHeight; $Scale["XMin"] = $XMin; $Scale["XMax"] = $XMax; + + /* Compute the needed decimals for the metric view to avoid repetition of the same X Axis labels */ + if ( $Mode == AXIS_FORMAT_METRIC && $Format == NULL ) + { + $Done = FALSE; $GoodDecimals = 0; + for($Decimals=0;$Decimals<=10;$Decimals++) + { + if ( !$Done ) + { + $LastLabel = "zob"; $ScaleOK = TRUE; + for($i=0;$i<=$Rows;$i++) + { + $Value = $XMin + $i*$RowHeight; + $Label = $this->scaleFormat($Value,AXIS_FORMAT_METRIC,$Decimals); + + if ( $LastLabel == $Label ) { $ScaleOK = FALSE; } + $LastLabel = $Label; + } + if ( $ScaleOK ) { $Done = TRUE; $GoodDecimals = $Decimals; } + } + } + + $Scale["Format"] = $GoodDecimals; + } + } + else + { + /* If all values are the same we keep a +1/-1 scale */ + $Rows = 2; $XMin = $XMax-1; $XMax = $XMax+1; $RowHeight = 1; + + /* Return the results */ + $Scale["Rows"] = $Rows; $Scale["RowHeight"] = $RowHeight; $Scale["XMin"] = $XMin; $Scale["XMax"] = $XMax; + } + + return($Scale); + } + + function modulo($Value1,$Value2) + { + if (floor($Value2) == 0) { return(0); } + if (floor($Value2) != 0) { return($Value1 % $Value2); } + + $MinValue = min($Value1,$Value2); $Factor = 10; + while ( floor($MinValue*$Factor) == 0 ) + { $Factor = $Factor * 10; } + + return(($Value1*$Factor) % ($Value2*$Factor)); + } + + /* Draw an X threshold */ + function drawXThreshold($Value,$Format="") + { + $R = isset($Format["R"]) ? $Format["R"] : 255; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 50; + $Weight = isset($Format["Weight"]) ? $Format["Weight"] : NULL; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : 6; + $Wide = isset($Format["Wide"]) ? $Format["Wide"] : FALSE; + $WideFactor = isset($Format["WideFactor"]) ? $Format["WideFactor"] : 5; + $WriteCaption = isset($Format["WriteCaption"]) ? $Format["WriteCaption"] : FALSE; + $Caption = isset($Format["Caption"]) ? $Format["Caption"] : NULL; + $CaptionAlign = isset($Format["CaptionAlign"]) ? $Format["CaptionAlign"] : CAPTION_LEFT_TOP; + $CaptionOffset = isset($Format["CaptionOffset"]) ? $Format["CaptionOffset"] : 5; + $CaptionR = isset($Format["CaptionR"]) ? $Format["CaptionR"] : 255; + $CaptionG = isset($Format["CaptionG"]) ? $Format["CaptionG"] : 255; + $CaptionB = isset($Format["CaptionB"]) ? $Format["CaptionB"] : 255; + $CaptionAlpha = isset($Format["CaptionAlpha"]) ? $Format["CaptionAlpha"] : 100; + $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : TRUE; + $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : FALSE; + $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 3; + $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : TRUE; + $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 3; + $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0; + $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0; + $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0; + $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 30; + $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : ""; + $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 255; + $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 255; + $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 255; + $BoxBorderAlpha = isset($Format["BoxBorderAlpha"]) ? $Format["BoxBorderAlpha"] : 100; + $ValueIsLabel = isset($Format["ValueIsLabel"]) ? $Format["ValueIsLabel"] : FALSE; + + $Data = $this->DataSet->getData(); + $AbscissaMargin = $this->getAbscissaMargin($Data); + $XScale = $this->scaleGetXSettings(); + + if ( is_array($Value) ) { foreach ($Value as $Key => $ID) { $this->drawXThreshold($ID,$Format); } return(0); } + + if ( $ValueIsLabel ) + { + $Format["ValueIsLabel"] = FALSE; + foreach($Data["Series"][$Data["Abscissa"]]["Data"] as $Key => $SerieValue) + { if ( $SerieValue == $Value ) { $this->drawXThreshold($Key,$Format); } } + + return(0); + } + + $CaptionSettings = array("DrawBox"=>$DrawBox,"DrawBoxBorder"=>$DrawBoxBorder,"BorderOffset"=>$BorderOffset,"BoxRounded"=>$BoxRounded,"RoundedRadius"=>$RoundedRadius, + "BoxR"=>$BoxR,"BoxG"=>$BoxG,"BoxB"=>$BoxB,"BoxAlpha"=>$BoxAlpha,"BoxSurrounding"=>$BoxSurrounding, + "BoxBorderR"=>$BoxBorderR,"BoxBorderG"=>$BoxBorderG,"BoxBorderB"=>$BoxBorderB,"BoxBorderAlpha"=>$BoxBorderAlpha, + "R"=>$CaptionR,"G"=>$CaptionG,"B"=>$CaptionB,"Alpha"=>$CaptionAlpha); + + if ( $Caption == NULL ) + { + if ( isset($Data["Abscissa"]) ) + { + if ( isset($Data["Series"][$Data["Abscissa"]]["Data"][$Value]) ) + $Caption = $Data["Series"][$Data["Abscissa"]]["Data"][$Value]; + else + $Caption = $Value; + } + else + $Caption = $Value; + } + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + $XStep = (($this->GraphAreaX2 - $this->GraphAreaX1) - $XScale[0] *2 ) / $XScale[1]; + $XPos = $this->GraphAreaX1 + $XScale[0] + $XStep * $Value; + $YPos1 = $this->GraphAreaY1 + $Data["YMargin"]; + $YPos2 = $this->GraphAreaY2 - $Data["YMargin"]; + + if ( $XPos >= $this->GraphAreaX1 + $AbscissaMargin && $XPos <= $this->GraphAreaX2 - $AbscissaMargin ) + { + $this->drawLine($XPos,$YPos1,$XPos,$YPos2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); + + if ( $Wide ) + { + $this->drawLine($XPos-1,$YPos1,$XPos-1,$YPos2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); + $this->drawLine($XPos+1,$YPos1,$XPos+1,$YPos2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); + } + + if ( $WriteCaption ) + { + if ( $CaptionAlign == CAPTION_LEFT_TOP ) + { $Y = $YPos1 + $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE; } + else + { $Y = $YPos2 - $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_BOTTOMMIDDLE; } + + $this->drawText($XPos,$Y,$Caption,$CaptionSettings); + } + + return(array("X"=>$XPos)); + } + } + elseif( $Data["Orientation"] == SCALE_POS_TOPBOTTOM ) + { + $XStep = (($this->GraphAreaY2 - $this->GraphAreaY1) - $XScale[0] *2 ) / $XScale[1]; + $XPos = $this->GraphAreaY1 + $XScale[0] + $XStep * $Value; + $YPos1 = $this->GraphAreaX1 + $Data["YMargin"]; + $YPos2 = $this->GraphAreaX2 - $Data["YMargin"]; + + if ( $XPos >= $this->GraphAreaY1 + $AbscissaMargin && $XPos <= $this->GraphAreaY2 - $AbscissaMargin ) + { + $this->drawLine($YPos1,$XPos,$YPos2,$XPos,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); + + if ( $Wide ) + { + $this->drawLine($YPos1,$XPos-1,$YPos2,$XPos-1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); + $this->drawLine($YPos1,$XPos+1,$YPos2,$XPos+1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); + } + + if ( $WriteCaption ) + { + if ( $CaptionAlign == CAPTION_LEFT_TOP ) + { $Y = $YPos1 + $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLELEFT; } + else + { $Y = $YPos2 - $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLERIGHT; } + + $this->drawText($Y,$XPos,$Caption,$CaptionSettings); + } + + return(array("X"=>$XPos)); + } + } + } + + /* Draw an X threshold area */ + function drawXThresholdArea($Value1,$Value2,$Format="") + { + $R = isset($Format["R"]) ? $Format["R"] : 255; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 20; + $Border = isset($Format["Border"]) ? $Format["Border"] : TRUE; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha + 20; + $BorderTicks = isset($Format["BorderTicks"]) ? $Format["BorderTicks"] : 2; + $AreaName = isset($Format["AreaName"]) ? $Format["AreaName"] : NULL; + $NameAngle = isset($Format["NameAngle"]) ? $Format["NameAngle"] : ZONE_NAME_ANGLE_AUTO; + $NameR = isset($Format["NameR"]) ? $Format["NameR"] : 255; + $NameG = isset($Format["NameG"]) ? $Format["NameG"] : 255; + $NameB = isset($Format["NameB"]) ? $Format["NameB"] : 255; + $NameAlpha = isset($Format["NameAlpha"]) ? $Format["NameAlpha"] : 100; + $DisableShadowOnArea = isset($Format["DisableShadowOnArea"]) ? $Format["DisableShadowOnArea"] : TRUE; + + $RestoreShadow = $this->Shadow; + if ( $DisableShadowOnArea && $this->Shadow ) { $this->Shadow = FALSE; } + + if ($BorderAlpha >100) { $BorderAlpha = 100;} + + $Data = $this->DataSet->getData(); + $XScale = $this->scaleGetXSettings(); + $AbscissaMargin = $this->getAbscissaMargin($Data); + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + $XStep = (($this->GraphAreaX2 - $this->GraphAreaX1) - $XScale[0] *2 ) / $XScale[1]; + $XPos1 = $this->GraphAreaX1 + $XScale[0] + $XStep * $Value1; + $XPos2 = $this->GraphAreaX1 + $XScale[0] + $XStep * $Value2; + $YPos1 = $this->GraphAreaY1 + $Data["YMargin"]; + $YPos2 = $this->GraphAreaY2 - $Data["YMargin"]; + + if ( $XPos1 < $this->GraphAreaX1 + $XScale[0] ) { $XPos1 = $this->GraphAreaX1 + $XScale[0]; } + if ( $XPos1 > $this->GraphAreaX2 - $XScale[0] ) { $XPos1 = $this->GraphAreaX2 - $XScale[0]; } + if ( $XPos2 < $this->GraphAreaX1 + $XScale[0] ) { $XPos2 = $this->GraphAreaX1 + $XScale[0]; } + if ( $XPos2 > $this->GraphAreaX2 - $XScale[0] ) { $XPos2 = $this->GraphAreaX2 - $XScale[0]; } + + $this->drawFilledRectangle($XPos1,$YPos1,$XPos2,$YPos2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); + + if ( $Border ) + { + $this->drawLine($XPos1,$YPos1,$XPos1,$YPos2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); + $this->drawLine($XPos2,$YPos1,$XPos2,$YPos2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); + } + + if ( $AreaName != NULL ) + { + $XPos = ($XPos2-$XPos1)/2 + $XPos1; + $YPos = ($YPos2-$YPos1)/2 + $YPos1; + + if ( $NameAngle == ZONE_NAME_ANGLE_AUTO ) + { + $TxtPos = $this->getTextBox($XPos,$YPos,$this->FontName,$this->FontSize,0,$AreaName); + $TxtWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"]; + if ( abs($XPos2 - $XPos1) > $TxtWidth ) { $NameAngle = 0; } else { $NameAngle = 90; } + } + $this->Shadow = $RestoreShadow; + $this->drawText($XPos,$YPos,$AreaName,array("R"=>$NameR,"G"=>$NameG,"B"=>$NameB,"Alpha"=>$NameAlpha,"Angle"=>$NameAngle,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE)); + if ( $DisableShadowOnArea ) { $this->Shadow = FALSE; } + } + + $this->Shadow = $RestoreShadow; + return(array("X1"=>$XPos1,"X2"=>$XPos2)); + } + elseif ( $Data["Orientation"] == SCALE_POS_TOPBOTTOM ) + { + $XStep = (($this->GraphAreaY2 - $this->GraphAreaY1) - $XScale[0] *2 ) / $XScale[1]; + $XPos1 = $this->GraphAreaY1 + $XScale[0] + $XStep * $Value1; + $XPos2 = $this->GraphAreaY1 + $XScale[0] + $XStep * $Value2; + $YPos1 = $this->GraphAreaX1 + $Data["YMargin"]; + $YPos2 = $this->GraphAreaX2 - $Data["YMargin"]; + + if ( $XPos1 < $this->GraphAreaY1 + $XScale[0] ) { $XPos1 = $this->GraphAreaY1 + $XScale[0]; } + if ( $XPos1 > $this->GraphAreaY2 - $XScale[0] ) { $XPos1 = $this->GraphAreaY2 - $XScale[0]; } + if ( $XPos2 < $this->GraphAreaY1 + $XScale[0] ) { $XPos2 = $this->GraphAreaY1 + $XScale[0]; } + if ( $XPos2 > $this->GraphAreaY2 - $XScale[0] ) { $XPos2 = $this->GraphAreaY2 - $XScale[0]; } + + $this->drawFilledRectangle($YPos1,$XPos1,$YPos2,$XPos2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); + + if ( $Border ) + { + $this->drawLine($YPos1,$XPos1,$YPos2,$XPos1,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); + $this->drawLine($YPos1,$XPos2,$YPos2,$XPos2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); + } + + if ( $AreaName != NULL ) + { + $XPos = ($XPos2-$XPos1)/2 + $XPos1; + $YPos = ($YPos2-$YPos1)/2 + $YPos1; + + $this->Shadow = $RestoreShadow; + $this->drawText($YPos,$XPos,$AreaName,array("R"=>$NameR,"G"=>$NameG,"B"=>$NameB,"Alpha"=>$NameAlpha,"Angle"=>0,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE)); + if ( $DisableShadowOnArea ) { $this->Shadow = FALSE; } + } + + $this->Shadow = $RestoreShadow; + return(array("X1"=>$XPos1,"X2"=>$XPos2)); + } + } + + /* Draw an Y threshold with the computed scale */ + function drawThreshold($Value,$Format="") + { + $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0; + $R = isset($Format["R"]) ? $Format["R"] : 255; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 50; + $Weight = isset($Format["Weight"]) ? $Format["Weight"] : NULL; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : 6; + $Wide = isset($Format["Wide"]) ? $Format["Wide"] : FALSE; + $WideFactor = isset($Format["WideFactor"]) ? $Format["WideFactor"] : 5; + $WriteCaption = isset($Format["WriteCaption"]) ? $Format["WriteCaption"] : FALSE; + $Caption = isset($Format["Caption"]) ? $Format["Caption"] : NULL; + $CaptionAlign = isset($Format["CaptionAlign"]) ? $Format["CaptionAlign"] : CAPTION_LEFT_TOP; + $CaptionOffset = isset($Format["CaptionOffset"]) ? $Format["CaptionOffset"] : 10; + $CaptionR = isset($Format["CaptionR"]) ? $Format["CaptionR"] : 255; + $CaptionG = isset($Format["CaptionG"]) ? $Format["CaptionG"] : 255; + $CaptionB = isset($Format["CaptionB"]) ? $Format["CaptionB"] : 255; + $CaptionAlpha = isset($Format["CaptionAlpha"]) ? $Format["CaptionAlpha"] : 100; + $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : TRUE; + $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : FALSE; + $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 5; + $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : TRUE; + $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 3; + $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0; + $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0; + $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0; + $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 20; + $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : ""; + $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 255; + $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 255; + $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 255; + $BoxBorderAlpha = isset($Format["BoxBorderAlpha"]) ? $Format["BoxBorderAlpha"] : 100; + $NoMargin = isset($Format["NoMargin"]) ? $Format["NoMargin"] : FALSE; + + if ( is_array($Value) ) { foreach ($Value as $Key => $ID) { $this->drawThreshold($ID,$Format); } return(0); } + + $CaptionSettings = array("DrawBox"=>$DrawBox,"DrawBoxBorder"=>$DrawBoxBorder,"BorderOffset"=>$BorderOffset,"BoxRounded"=>$BoxRounded,"RoundedRadius"=>$RoundedRadius, + "BoxR"=>$BoxR,"BoxG"=>$BoxG,"BoxB"=>$BoxB,"BoxAlpha"=>$BoxAlpha,"BoxSurrounding"=>$BoxSurrounding, + "BoxBorderR"=>$BoxBorderR,"BoxBorderG"=>$BoxBorderG,"BoxBorderB"=>$BoxBorderB,"BoxBorderAlpha"=>$BoxBorderAlpha, + "R"=>$CaptionR,"G"=>$CaptionG,"B"=>$CaptionB,"Alpha"=>$CaptionAlpha); + + $Data = $this->DataSet->getData(); + $AbscissaMargin = $this->getAbscissaMargin($Data); + + if ( $NoMargin ) { $AbscissaMargin = 0; } + if ( !isset($Data["Axis"][$AxisID]) ) { return(-1); } + if ( $Caption == NULL ) { $Caption = $Value; } + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + $YPos = $this->scaleComputeY($Value,array("AxisID"=>$AxisID)); + if ( $YPos >= $this->GraphAreaY1+$Data["Axis"][$AxisID]["Margin"] && $YPos <= $this->GraphAreaY2-$Data["Axis"][$AxisID]["Margin"] ) + { + $X1 = $this->GraphAreaX1 + $AbscissaMargin; + $X2 = $this->GraphAreaX2 - $AbscissaMargin; + + $this->drawLine($X1,$YPos,$X2,$YPos,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); + + if ( $Wide ) + { + $this->drawLine($X1,$YPos-1,$X2,$YPos-1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); + $this->drawLine($X1,$YPos+1,$X2,$YPos+1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); + } + + if ( $WriteCaption ) + { + if ( $CaptionAlign == CAPTION_LEFT_TOP ) + { $X = $X1 + $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLELEFT; } + else + { $X = $X2 - $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLERIGHT; } + + $this->drawText($X,$YPos,$Caption,$CaptionSettings); + } + } + + return(array("Y"=>$YPos)); + } + + if ( $Data["Orientation"] == SCALE_POS_TOPBOTTOM ) + { + $XPos = $this->scaleComputeY($Value,array("AxisID"=>$AxisID)); + if ( $XPos >= $this->GraphAreaX1+$Data["Axis"][$AxisID]["Margin"] && $XPos <= $this->GraphAreaX2-$Data["Axis"][$AxisID]["Margin"] ) + { + $Y1 = $this->GraphAreaY1 + $AbscissaMargin; + $Y2 = $this->GraphAreaY2 - $AbscissaMargin; + + $this->drawLine($XPos,$Y1,$XPos,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); + + if ( $Wide ) + { + $this->drawLine($XPos-1,$Y1,$XPos-1,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); + $this->drawLine($XPos+1,$Y1,$XPos+1,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); + } + + if ( $WriteCaption ) + { + if ( $CaptionAlign == CAPTION_LEFT_TOP ) + { $Y = $Y1 + $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE; } + else + { $Y = $Y2 - $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_BOTTOMMIDDLE; } + + $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE; + $this->drawText($XPos,$Y,$Caption,$CaptionSettings); + } + } + + return(array("Y"=>$XPos)); + } + } + + /* Draw a threshold with the computed scale */ + function drawThresholdArea($Value1,$Value2,$Format="") + { + $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0; + $R = isset($Format["R"]) ? $Format["R"] : 255; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 20; + $Border = isset($Format["Border"]) ? $Format["Border"] : TRUE; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha + 20; + $BorderTicks = isset($Format["BorderTicks"]) ? $Format["BorderTicks"] : 2; + $AreaName = isset($Format["AreaName"]) ? $Format["AreaName"] : NULL; + $NameAngle = isset($Format["NameAngle"]) ? $Format["NameAngle"] : ZONE_NAME_ANGLE_AUTO; + $NameR = isset($Format["NameR"]) ? $Format["NameR"] : 255; + $NameG = isset($Format["NameG"]) ? $Format["NameG"] : 255; + $NameB = isset($Format["NameB"]) ? $Format["NameB"] : 255; + $NameAlpha = isset($Format["NameAlpha"]) ? $Format["NameAlpha"] : 100; + $DisableShadowOnArea = isset($Format["DisableShadowOnArea"]) ? $Format["DisableShadowOnArea"] : TRUE; + $NoMargin = isset($Format["NoMargin"]) ? $Format["NoMargin"] : FALSE; + + if ($Value1 > $Value2) { list($Value1, $Value2) = array($Value2, $Value1); } + + $RestoreShadow = $this->Shadow; + if ( $DisableShadowOnArea && $this->Shadow ) { $this->Shadow = FALSE; } + + if ($BorderAlpha >100) { $BorderAlpha = 100;} + + $Data = $this->DataSet->getData(); + $AbscissaMargin = $this->getAbscissaMargin($Data); + + if ( $NoMargin ) { $AbscissaMargin = 0; } + if ( !isset($Data["Axis"][$AxisID]) ) { return(-1); } + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + $XPos1 = $this->GraphAreaX1 + $AbscissaMargin; + $XPos2 = $this->GraphAreaX2 - $AbscissaMargin; + $YPos1 = $this->scaleComputeY($Value1,array("AxisID"=>$AxisID)); + $YPos2 = $this->scaleComputeY($Value2,array("AxisID"=>$AxisID)); + + if ( $YPos1 < $this->GraphAreaY1+$Data["Axis"][$AxisID]["Margin"] ) { $YPos1 = $this->GraphAreaY1+$Data["Axis"][$AxisID]["Margin"]; } + if ( $YPos1 > $this->GraphAreaY2-$Data["Axis"][$AxisID]["Margin"] ) { $YPos1 = $this->GraphAreaY2-$Data["Axis"][$AxisID]["Margin"]; } + if ( $YPos2 < $this->GraphAreaY1+$Data["Axis"][$AxisID]["Margin"] ) { $YPos2 = $this->GraphAreaY1+$Data["Axis"][$AxisID]["Margin"]; } + if ( $YPos2 > $this->GraphAreaY2-$Data["Axis"][$AxisID]["Margin"] ) { $YPos2 = $this->GraphAreaY2-$Data["Axis"][$AxisID]["Margin"]; } + + $this->drawFilledRectangle($XPos1,$YPos1,$XPos2,$YPos2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); + if ( $Border ) + { + $this->drawLine($XPos1,$YPos1,$XPos2,$YPos1,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); + $this->drawLine($XPos1,$YPos2,$XPos2,$YPos2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); + } + + if ( $AreaName != NULL ) + { + $XPos = ($XPos2-$XPos1)/2 + $XPos1; + $YPos = ($YPos2-$YPos1)/2 + $YPos1; + $this->Shadow = $RestoreShadow; + $this->drawText($XPos,$YPos,$AreaName,array("R"=>$NameR,"G"=>$NameG,"B"=>$NameB,"Alpha"=>$NameAlpha,"Angle"=>0,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE)); + if ( $DisableShadowOnArea ) { $this->Shadow = FALSE; } + } + + $this->Shadow = $RestoreShadow; + return(array("Y1"=>$YPos1,"Y2"=>$YPos2)); + } + elseif ( $Data["Orientation"] == SCALE_POS_TOPBOTTOM ) + { + $YPos1 = $this->GraphAreaY1 + $AbscissaMargin; + $YPos2 = $this->GraphAreaY2 - $AbscissaMargin; + $XPos1 = $this->scaleComputeY($Value1,array("AxisID"=>$AxisID)); + $XPos2 = $this->scaleComputeY($Value2,array("AxisID"=>$AxisID)); + + if ( $XPos1 < $this->GraphAreaX1+$Data["Axis"][$AxisID]["Margin"] ) { $XPos1 = $this->GraphAreaX1+$Data["Axis"][$AxisID]["Margin"]; } + if ( $XPos1 > $this->GraphAreaX2-$Data["Axis"][$AxisID]["Margin"] ) { $XPos1 = $this->GraphAreaX2-$Data["Axis"][$AxisID]["Margin"]; } + if ( $XPos2 < $this->GraphAreaX1+$Data["Axis"][$AxisID]["Margin"] ) { $XPos2 = $this->GraphAreaX1+$Data["Axis"][$AxisID]["Margin"]; } + if ( $XPos2 > $this->GraphAreaX2-$Data["Axis"][$AxisID]["Margin"] ) { $XPos2 = $this->GraphAreaX2-$Data["Axis"][$AxisID]["Margin"]; } + + $this->drawFilledRectangle($XPos1,$YPos1,$XPos2,$YPos2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); + if ( $Border ) + { + $this->drawLine($XPos1,$YPos1,$XPos1,$YPos2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); + $this->drawLine($XPos2,$YPos1,$XPos2,$YPos2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); + } + + if ( $AreaName != NULL ) + { + $XPos = ($YPos2-$YPos1)/2 + $YPos1; + $YPos = ($XPos2-$XPos1)/2 + $XPos1; + + if ( $NameAngle == ZONE_NAME_ANGLE_AUTO ) + { + $TxtPos = $this->getTextBox($XPos,$YPos,$this->FontName,$this->FontSize,0,$AreaName); + $TxtWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"]; + if ( abs($XPos2 - $XPos1) > $TxtWidth ) { $NameAngle = 0; } else { $NameAngle = 90; } + } + $this->Shadow = $RestoreShadow; + $this->drawText($YPos,$XPos,$AreaName,array("R"=>$NameR,"G"=>$NameG,"B"=>$NameB,"Alpha"=>$NameAlpha,"Angle"=>$NameAngle,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE)); + if ( $DisableShadowOnArea ) { $this->Shadow = FALSE; } + } + + $this->Shadow = $RestoreShadow; + return(array("Y1"=>$XPos1,"Y2"=>$XPos2)); + } + } + + function scaleGetXSettings() + { + $Data = $this->DataSet->getData(); + foreach($Data["Axis"] as $AxisID => $Settings) + { + if ( $Settings["Identity"] == AXIS_X ) + { + $Rows = $Settings["Rows"]; + + return(array($Settings["Margin"],$Rows)); + } + } + } + + function scaleComputeY($Values,$Option="",$ReturnOnly0Height=FALSE) + { + $AxisID = isset($Option["AxisID"]) ? $Option["AxisID"] : 0; + $SerieName = isset($Option["SerieName"]) ? $Option["SerieName"] : NULL; + + $Data = $this->DataSet->getData(); + if ( !isset($Data["Axis"][$AxisID]) ) { return(-1); } + + if ( $SerieName != NULL ) { $AxisID = $Data["Series"][$SerieName]["Axis"]; } + if ( !is_array($Values) ) { $tmp = $Values; $Values = ""; $Values[0] = $tmp; } + + $Result = ""; + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Data["Axis"][$AxisID]["Margin"]*2; + $ScaleHeight = $Data["Axis"][$AxisID]["ScaleMax"] - $Data["Axis"][$AxisID]["ScaleMin"]; + $Step = $Height / $ScaleHeight; + + if ( $ReturnOnly0Height ) + { foreach($Values as $Key => $Value) { if ( $Value == VOID ) { $Result[] = VOID; } else { $Result[] = $Step * $Value; } } } + else + { foreach($Values as $Key => $Value) { if ( $Value == VOID ) { $Result[] = VOID; } else { $Result[] = $this->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"] - ($Step * ($Value-$Data["Axis"][$AxisID]["ScaleMin"])); } } } + } + else + { + $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Data["Axis"][$AxisID]["Margin"]*2; + $ScaleWidth = $Data["Axis"][$AxisID]["ScaleMax"] - $Data["Axis"][$AxisID]["ScaleMin"]; + $Step = $Width / $ScaleWidth; + + if ( $ReturnOnly0Height ) + { foreach($Values as $Key => $Value) { if ( $Value == VOID ) { $Result[] = VOID; } else { $Result[] = $Step * $Value; } } } + else + { foreach($Values as $Key => $Value) { if ( $Value == VOID ) { $Result[] = VOID; } else { $Result[] = $this->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"] + ($Step * ($Value-$Data["Axis"][$AxisID]["ScaleMin"])); } } } + } + + if ( count($Result) == 1 ) + return($Result[0]); + else + return($Result); + } + + /* Format the axis values */ + function scaleFormat($Value,$Mode=NULL,$Format=NULL,$Unit=NULL) + { + if ( $Value == VOID ) { return(""); } + + if ( $Mode == AXIS_FORMAT_TRAFFIC ) + { + if ( $Value == 0 ) { return("0B"); } + $Units = array("B","KB","MB","GB","TB","PB"); + $Sign = ""; if ( $Value < 0 ) { $Value = abs($Value); $Sign = "-"; } + + $Value = number_format($Value/pow(1024,($Scale=floor(log($Value,1024)))),2,",","."); + return($Sign.$Value." ".$Units[$Scale]); + } + + if ( $Mode == AXIS_FORMAT_CUSTOM ) + { if ( function_exists($Format) ) { return(call_user_func($Format,$Value)); } } + + if ( $Mode == AXIS_FORMAT_DATE ) + { if ( $Format == NULL ) { $Pattern = "d/m/Y"; } else { $Pattern = $Format; } return(gmdate($Pattern,$Value)); } + + if ( $Mode == AXIS_FORMAT_TIME ) + { if ( $Format == NULL ) { $Pattern = "H:i:s"; } else { $Pattern = $Format; } return(gmdate($Pattern,$Value)); } + + if ( $Mode == AXIS_FORMAT_CURRENCY ) + { return($Format.number_format($Value,2)); } + + if ( $Mode == AXIS_FORMAT_METRIC ) + { + if (abs($Value) > 1000000000) + return(round($Value/1000000000,$Format)."g".$Unit); + if (abs($Value) > 1000000) + return(round($Value/1000000,$Format)."m".$Unit); + elseif (abs($Value) >= 1000) + return(round($Value/1000,$Format)."k".$Unit); + + } + return($Value.$Unit); + } + + /* Write Max value on a chart */ + function writeBounds($Type=BOUND_BOTH,$Format=NULL) + { + $MaxLabelTxt = isset($Format["MaxLabelTxt"]) ? $Format["MaxLabelTxt"] : "max="; + $MinLabelTxt = isset($Format["MinLabelTxt"]) ? $Format["MinLabelTxt"] : "min="; + $Decimals = isset($Format["Decimals"]) ? $Format["Decimals"] : 1; + $ExcludedSeries = isset($Format["ExcludedSeries"]) ? $Format["ExcludedSeries"] : ""; + $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 4; + $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; + $MaxDisplayR = isset($Format["MaxDisplayR"]) ? $Format["MaxDisplayR"] : 0; + $MaxDisplayG = isset($Format["MaxDisplayG"]) ? $Format["MaxDisplayG"] : 0; + $MaxDisplayB = isset($Format["MaxDisplayB"]) ? $Format["MaxDisplayB"] : 0; + $MinDisplayR = isset($Format["MinDisplayR"]) ? $Format["MinDisplayR"] : 255; + $MinDisplayG = isset($Format["MinDisplayG"]) ? $Format["MinDisplayG"] : 255; + $MinDisplayB = isset($Format["MinDisplayB"]) ? $Format["MinDisplayB"] : 255; + $MinLabelPos = isset($Format["MinLabelPos"]) ? $Format["MinLabelPos"] : BOUND_LABEL_POS_AUTO; + $MaxLabelPos = isset($Format["MaxLabelPos"]) ? $Format["MaxLabelPos"] : BOUND_LABEL_POS_AUTO; + $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : TRUE; + $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : FALSE; + $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 5; + $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : TRUE; + $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 3; + $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0; + $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0; + $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0; + $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 20; + $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : ""; + $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 255; + $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 255; + $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 255; + $BoxBorderAlpha = isset($Format["BoxBorderAlpha"]) ? $Format["BoxBorderAlpha"] : 100; + + $CaptionSettings = array("DrawBox"=>$DrawBox,"DrawBoxBorder"=>$DrawBoxBorder,"BorderOffset"=>$BorderOffset,"BoxRounded"=>$BoxRounded,"RoundedRadius"=>$RoundedRadius, + "BoxR"=>$BoxR,"BoxG"=>$BoxG,"BoxB"=>$BoxB,"BoxAlpha"=>$BoxAlpha,"BoxSurrounding"=>$BoxSurrounding, + "BoxBorderR"=>$BoxBorderR,"BoxBorderG"=>$BoxBorderG,"BoxBorderB"=>$BoxBorderB,"BoxBorderAlpha"=>$BoxBorderAlpha); + + list($XMargin,$XDivs) = $this->scaleGetXSettings(); + + $Data = $this->DataSet->getData(); + foreach($Data["Series"] as $SerieName => $Serie) + { + if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] && !isset($ExcludedSeries[$SerieName])) + { + $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; + if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; } + + $MinValue = $this->DataSet->getMin($SerieName); + $MaxValue = $this->DataSet->getMax($SerieName); + + $MinPos = VOID; $MaxPos = VOID; + foreach($Serie["Data"] as $Key => $Value) + { + if ( $Value == $MinValue && $MinPos == VOID ) { $MinPos = $Key; } + if ( $Value == $MaxValue ) { $MaxPos = $Key; } + } + + $AxisID = $Serie["Axis"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; + $X = $this->GraphAreaX1 + $XMargin; + $SerieOffset = isset($Serie["XOffset"]) ? $Serie["XOffset"] : 0; + + if ( $Type == BOUND_MAX || $Type == BOUND_BOTH ) + { + if ( $MaxLabelPos == BOUND_LABEL_POS_TOP || ( $MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue >= 0) ) { $YPos = $PosArray[$MaxPos] - $DisplayOffset + 2; $Align = TEXT_ALIGN_BOTTOMMIDDLE; } + if ( $MaxLabelPos == BOUND_LABEL_POS_BOTTOM || ( $MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue < 0) ) { $YPos = $PosArray[$MaxPos] + $DisplayOffset + 2; $Align = TEXT_ALIGN_TOPMIDDLE; } + + $XPos = $X + $MaxPos*$XStep + $SerieOffset; + $Label = $MaxLabelTxt.$this->scaleFormat(round($MaxValue,$Decimals),$Mode,$Format,$Unit); + + $TxtPos = $this->getTextBox($XPos,$YPos,$this->FontName,$this->FontSize,0,$Label); + $XOffset = 0; $YOffset = 0; + if ( $TxtPos[0]["X"] < $this->GraphAreaX1 ) { $XOffset = (($this->GraphAreaX1 - $TxtPos[0]["X"])/2); } + if ( $TxtPos[1]["X"] > $this->GraphAreaX2 ) { $XOffset = -(($TxtPos[1]["X"] - $this->GraphAreaX2)/2); } + if ( $TxtPos[2]["Y"] < $this->GraphAreaY1 ) { $YOffset = $this->GraphAreaY1 - $TxtPos[2]["Y"]; } + if ( $TxtPos[0]["Y"] > $this->GraphAreaY2 ) { $YOffset = -($TxtPos[0]["Y"] - $this->GraphAreaY2); } + + $CaptionSettings["R"] = $MaxDisplayR; $CaptionSettings["G"] = $MaxDisplayG; + $CaptionSettings["B"] = $MaxDisplayB; $CaptionSettings["Align"] = $Align; + + $this->drawText($XPos+$XOffset,$YPos+$YOffset,$Label,$CaptionSettings); + } + + if ( $Type == BOUND_MIN || $Type == BOUND_BOTH ) + { + if ( $MinLabelPos == BOUND_LABEL_POS_TOP || ( $MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue >= 0) ) { $YPos = $PosArray[$MinPos] - $DisplayOffset + 2; $Align = TEXT_ALIGN_BOTTOMMIDDLE; } + if ( $MinLabelPos == BOUND_LABEL_POS_BOTTOM || ( $MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue < 0) ) { $YPos = $PosArray[$MinPos] + $DisplayOffset + 2; $Align = TEXT_ALIGN_TOPMIDDLE; } + + $XPos = $X + $MinPos*$XStep + $SerieOffset; + $Label = $MinLabelTxt.$this->scaleFormat(round($MinValue,$Decimals),$Mode,$Format,$Unit); + + $TxtPos = $this->getTextBox($XPos,$YPos,$this->FontName,$this->FontSize,0,$Label); + $XOffset = 0; $YOffset = 0; + if ( $TxtPos[0]["X"] < $this->GraphAreaX1 ) { $XOffset = (($this->GraphAreaX1 - $TxtPos[0]["X"])/2); } + if ( $TxtPos[1]["X"] > $this->GraphAreaX2 ) { $XOffset = -(($TxtPos[1]["X"] - $this->GraphAreaX2)/2); } + if ( $TxtPos[2]["Y"] < $this->GraphAreaY1 ) { $YOffset = $this->GraphAreaY1 - $TxtPos[2]["Y"]; } + if ( $TxtPos[0]["Y"] > $this->GraphAreaY2 ) { $YOffset = -($TxtPos[0]["Y"] - $this->GraphAreaY2); } + + $CaptionSettings["R"] = $MinDisplayR; $CaptionSettings["G"] = $MinDisplayG; + $CaptionSettings["B"] = $MinDisplayB; $CaptionSettings["Align"] = $Align; + + $this->drawText($XPos+$XOffset,$YPos-$DisplayOffset+$YOffset,$Label,$CaptionSettings); + } + } + else + { + $XStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; + $X = $this->GraphAreaY1 + $XMargin; + $SerieOffset = isset($Serie["XOffset"]) ? $Serie["XOffset"] : 0; + + if ( $Type == BOUND_MAX || $Type == BOUND_BOTH ) + { + if ( $MaxLabelPos == BOUND_LABEL_POS_TOP || ( $MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue >= 0) ) { $YPos = $PosArray[$MaxPos] + $DisplayOffset + 2; $Align = TEXT_ALIGN_MIDDLELEFT; } + if ( $MaxLabelPos == BOUND_LABEL_POS_BOTTOM || ( $MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue < 0) ) { $YPos = $PosArray[$MaxPos] - $DisplayOffset + 2; $Align = TEXT_ALIGN_MIDDLERIGHT; } + + $XPos = $X + $MaxPos*$XStep + $SerieOffset; + $Label = $MaxLabelTxt.$this->scaleFormat($MaxValue,$Mode,$Format,$Unit); + + $TxtPos = $this->getTextBox($YPos,$XPos,$this->FontName,$this->FontSize,0,$Label); + $XOffset = 0; $YOffset = 0; + if ( $TxtPos[0]["X"] < $this->GraphAreaX1 ) { $XOffset = $this->GraphAreaX1 - $TxtPos[0]["X"]; } + if ( $TxtPos[1]["X"] > $this->GraphAreaX2 ) { $XOffset = -($TxtPos[1]["X"] - $this->GraphAreaX2); } + if ( $TxtPos[2]["Y"] < $this->GraphAreaY1 ) { $YOffset = ($this->GraphAreaY1 - $TxtPos[2]["Y"])/2; } + if ( $TxtPos[0]["Y"] > $this->GraphAreaY2 ) { $YOffset = -(($TxtPos[0]["Y"] - $this->GraphAreaY2)/2);} + + $CaptionSettings["R"] = $MaxDisplayR; $CaptionSettings["G"] = $MaxDisplayG; + $CaptionSettings["B"] = $MaxDisplayB; $CaptionSettings["Align"] = $Align; + + $this->drawText($YPos+$XOffset,$XPos+$YOffset,$Label,$CaptionSettings); + } + + if ( $Type == BOUND_MIN || $Type == BOUND_BOTH ) + { + if ( $MinLabelPos == BOUND_LABEL_POS_TOP || ( $MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue >= 0) ) { $YPos = $PosArray[$MinPos] + $DisplayOffset + 2; $Align = TEXT_ALIGN_MIDDLELEFT; } + if ( $MinLabelPos == BOUND_LABEL_POS_BOTTOM || ( $MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue < 0) ) { $YPos = $PosArray[$MinPos] - $DisplayOffset + 2; $Align = TEXT_ALIGN_MIDDLERIGHT; } + + $XPos = $X + $MinPos*$XStep + $SerieOffset; + $Label = $MinLabelTxt.$this->scaleFormat($MinValue,$Mode,$Format,$Unit); + + $TxtPos = $this->getTextBox($YPos,$XPos,$this->FontName,$this->FontSize,0,$Label); + $XOffset = 0; $YOffset = 0; + if ( $TxtPos[0]["X"] < $this->GraphAreaX1 ) { $XOffset = $this->GraphAreaX1 - $TxtPos[0]["X"]; } + if ( $TxtPos[1]["X"] > $this->GraphAreaX2 ) { $XOffset = -($TxtPos[1]["X"] - $this->GraphAreaX2); } + if ( $TxtPos[2]["Y"] < $this->GraphAreaY1 ) { $YOffset = ($this->GraphAreaY1 - $TxtPos[2]["Y"])/2; } + if ( $TxtPos[0]["Y"] > $this->GraphAreaY2 ) { $YOffset = -(($TxtPos[0]["Y"] - $this->GraphAreaY2)/2);} + + $CaptionSettings["R"] = $MinDisplayR; $CaptionSettings["G"] = $MinDisplayG; + $CaptionSettings["B"] = $MinDisplayB; $CaptionSettings["Align"] = $Align; + + $this->drawText($YPos+$XOffset,$XPos+$YOffset,$Label,$CaptionSettings); + } + } + } + } + } + + /* Draw a plot chart */ + function drawPlotChart($Format=NULL) + { + $PlotSize = isset($Format["PlotSize"]) ? $Format["PlotSize"] : NULL; + $PlotBorder = isset($Format["PlotBorder"]) ? $Format["PlotBorder"] : FALSE; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 50; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 50; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 50; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 30; + $BorderSize = isset($Format["BorderSize"]) ? $Format["BorderSize"] : 2; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; + $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : FALSE; + $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 4; + $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; + $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; + $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; + $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; + + $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; + + $Data = $this->DataSet->getData(); + list($XMargin,$XDivs) = $this->scaleGetXSettings(); + + foreach($Data["Series"] as $SerieName => $Serie) + { + if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) + { + if ( isset($Serie["Weight"]) ) { $SerieWeight = $Serie["Weight"] + 2; } else { $SerieWeight = 2; } + if ( $PlotSize != NULL ) { $SerieWeight = $PlotSize; } + + $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; + if ( $Surrounding != NULL ) { $BorderR = $R + $Surrounding; $BorderG = $G + $Surrounding; $BorderB = $B + $Surrounding; } + if ( isset($Serie["Picture"]) ) + { $Picture = $Serie["Picture"]; list($PicWidth,$PicHeight,$PicType) = $this->getPicInfo($Picture); } + else { $Picture = NULL; $PicOffset = 0; } + + if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; } + + $AxisID = $Serie["Axis"]; + $Shape = $Serie["Shape"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + if (isset($Serie["Description"])) { $SerieDescription = $Serie["Description"]; } else { $SerieDescription = $SerieName; } + + $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } + if ( $Picture != NULL ) { $PicOffset = $PicHeight / 2; $SerieWeight = 0; } + $X = $this->GraphAreaX1 + $XMargin; + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + foreach($PosArray as $Key => $Y) + { + if ( $DisplayValues ) + $this->drawText($X,$Y-$DisplayOffset-$SerieWeight-$BorderSize-$PicOffset,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); + + if ( $Y != VOID ) + { + if ( $RecordImageMap ) { $this->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$SerieWeight,$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + + if ( $Picture != NULL ) + { $this->drawFromPicture($PicType,$Picture,$X-$PicWidth/2,$Y-$PicHeight/2); } + else + { $this->drawShape($X,$Y,$Shape,$SerieWeight,$PlotBorder,$BorderSize,$R,$G,$B,$Alpha,$BorderR,$BorderG,$BorderB,$BorderAlpha); } + } + $X = $X + $XStep; + } + } + else + { + if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } + if ( $Picture != NULL ) { $PicOffset = $PicWidth / 2; $SerieWeight = 0; } + $Y = $this->GraphAreaY1 + $XMargin; + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + foreach($PosArray as $Key => $X) + { + if ( $DisplayValues ) + $this->drawText($X+$DisplayOffset+$SerieWeight+$BorderSize+$PicOffset,$Y,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("Angle"=>270,"R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); + + if ( $X != VOID ) + { + if ( $RecordImageMap ) { $this->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$SerieWeight,$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + + if ( $Picture != NULL ) + { $this->drawFromPicture($PicType,$Picture,$X-$PicWidth/2,$Y-$PicHeight/2); } + else + { $this->drawShape($X,$Y,$Shape,$SerieWeight,$PlotBorder,$BorderSize,$R,$G,$B,$Alpha,$BorderR,$BorderG,$BorderB,$BorderAlpha); } + } + $Y = $Y + $YStep; + } + } + } + } + } + + /* Draw a spline chart */ + function drawSplineChart($Format=NULL) + { + $BreakVoid = isset($Format["BreakVoid"]) ? $Format["BreakVoid"] : TRUE; + $VoidTicks = isset($Format["VoidTicks"]) ? $Format["VoidTicks"] : 4; + $BreakR = isset($Format["BreakR"]) ? $Format["BreakR"] : NULL; // 234 + $BreakG = isset($Format["BreakG"]) ? $Format["BreakG"] : NULL; // 55 + $BreakB = isset($Format["BreakB"]) ? $Format["BreakB"] : NULL; // 26 + $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : FALSE; + $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2; + $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; + $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; + $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; + $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; + $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 5; + + $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; + + $Data = $this->DataSet->getData(); + list($XMargin,$XDivs) = $this->scaleGetXSettings(); + foreach($Data["Series"] as $SerieName => $Serie) + { + if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) + { + $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; $Weight = $Serie["Weight"]; + + if ( $BreakR == NULL ) + $BreakSettings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$VoidTicks); + else + $BreakSettings = array("R"=>$BreakR,"G"=>$BreakG,"B"=>$BreakB,"Alpha"=>$Alpha,"Ticks"=>$VoidTicks,"Weight"=>$Weight); + + if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; } + + $AxisID = $Serie["Axis"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + if (isset($Serie["Description"])) { $SerieDescription = $Serie["Description"]; } else { $SerieDescription = $SerieName; } + + $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } + $X = $this->GraphAreaX1 + $XMargin; $WayPoints = ""; + $Force = $XStep / 5; + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + $LastGoodY = NULL; $LastGoodX = NULL; $LastX = 1; $LastY = 1; + foreach($PosArray as $Key => $Y) + { + if ( $DisplayValues ) + $this->drawText($X,$Y-$DisplayOffset,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); + + if ( $RecordImageMap && $Y != VOID ) { $this->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$ImageMapPlotSize,$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + + if ( $Y == VOID && $LastY != NULL ) + { $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); $WayPoints = ""; } + + if ( $Y != VOID && $LastY == NULL && $LastGoodY != NULL && !$BreakVoid ) + { $this->drawLine($LastGoodX,$LastGoodY,$X,$Y,$BreakSettings); } + + if ( $Y != VOID ) + $WayPoints[] = array($X,$Y); + + if ( $Y != VOID ) { $LastGoodY = $Y; $LastGoodX = $X; } + if ( $Y == VOID ) { $Y = NULL; } + + $LastX = $X; $LastY = $Y; + $X = $X + $XStep; + } + $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); + } + else + { + if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } + $Y = $this->GraphAreaY1 + $XMargin; $WayPoints = ""; + $Force = $YStep / 5; + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + $LastGoodY = NULL; $LastGoodX = NULL; $LastX = 1; $LastY = 1; + foreach($PosArray as $Key => $X) + { + if ( $DisplayValues ) + $this->drawText($X+$DisplayOffset,$Y,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("Angle"=>270,"R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); + + if ( $RecordImageMap && $X != VOID ) { $this->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$ImageMapPlotSize,$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + + if ( $X == VOID && $LastX != NULL ) + { $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); $WayPoints = ""; } + + if ( $X != VOID && $LastX == NULL && $LastGoodX != NULL && !$BreakVoid ) + { $this->drawLine($LastGoodX,$LastGoodY,$X,$Y,$BreakSettings); } + + if ( $X != VOID ) + $WayPoints[] = array($X,$Y); + + if ( $X != VOID ) { $LastGoodX = $X; $LastGoodY = $Y; } + if ( $X == VOID ) { $X = NULL; } + + $LastX = $X; $LastY = $Y; + $Y = $Y + $YStep; + } + $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); + } + } + } + } + + /* Draw a filled spline chart */ + function drawFilledSplineChart($Format=NULL) + { + $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : FALSE; + $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2; + $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; + $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; + $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; + $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; + $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : TRUE; + $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : NULL; + + $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; + + $Data = $this->DataSet->getData(); + list($XMargin,$XDivs) = $this->scaleGetXSettings(); + foreach($Data["Series"] as $SerieName => $Serie) + { + if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) + { + $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; + if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; } + + $AxisID = $Serie["Axis"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); + if ( $AroundZero ) { $YZero = $this->scaleComputeY(0,array("AxisID"=>$Serie["Axis"])); } + + if ( $Threshold != NULL ) + { + foreach($Threshold as $Key => $Params) + { + $Threshold[$Key]["MinX"] = $this->scaleComputeY($Params["Min"],array("AxisID"=>$Serie["Axis"])); + $Threshold[$Key]["MaxX"] = $this->scaleComputeY($Params["Max"],array("AxisID"=>$Serie["Axis"])); + } + } + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } + $X = $this->GraphAreaX1 + $XMargin; $WayPoints = ""; + $Force = $XStep / 5; + + if ( !$AroundZero ) { $YZero = $this->GraphAreaY2-1; } + if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; } + if ( $YZero < $this->GraphAreaY1+1 ) { $YZero = $this->GraphAreaY1+1; } + + $LastX = ""; $LastY = ""; + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + foreach($PosArray as $Key => $Y) + { + if ( $DisplayValues ) + $this->drawText($X,$Y-$DisplayOffset,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); + + if ( $Y == VOID ) + { + $Area = $this->drawSpline($WayPoints,array("Force"=>$Force,"PathOnly"=>TRUE)); + + if ( $Area != "" ) + { + foreach ($Area as $key => $Points) + { + $Corners = ""; $Corners[] = $Area[$key][0]["X"]; $Corners[] = $YZero; + foreach($Points as $subKey => $Point) + { + if ( $subKey == count($Points)-1) { $Corners[] = $Point["X"]-1; } else { $Corners[] = $Point["X"]; } + $Corners[] = $Point["Y"]+1; + } + $Corners[] = $Points[$subKey]["X"]-1; $Corners[] = $YZero; + + $this->drawPolygonChart($Corners,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/2,"NoBorder"=>TRUE,"Threshold"=>$Threshold)); + } + $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); + } + + $WayPoints = ""; + } + else + $WayPoints[] = array($X,$Y-.5); /* -.5 for AA visual fix */ + + $X = $X + $XStep; + } + $Area = $this->drawSpline($WayPoints,array("Force"=>$Force,"PathOnly"=>TRUE)); + + if ( $Area != "" ) + { + foreach ($Area as $key => $Points) + { + $Corners = ""; $Corners[] = $Area[$key][0]["X"]; $Corners[] = $YZero; + foreach($Points as $subKey => $Point) + { + if ( $subKey == count($Points)-1) { $Corners[] = $Point["X"]-1; } else { $Corners[] = $Point["X"]; } + $Corners[] = $Point["Y"]+1; + } + $Corners[] = $Points[$subKey]["X"]-1; $Corners[] = $YZero; + + $this->drawPolygonChart($Corners,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/2,"NoBorder"=>TRUE,"Threshold"=>$Threshold)); + } + $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); + } + } + else + { + if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } + $Y = $this->GraphAreaY1 + $XMargin; $WayPoints = ""; + $Force = $YStep / 5; + + if ( !$AroundZero ) { $YZero = $this->GraphAreaX1+1; } + if ( $YZero > $this->GraphAreaX2-1 ) { $YZero = $this->GraphAreaX2-1; } + if ( $YZero < $this->GraphAreaX1+1 ) { $YZero = $this->GraphAreaX1+1; } + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + foreach($PosArray as $Key => $X) + { + if ( $DisplayValues ) + $this->drawText($X+$DisplayOffset,$Y,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("Angle"=>270,"R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); + + if ( $X == VOID ) + { + $Area = $this->drawSpline($WayPoints,array("Force"=>$Force,"PathOnly"=>TRUE)); + + if ( $Area != "" ) + { + foreach ($Area as $key => $Points) + { + $Corners = ""; $Corners[] = $YZero; $Corners[] = $Area[$key][0]["Y"]; + foreach($Points as $subKey => $Point) + { + if ( $subKey == count($Points)-1) { $Corners[] = $Point["X"]-1; } else { $Corners[] = $Point["X"]; } + $Corners[] = $Point["Y"]; + } + $Corners[] = $YZero; $Corners[] = $Points[$subKey]["Y"]-1; + + $this->drawPolygonChart($Corners,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/2,"NoBorder"=>TRUE,"Threshold"=>$Threshold)); + } + $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); + } + + $WayPoints = ""; + } + else + $WayPoints[] = array($X,$Y); + + $Y = $Y + $YStep; + } + $Area = $this->drawSpline($WayPoints,array("Force"=>$Force,"PathOnly"=>TRUE)); + + if ( $Area != "" ) + { + foreach ($Area as $key => $Points) + { + $Corners = ""; $Corners[] = $YZero; $Corners[] = $Area[$key][0]["Y"]; + foreach($Points as $subKey => $Point) + { + if ( $subKey == count($Points)-1) { $Corners[] = $Point["X"]-1; } else { $Corners[] = $Point["X"]; } + $Corners[] = $Point["Y"]; + } + $Corners[] = $YZero; $Corners[] = $Points[$subKey]["Y"]-1; + + $this->drawPolygonChart($Corners,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/2,"NoBorder"=>TRUE,"Threshold"=>$Threshold)); + } + $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks)); + } + + } + } + } + } + + /* Draw a line chart */ + function drawLineChart($Format=NULL) + { + $BreakVoid = isset($Format["BreakVoid"]) ? $Format["BreakVoid"] : TRUE; + $VoidTicks = isset($Format["VoidTicks"]) ? $Format["VoidTicks"] : 4; + $BreakR = isset($Format["BreakR"]) ? $Format["BreakR"] : NULL; + $BreakG = isset($Format["BreakG"]) ? $Format["BreakG"] : NULL; + $BreakB = isset($Format["BreakB"]) ? $Format["BreakB"] : NULL; + $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : FALSE; + $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2; + $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; + $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; + $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; + $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; + $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 5; + $ForceColor = isset($Format["ForceColor"]) ? $Format["ForceColor"] : FALSE; + $ForceR = isset($Format["ForceR"]) ? $Format["ForceR"] : 0; + $ForceG = isset($Format["ForceG"]) ? $Format["ForceG"] : 0; + $ForceB = isset($Format["ForceB"]) ? $Format["ForceB"] : 0; + $ForceAlpha = isset($Format["ForceAlpha"]) ? $Format["ForceAlpha"] : 100; + + $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; + + $Data = $this->DataSet->getData(); + list($XMargin,$XDivs) = $this->scaleGetXSettings(); + foreach($Data["Series"] as $SerieName => $Serie) + { + if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) + { + $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; $Weight = $Serie["Weight"]; + + if ( $ForceColor ) + { $R = $ForceR; $G = $ForceG; $B = $ForceB; $Alpha = $ForceAlpha; } + + if ( $BreakR == NULL ) + $BreakSettings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$VoidTicks,"Weight"=>$Weight); + else + $BreakSettings = array("R"=>$BreakR,"G"=>$BreakG,"B"=>$BreakB,"Alpha"=>$Alpha,"Ticks"=>$VoidTicks,"Weight"=>$Weight); + + if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; } + + $AxisID = $Serie["Axis"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + if (isset($Serie["Description"])) { $SerieDescription = $Serie["Description"]; } else { $SerieDescription = $SerieName; } + + $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } + $X = $this->GraphAreaX1 + $XMargin; $LastX = NULL; $LastY = NULL; + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + $LastGoodY = NULL; $LastGoodX = NULL; + foreach($PosArray as $Key => $Y) + { + if ( $DisplayValues && $Serie["Data"][$Key] != VOID ) + { + if ( $Serie["Data"][$Key] > 0 ) { $Align = TEXT_ALIGN_BOTTOMMIDDLE; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_TOPMIDDLE; $Offset = -$DisplayOffset; } + $this->drawText($X,$Y-$Offset-$Weight,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>$Align)); + } + + if ( $RecordImageMap && $Y != VOID ) { $this->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$ImageMapPlotSize,$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + + if ( $Y != VOID && $LastX != NULL && $LastY != NULL ) + $this->drawLine($LastX,$LastY,$X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); + + if ( $Y != VOID && $LastY == NULL && $LastGoodY != NULL && !$BreakVoid ) + { $this->drawLine($LastGoodX,$LastGoodY,$X,$Y,$BreakSettings); $LastGoodY = NULL; } + + if ( $Y != VOID ) { $LastGoodY = $Y; $LastGoodX = $X; } + if ( $Y == VOID ) { $Y = NULL; } + + $LastX = $X; $LastY = $Y; + $X = $X + $XStep; + } + } + else + { + if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } + $Y = $this->GraphAreaY1 + $XMargin; $LastX = NULL; $LastY = NULL; + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + $LastGoodY = NULL; $LastGoodX = NULL; + foreach($PosArray as $Key => $X) + { + if ( $DisplayValues && $Serie["Data"][$Key] != VOID ) + { $this->drawText($X+$DisplayOffset+$Weight,$Y,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("Angle"=>270,"R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); } + + if ( $RecordImageMap && $X != VOID ) { $this->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$ImageMapPlotSize,$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + + if ( $X != VOID && $LastX != NULL && $LastY != NULL ) + $this->drawLine($LastX,$LastY,$X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); + + if ( $X != VOID && $LastX == NULL && $LastGoodY != NULL && !$BreakVoid ) + { $this->drawLine($LastGoodX,$LastGoodY,$X,$Y,$BreakSettings); $LastGoodY = NULL; } + + if ( $X != VOID ) { $LastGoodY = $Y; $LastGoodX = $X; } + if ( $X == VOID ) { $X = NULL; } + + $LastX = $X; $LastY = $Y; + $Y = $Y + $YStep; + } + } + } + } + } + + /* Draw a line chart */ + function drawZoneChart($SerieA,$SerieB,$Format=NULL) + { + $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0; + $LineR = isset($Format["LineR"]) ? $Format["LineR"] : 150; + $LineG = isset($Format["LineG"]) ? $Format["LineG"] : 150; + $LineB = isset($Format["LineB"]) ? $Format["LineB"] : 150; + $LineAlpha = isset($Format["LineAlpha"]) ? $Format["LineAlpha"] : 50; + $LineTicks = isset($Format["LineTicks"]) ? $Format["LineTicks"] : 1; + $AreaR = isset($Format["AreaR"]) ? $Format["AreaR"] : 150; + $AreaG = isset($Format["AreaG"]) ? $Format["AreaG"] : 150; + $AreaB = isset($Format["AreaB"]) ? $Format["AreaB"] : 150; + $AreaAlpha = isset($Format["AreaAlpha"]) ? $Format["AreaAlpha"] : 5; + + $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; + + $Data = $this->DataSet->getData(); + if ( !isset($Data["Series"][$SerieA]["Data"]) || !isset($Data["Series"][$SerieB]["Data"]) ) { return(0); } + $SerieAData = $Data["Series"][$SerieA]["Data"]; + $SerieBData = $Data["Series"][$SerieB]["Data"]; + + list($XMargin,$XDivs) = $this->scaleGetXSettings(); + + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + $PosArrayA = $this->scaleComputeY($SerieAData,array("AxisID"=>$AxisID)); + $PosArrayB = $this->scaleComputeY($SerieBData,array("AxisID"=>$AxisID)); + if ( count($PosArrayA) != count($PosArrayB) ) { return(0); } + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } + $X = $this->GraphAreaX1 + $XMargin; $LastX = NULL; $LastY = NULL; + + $LastX = NULL; $LastY1 = NULL; $LastY2 = NULL; + $BoundsA = ""; $BoundsB = ""; + foreach($PosArrayA as $Key => $Y1) + { + $Y2 = $PosArrayB[$Key]; + + $BoundsA[] = $X; $BoundsA[] = $Y1; + $BoundsB[] = $X; $BoundsB[] = $Y2; + + $LastX = $X; + $LastY1 = $Y1; $LastY2 = $Y2; + + $X = $X + $XStep; + } + $Bounds = array_merge($BoundsA,$this->reversePlots($BoundsB)); + $this->drawPolygonChart($Bounds,array("R"=>$AreaR,"G"=>$AreaG,"B"=>$AreaB,"Alpha"=>$AreaAlpha)); + + for($i=0;$i<=count($BoundsA)-4;$i=$i+2) + { + $this->drawLine($BoundsA[$i],$BoundsA[$i+1],$BoundsA[$i+2],$BoundsA[$i+3],array("R"=>$LineR,"G"=>$LineG,"B"=>$LineB,"Alpha"=>$LineAlpha,"Ticks"=>$LineTicks)); + $this->drawLine($BoundsB[$i],$BoundsB[$i+1],$BoundsB[$i+2],$BoundsB[$i+3],array("R"=>$LineR,"G"=>$LineG,"B"=>$LineB,"Alpha"=>$LineAlpha,"Ticks"=>$LineTicks)); + } + } + else + { + if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } + $Y = $this->GraphAreaY1 + $XMargin; $LastX = NULL; $LastY = NULL; + + $LastY = NULL; $LastX1 = NULL; $LastX2 = NULL; + $BoundsA = ""; $BoundsB = ""; + foreach($PosArrayA as $Key => $X1) + { + $X2 = $PosArrayB[$Key]; + + $BoundsA[] = $X1; $BoundsA[] = $Y; + $BoundsB[] = $X2; $BoundsB[] = $Y; + + $LastY = $Y; + $LastX1 = $X1; $LastX2 = $X2; + + $Y = $Y + $YStep; + } + $Bounds = array_merge($BoundsA,$this->reversePlots($BoundsB)); + $this->drawPolygonChart($Bounds,array("R"=>$AreaR,"G"=>$AreaG,"B"=>$AreaB,"Alpha"=>$AreaAlpha)); + + for($i=0;$i<=count($BoundsA)-4;$i=$i+2) + { + $this->drawLine($BoundsA[$i],$BoundsA[$i+1],$BoundsA[$i+2],$BoundsA[$i+3],array("R"=>$LineR,"G"=>$LineG,"B"=>$LineB,"Alpha"=>$LineAlpha,"Ticks"=>$LineTicks)); + $this->drawLine($BoundsB[$i],$BoundsB[$i+1],$BoundsB[$i+2],$BoundsB[$i+3],array("R"=>$LineR,"G"=>$LineG,"B"=>$LineB,"Alpha"=>$LineAlpha,"Ticks"=>$LineTicks)); + } + } + } + + /* Draw a step chart */ + function drawStepChart($Format=NULL) + { + $BreakVoid = isset($Format["BreakVoid"]) ? $Format["BreakVoid"] : FALSE; + $ReCenter = isset($Format["ReCenter"]) ? $Format["ReCenter"] : TRUE; + $VoidTicks = isset($Format["VoidTicks"]) ? $Format["VoidTicks"] : 4; + $BreakR = isset($Format["BreakR"]) ? $Format["BreakR"] : NULL; + $BreakG = isset($Format["BreakG"]) ? $Format["BreakG"] : NULL; + $BreakB = isset($Format["BreakB"]) ? $Format["BreakB"] : NULL; + $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] :FALSE; + $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2; + $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; + $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; + $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; + $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; + $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 5; + + $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; + + $Data = $this->DataSet->getData(); + list($XMargin,$XDivs) = $this->scaleGetXSettings(); + foreach($Data["Series"] as $SerieName => $Serie) + { + if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) + { + $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; $Weight = $Serie["Weight"]; + + if (isset($Serie["Description"])) { $SerieDescription = $Serie["Description"]; } else { $SerieDescription = $SerieName; } + + if ( $BreakR == NULL ) + $BreakSettings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$VoidTicks,"Weight"=>$Weight); + else + $BreakSettings = array("R"=>$BreakR,"G"=>$BreakG,"B"=>$BreakB,"Alpha"=>$Alpha,"Ticks"=>$VoidTicks,"Weight"=>$Weight); + + if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; } + + $AxisID = $Serie["Axis"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight); + + $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } + $X = $this->GraphAreaX1 + $XMargin; $LastX = NULL; $LastY = NULL; + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + $LastGoodY = NULL; $LastGoodX = NULL; $Init = FALSE; + foreach($PosArray as $Key => $Y) + { + if ( $DisplayValues && $Serie["Data"][$Key] != VOID ) + { + if ( $Y <= $LastY ) { $Align = TEXT_ALIGN_BOTTOMMIDDLE; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_TOPMIDDLE; $Offset = -$DisplayOffset; } + $this->drawText($X,$Y-$Offset-$Weight,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>$Align)); + } + + if ( $Y != VOID && $LastX != NULL && $LastY != NULL ) + { + $this->drawLine($LastX,$LastY,$X,$LastY,$Color); + $this->drawLine($X,$LastY,$X,$Y,$Color); + if ( $ReCenter && $X+$XStep < $this->GraphAreaX2 - $XMargin ) + { + $this->drawLine($X,$Y,$X+$XStep,$Y,$Color); + if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X-$ImageMapPlotSize).",".floor($Y-$ImageMapPlotSize).",".floor($X+$XStep+$ImageMapPlotSize).",".floor($Y+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + } + else + { if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastX-$ImageMapPlotSize).",".floor($LastY-$ImageMapPlotSize).",".floor($X+$ImageMapPlotSize).",".floor($LastY+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } } + } + + if ( $Y != VOID && $LastY == NULL && $LastGoodY != NULL && !$BreakVoid ) + { + if ( $ReCenter ) + { + $this->drawLine($LastGoodX+$XStep,$LastGoodY,$X,$LastGoodY,$BreakSettings); + if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastGoodX+$XStep-$ImageMapPlotSize).",".floor($LastGoodY-$ImageMapPlotSize).",".floor($X+$ImageMapPlotSize).",".floor($LastGoodY+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + } + else + { + $this->drawLine($LastGoodX,$LastGoodY,$X,$LastGoodY,$BreakSettings); + if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastGoodX-$ImageMapPlotSize).",".floor($LastGoodY-$ImageMapPlotSize).",".floor($X+$ImageMapPlotSize).",".floor($LastGoodY+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + } + + $this->drawLine($X,$LastGoodY,$X,$Y,$BreakSettings); + $LastGoodY = NULL; + } + elseif( !$BreakVoid && $LastGoodY == NULL && $Y != VOID ) + { + $this->drawLine($this->GraphAreaX1 + $XMargin,$Y,$X,$Y,$BreakSettings); + if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($this->GraphAreaX1+$XMargin-$ImageMapPlotSize).",".floor($Y-$ImageMapPlotSize).",".floor($X+$ImageMapPlotSize).",".floor($Y+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + } + + if ( $Y != VOID ) { $LastGoodY = $Y; $LastGoodX = $X; } + if ( $Y == VOID ) { $Y = NULL; } + + if ( !$Init && $ReCenter ) { $X = $X - $XStep/2; $Init = TRUE; } + $LastX = $X; $LastY = $Y; + if ( $LastX < $this->GraphAreaX1 + $XMargin ) { $LastX = $this->GraphAreaX1 + $XMargin; } + $X = $X + $XStep; + } + if ( $ReCenter ) + { + $this->drawLine($LastX,$LastY,$this->GraphAreaX2 - $XMargin,$LastY,$Color); + if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastX-$ImageMapPlotSize).",".floor($LastY-$ImageMapPlotSize).",".floor($this->GraphAreaX2-$XMargin+$ImageMapPlotSize).",".floor($LastY+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + } + } + else + { + if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } + $Y = $this->GraphAreaY1 + $XMargin; $LastX = NULL; $LastY = NULL; + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + $LastGoodY = NULL; $LastGoodX = NULL; $Init = FALSE; + foreach($PosArray as $Key => $X) + { + if ( $DisplayValues && $Serie["Data"][$Key] != VOID ) + { + if ( $X >= $LastX ) { $Align = TEXT_ALIGN_MIDDLELEFT; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_MIDDLERIGHT; $Offset = -$DisplayOffset; } + $this->drawText($X+$Offset+$Weight,$Y,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>$Align)); + } + + if ( $X != VOID && $LastX != NULL && $LastY != NULL ) + { + $this->drawLine($LastX,$LastY,$LastX,$Y,$Color); + $this->drawLine($LastX,$Y,$X,$Y,$Color); + + if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastX-$ImageMapPlotSize).",".floor($LastY-$ImageMapPlotSize).",".floor($LastX+$XStep+$ImageMapPlotSize).",".floor($Y+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + } + + if ( $X != VOID && $LastX == NULL && $LastGoodY != NULL && !$BreakVoid ) + { + $this->drawLine($LastGoodX,$LastGoodY,$LastGoodX,$LastGoodY+$YStep,$Color); + if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastGoodX-$ImageMapPlotSize).",".floor($LastGoodY-$ImageMapPlotSize).",".floor($LastGoodX+$ImageMapPlotSize).",".floor($LastGoodY+$YStep+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + + $this->drawLine($LastGoodX,$LastGoodY+$YStep,$LastGoodX,$Y,$BreakSettings); + if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastGoodX-$ImageMapPlotSize).",".floor($LastGoodY+$YStep-$ImageMapPlotSize).",".floor($LastGoodX+$ImageMapPlotSize).",".floor($YStep+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + + $this->drawLine($LastGoodX,$Y,$X,$Y,$BreakSettings); + $LastGoodY = NULL; + } + elseif ( $X != VOID && $LastGoodY == NULL && !$BreakVoid ) + { + $this->drawLine($X,$this->GraphAreaY1 + $XMargin,$X,$Y,$BreakSettings); + if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X-$ImageMapPlotSize).",".floor($this->GraphAreaY1+$XMargin-$ImageMapPlotSize).",".floor($X+$ImageMapPlotSize).",".floor($Y+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + } + + if ( $X != VOID ) { $LastGoodY = $Y; $LastGoodX = $X; } + if ( $X == VOID ) { $X = NULL; } + + if ( !$Init && $ReCenter ) { $Y = $Y - $YStep/2; $Init = TRUE; } + $LastX = $X; $LastY = $Y; + if ( $LastY < $this->GraphAreaY1 + $XMargin ) { $LastY = $this->GraphAreaY1 + $XMargin; } + $Y = $Y + $YStep; + } + if ( $ReCenter ) + { + $this->drawLine($LastX,$LastY,$LastX,$this->GraphAreaY2 - $XMargin,$Color); + if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastX-$ImageMapPlotSize).",".floor($LastY-$ImageMapPlotSize).",".floor($LastX+$ImageMapPlotSize).",".floor($this->GraphAreaY2-$XMargin+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + } + } + } + } + } + + /* Draw a step chart */ + function drawFilledStepChart($Format=NULL) + { + $ReCenter = isset($Format["ReCenter"]) ? $Format["ReCenter"] : TRUE; + $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] :FALSE; + $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2; + $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; + $ForceTransparency = isset($Format["ForceTransparency"]) ? $Format["ForceTransparency"] : NULL; + $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; + $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; + $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; + $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : TRUE; + + $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; + + $Data = $this->DataSet->getData(); + list($XMargin,$XDivs) = $this->scaleGetXSettings(); + foreach($Data["Series"] as $SerieName => $Serie) + { + if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) + { + $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; $Weight = $Serie["Weight"]; + + if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; } + + $AxisID = $Serie["Axis"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + $Color = array("R"=>$R,"G"=>$G,"B"=>$B); + if ( $ForceTransparency != NULL ) { $Color["Alpha"] = $ForceTransparency; } else { $Color["Alpha"] = $Alpha; } + + $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); + $YZero = $this->scaleComputeY(0,array("AxisID"=>$Serie["Axis"])); + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; } + if ( $YZero < $this->GraphAreaY1+1 ) { $YZero = $this->GraphAreaY1+1; } + + if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } + $X = $this->GraphAreaX1 + $XMargin; $LastX = NULL; $LastY = NULL; + + if ( !$AroundZero ) { $YZero = $this->GraphAreaY2-1; } + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + $LastGoodY = NULL; $LastGoodX = NULL; $Points = ""; $Init = FALSE; + foreach($PosArray as $Key => $Y) + { + if ( $Y == VOID && $LastX != NULL && $LastY != NULL && $Points !="" ) + { + $Points[] = $LastX; $Points[] = $LastY; + $Points[] = $X; $Points[] = $LastY; + $Points[] = $X; $Points[] = $YZero; + $this->drawPolygon($Points,$Color); + $Points = ""; + } + + if ( $Y != VOID && $LastX != NULL && $LastY != NULL ) + { + if ( $Points == "") { $Points[] = $LastX; $Points[] = $YZero; } + $Points[] = $LastX; $Points[] = $LastY; + $Points[] = $X; $Points[] = $LastY; + $Points[] = $X; $Points[] = $Y; + } + + if ( $Y != VOID ) { $LastGoodY = $Y; $LastGoodX = $X; } + if ( $Y == VOID ) { $Y = NULL; } + + if ( !$Init && $ReCenter ) { $X = $X - $XStep/2; $Init = TRUE; } + $LastX = $X; $LastY = $Y; + if ( $LastX < $this->GraphAreaX1 + $XMargin ) { $LastX = $this->GraphAreaX1 + $XMargin; } + $X = $X + $XStep; + } + + if ( $ReCenter ) + { + $Points[] = $LastX+$XStep/2; $Points[] = $LastY; + $Points[] = $LastX+$XStep/2; $Points[] = $YZero; + } + else + { $Points[] = $LastX; $Points[] = $YZero; } + + $this->drawPolygon($Points,$Color); + } + else + { + if ( $YZero < $this->GraphAreaX1+1 ) { $YZero = $this->GraphAreaX1+1; } + if ( $YZero > $this->GraphAreaX2-1 ) { $YZero = $this->GraphAreaX2-1; } + + if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } + $Y = $this->GraphAreaY1 + $XMargin; $LastX = NULL; $LastY = NULL; + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + $LastGoodY = NULL; $LastGoodX = NULL; $Points = ""; + foreach($PosArray as $Key => $X) + { + if ( $X == VOID && $LastX != NULL && $LastY != NULL && $Points !="" ) + { + $Points[] = $LastX; $Points[] = $LastY; + $Points[] = $LastX; $Points[] = $Y; + $Points[] = $YZero; $Points[] = $Y; + $this->drawPolygon($Points,$Color); + $Points = ""; + } + + if ( $X != VOID && $LastX != NULL && $LastY != NULL ) + { + if ( $Points == "") { $Points[] = $YZero; $Points[] = $LastY; } + $Points[] = $LastX; $Points[] = $LastY; + $Points[] = $LastX; $Points[] = $Y; + $Points[] = $X; $Points[] = $Y; + } + + if ( $X != VOID ) { $LastGoodY = $Y; $LastGoodX = $X; } + if ( $X == VOID ) { $X = NULL; } + + if ( $LastX == NULL && $ReCenter ) { $Y = $Y - $YStep/2; } + $LastX = $X; $LastY = $Y; + if ( $LastY < $this->GraphAreaY1 + $XMargin ) { $LastY = $this->GraphAreaY1 + $XMargin; } + $Y = $Y + $YStep; + } + + if ( $ReCenter ) + { + $Points[] = $LastX; $Points[] = $LastY+$YStep/2; + $Points[] = $YZero; $Points[] = $LastY+$YStep/2; + } + else + { $Points[] = $YZero; $Points[] = $LastY; } + + $this->drawPolygon($Points,$Color); + } + } + } + } + + /* Draw an area chart */ + function drawAreaChart($Format=NULL) + { + $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : FALSE; + $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2; + $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; + $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; + $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; + $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; + $ForceTransparency = isset($Format["ForceTransparency"]) ? $Format["ForceTransparency"] : 25; + $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : TRUE; + $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : NULL; + + $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; + + $Data = $this->DataSet->getData(); + list($XMargin,$XDivs) = $this->scaleGetXSettings(); + + foreach($Data["Series"] as $SerieName => $Serie) + { + if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) + { + $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; + if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; } + + $AxisID = $Serie["Axis"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); + $YZero = $this->scaleComputeY(0,array("AxisID"=>$Serie["Axis"])); + + if ( $Threshold != NULL ) + { + foreach($Threshold as $Key => $Params) + { + $Threshold[$Key]["MinX"] = $this->scaleComputeY($Params["Min"],array("AxisID"=>$Serie["Axis"])); + $Threshold[$Key]["MaxX"] = $this->scaleComputeY($Params["Max"],array("AxisID"=>$Serie["Axis"])); + } + } + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; } + + $Areas = ""; $AreaID = 0; + $Areas[$AreaID][] = $this->GraphAreaX1 + $XMargin; + if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaY2-1; } + + if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } + $X = $this->GraphAreaX1 + $XMargin; $LastX = NULL; $LastY = NULL; + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + foreach($PosArray as $Key => $Y) + { + if ( $DisplayValues && $Serie["Data"][$Key] != VOID ) + { + if ( $Serie["Data"][$Key] > 0 ) { $Align = TEXT_ALIGN_BOTTOMMIDDLE; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_TOPMIDDLE; $Offset = -$DisplayOffset; } + $this->drawText($X,$Y-$Offset,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>$Align)); + } + + if ( $Y == VOID && isset($Areas[$AreaID]) ) + { + if($LastX == NULL) + { $Areas[$AreaID][] = $X; } + else + { $Areas[$AreaID][] = $LastX; } + + if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaY2-1; } + $AreaID++; + } + elseif ($Y != VOID) + { + if ( !isset($Areas[$AreaID]) ) + { + $Areas[$AreaID][] = $X; + if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaY2-1; } + } + + $Areas[$AreaID][] = $X; + $Areas[$AreaID][] = $Y; + } + + $LastX = $X; + $X = $X + $XStep; + } + $Areas[$AreaID][] = $LastX; + if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaY2-1; } + + /* Handle shadows in the areas */ + if ( $this->Shadow ) + { + $ShadowArea = ""; + foreach($Areas as $Key => $Points) + { + $ShadowArea[$Key] = ""; + foreach($Points as $Key2 => $Value) + { + if ( $Key2 % 2 == 0 ) + { $ShadowArea[$Key][] = $Value + $this->ShadowX; } + else + { $ShadowArea[$Key][] = $Value + $this->ShadowY; } + } + } + + foreach($ShadowArea as $Key => $Points) + $this->drawPolygonChart($Points,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa)); + } + + $Alpha = $ForceTransparency != NULL ? $ForceTransparency : $Alpha; + $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Threshold"=>$Threshold); + + foreach($Areas as $Key => $Points) + $this->drawPolygonChart($Points,$Color); + } + else + { + if ( $YZero < $this->GraphAreaX1+1 ) { $YZero = $this->GraphAreaX1+1; } + if ( $YZero > $this->GraphAreaX2-1 ) { $YZero = $this->GraphAreaX2-1; } + + $Areas = ""; $AreaID = 0; + if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaX1+1; } + $Areas[$AreaID][] = $this->GraphAreaY1 + $XMargin; + + if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } + $Y = $this->GraphAreaY1 + $XMargin; $LastX = NULL; $LastY = NULL; + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + foreach($PosArray as $Key => $X) + { + if ( $DisplayValues && $Serie["Data"][$Key] != VOID ) + { + if ( $Serie["Data"][$Key] > 0 ) { $Align = TEXT_ALIGN_BOTTOMMIDDLE; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_TOPMIDDLE; $Offset = -$DisplayOffset; } + $this->drawText($X+$Offset,$Y,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("Angle"=>270,"R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>$Align)); + } + + if ( $X == VOID && isset($Areas[$AreaID]) ) + { + if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaX1+1; } + + if($LastY == NULL) + { $Areas[$AreaID][] = $Y; } + else + { $Areas[$AreaID][] = $LastY; } + + $AreaID++; + } + elseif ($X != VOID) + { + if ( !isset($Areas[$AreaID]) ) + { + if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaX1+1; } + $Areas[$AreaID][] = $Y; + } + + $Areas[$AreaID][] = $X; + $Areas[$AreaID][] = $Y; + } + + $LastX = $X; $LastY = $Y; + $Y = $Y + $YStep; + } + if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaX1+1; } + $Areas[$AreaID][] = $LastY; + + /* Handle shadows in the areas */ + if ( $this->Shadow ) + { + $ShadowArea = ""; + foreach($Areas as $Key => $Points) + { + $ShadowArea[$Key] = ""; + foreach($Points as $Key2 => $Value) + { + if ( $Key2 % 2 == 0 ) + { $ShadowArea[$Key][] = $Value + $this->ShadowX; } + else + { $ShadowArea[$Key][] = $Value + $this->ShadowY; } + } + } + + foreach($ShadowArea as $Key => $Points) + $this->drawPolygonChart($Points,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa)); + } + + $Alpha = $ForceTransparency != NULL ? $ForceTransparency : $Alpha; + $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Threshold"=>$Threshold); + + foreach($Areas as $Key => $Points) + $this->drawPolygonChart($Points,$Color); + } + } + } + } + + + /* Draw a bar chart */ + function drawBarChart($Format=NULL) + { + $Floating0Serie = isset($Format["Floating0Serie"]) ? $Format["Floating0Serie"] : NULL; + $Floating0Value = isset($Format["Floating0Value"]) ? $Format["Floating0Value"] : NULL; + $Draw0Line = isset($Format["Draw0Line"]) ? $Format["Draw0Line"] : FALSE; + $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : FALSE; + $DisplayOrientation = isset($Format["DisplayOrientation"]) ? $Format["DisplayOrientation"] : ORIENTATION_HORIZONTAL; + $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2; + $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; + $DisplayFont = isset($Format["DisplaySize"]) ? $Format["DisplaySize"] : $this->FontName; + $DisplaySize = isset($Format["DisplaySize"]) ? $Format["DisplaySize"] : $this->FontSize; + $DisplayPos = isset($Format["DisplayPos"]) ? $Format["DisplayPos"] : LABEL_POS_OUTSIDE; + $DisplayShadow = isset($Format["DisplayShadow"]) ? $Format["DisplayShadow"] : TRUE; + $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; + $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; + $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; + $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : TRUE; + $Interleave = isset($Format["Interleave"]) ? $Format["Interleave"] : .5; + $Rounded = isset($Format["Rounded"]) ? $Format["Rounded"] : FALSE; + $RoundRadius = isset($Format["RoundRadius"]) ? $Format["RoundRadius"] : 4; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1; + $Gradient = isset($Format["Gradient"]) ? $Format["Gradient"] : FALSE; + $GradientMode = isset($Format["GradientMode"]) ? $Format["GradientMode"] : GRADIENT_SIMPLE; + $GradientAlpha = isset($Format["GradientAlpha"]) ? $Format["GradientAlpha"] : 20; + $GradientStartR = isset($Format["GradientStartR"]) ? $Format["GradientStartR"] : 255; + $GradientStartG = isset($Format["GradientStartG"]) ? $Format["GradientStartG"] : 255; + $GradientStartB = isset($Format["GradientStartB"]) ? $Format["GradientStartB"] : 255; + $GradientEndR = isset($Format["GradientEndR"]) ? $Format["GradientEndR"] : 0; + $GradientEndG = isset($Format["GradientEndG"]) ? $Format["GradientEndG"] : 0; + $GradientEndB = isset($Format["GradientEndB"]) ? $Format["GradientEndB"] : 0; + $TxtMargin = isset($Format["TxtMargin"]) ? $Format["TxtMargin"] : 6; + $OverrideColors = isset($Format["OverrideColors"]) ? $Format["OverrideColors"] : NULL; + $OverrideSurrounding = isset($Format["OverrideSurrounding"]) ? $Format["OverrideSurrounding"] : 30; + $InnerSurrounding = isset($Format["InnerSurrounding"]) ? $Format["InnerSurrounding"] : NULL; + $InnerBorderR = isset($Format["InnerBorderR"]) ? $Format["InnerBorderR"] : -1; + $InnerBorderG = isset($Format["InnerBorderG"]) ? $Format["InnerBorderG"] : -1; + $InnerBorderB = isset($Format["InnerBorderB"]) ? $Format["InnerBorderB"] : -1; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; + + $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR; + + $Data = $this->DataSet->getData(); + list($XMargin,$XDivs) = $this->scaleGetXSettings(); + + if ( $OverrideColors != NULL ) + { + $OverrideColors = $this->validatePalette($OverrideColors,$OverrideSurrounding); + $this->DataSet->saveExtendedData("Palette",$OverrideColors); + } + + $RestoreShadow = $this->Shadow; + + $SeriesCount = $this->countDrawableSeries(); + $CurrentSerie = 0; + foreach($Data["Series"] as $SerieName => $Serie) + { + if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) + { + $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; + if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; } + if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; } + if ( $InnerSurrounding != NULL ) { $InnerBorderR = $R+$InnerSurrounding; $InnerBorderG = $G+$InnerSurrounding; $InnerBorderB = $B+$InnerSurrounding; } + if ( $InnerBorderR == -1 ) { $InnerColor = NULL; } else { $InnerColor = array("R"=>$InnerBorderR,"G"=>$InnerBorderG,"B"=>$InnerBorderB); } + $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB); + + $AxisID = $Serie["Axis"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + if (isset($Serie["Description"])) { $SerieDescription = $Serie["Description"]; } else { $SerieDescription = $SerieName; } + + $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); + + if ( $Floating0Value != NULL ) + { $YZero = $this->scaleComputeY($Floating0Value,array("AxisID"=>$Serie["Axis"])); } + else + { $YZero = $this->scaleComputeY(0,array("AxisID"=>$Serie["Axis"])); } + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; } + if ( $YZero < $this->GraphAreaY1+1 ) { $YZero = $this->GraphAreaY1+1; } + + if ( $XDivs == 0 ) { $XStep = 0; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } + $X = $this->GraphAreaX1 + $XMargin; + + if ( $AroundZero ) { $Y1 = $YZero; } else { $Y1 = $this->GraphAreaY2-1; } + if ( $XDivs == 0 ) { $XSize = ($this->GraphAreaX2-$this->GraphAreaX1)/($SeriesCount+$Interleave); } else { $XSize = ($XStep / ($SeriesCount+$Interleave) ); } + + $XOffset = -($XSize*$SeriesCount)/2 + $CurrentSerie * $XSize; + if ( $X + $XOffset <= $this->GraphAreaX1 ) { $XOffset = $this->GraphAreaX1 - $X + 1 ; } + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = $XOffset + $XSize / 2; + + if ( $Rounded || $BorderR != -1) { $XSpace = 1; } else { $XSpace = 0; } + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + + $ID = 0; + foreach($PosArray as $Key => $Y2) + { + if ( $Floating0Serie != NULL ) + { + if ( isset($Data["Series"][$Floating0Serie]["Data"][$Key]) ) + { $Value = $Data["Series"][$Floating0Serie]["Data"][$Key]; } + else + { $Value = 0; } + + $YZero = $this->scaleComputeY($Value,array("AxisID"=>$Serie["Axis"])); + if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; } + if ( $YZero < $this->GraphAreaY1+1 ) { $YZero = $this->GraphAreaY1+1; } + + if ( $AroundZero ) { $Y1 = $YZero; } else { $Y1 = $this->GraphAreaY2-1; } + } + + if ( $OverrideColors != NULL ) + { if ( isset($OverrideColors[$ID]) ) { $Color = array("R"=>$OverrideColors[$ID]["R"],"G"=>$OverrideColors[$ID]["G"],"B"=>$OverrideColors[$ID]["B"],"Alpha"=>$OverrideColors[$ID]["Alpha"],"BorderR"=>$OverrideColors[$ID]["BorderR"],"BorderG"=>$OverrideColors[$ID]["BorderG"],"BorderB"=>$OverrideColors[$ID]["BorderB"]); } else { $Color = $this->getRandomColor(); } } + + if ( $Y2 != VOID ) + { + $BarHeight = $Y1 - $Y2; + + if ( $Serie["Data"][$Key] == 0 ) + { + $this->drawLine($X+$XOffset+$XSpace,$Y1,$X+$XOffset+$XSize-$XSpace,$Y1,$Color); + if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X+$XOffset+$XSpace).",".floor($Y1-1).",".floor($X+$XOffset+$XSize-$XSpace).",".floor($Y1+1),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + } + else + { + if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X+$XOffset+$XSpace).",".floor($Y1).",".floor($X+$XOffset+$XSize-$XSpace).",".floor($Y2),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + + if ( $Rounded ) + $this->drawRoundedFilledRectangle($X+$XOffset+$XSpace,$Y1,$X+$XOffset+$XSize-$XSpace,$Y2,$RoundRadius,$Color); + else + { + $this->drawFilledRectangle($X+$XOffset+$XSpace,$Y1,$X+$XOffset+$XSize-$XSpace,$Y2,$Color); + + if ( $InnerColor != NULL ) { $this->drawRectangle($X+$XOffset+$XSpace+1,min($Y1,$Y2)+1,$X+$XOffset+$XSize-$XSpace-1,max($Y1,$Y2)-1,$InnerColor); } + + if ( $Gradient ) + { + $this->Shadow = FALSE; + + if ( $GradientMode == GRADIENT_SIMPLE ) + { + if ( $Serie["Data"][$Key] >= 0 ) + $GradienColor = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha); + else + $GradienColor = array("StartR"=>$GradientEndR,"StartG"=>$GradientEndG,"StartB"=>$GradientEndB,"EndR"=>$GradientStartR,"EndG"=>$GradientStartG,"EndB"=>$GradientStartB,"Alpha"=>$GradientAlpha); + + $this->drawGradientArea($X+$XOffset+$XSpace,$Y1,$X+$XOffset+$XSize-$XSpace,$Y2,DIRECTION_VERTICAL,$GradienColor); + } + elseif ( $GradientMode == GRADIENT_EFFECT_CAN ) + { + $GradienColor1 = array("StartR"=>$GradientEndR,"StartG"=>$GradientEndG,"StartB"=>$GradientEndB,"EndR"=>$GradientStartR,"EndG"=>$GradientStartG,"EndB"=>$GradientStartB,"Alpha"=>$GradientAlpha); + $GradienColor2 = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha); + $XSpan = floor($XSize / 3); + + $this->drawGradientArea($X+$XOffset+$XSpace,$Y1,$X+$XOffset+$XSpan-$XSpace,$Y2,DIRECTION_HORIZONTAL,$GradienColor1); + $this->drawGradientArea($X+$XOffset+$XSpan+$XSpace,$Y1,$X+$XOffset+$XSize-$XSpace,$Y2,DIRECTION_HORIZONTAL,$GradienColor2); + } + $this->Shadow = $RestoreShadow; + } + } + + if ( $Draw0Line ) + { + $Line0Color = array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20); + + if ( abs($Y1 - $Y2) > 3 ) { $Line0Width = 3; } else { $Line0Width = 1; } + if ( $Y1 - $Y2 < 0 ) { $Line0Width = -$Line0Width; } + + $this->drawFilledRectangle($X+$XOffset+$XSpace,floor($Y1),$X+$XOffset+$XSize-$XSpace,floor($Y1)-$Line0Width,$Line0Color); + $this->drawLine($X+$XOffset+$XSpace,floor($Y1),$X+$XOffset+$XSize-$XSpace,floor($Y1),$Line0Color); + } + } + + if ( $DisplayValues && $Serie["Data"][$Key] != VOID ) + { + if ( $DisplayShadow ) { $this->Shadow = TRUE; } + + $Caption = $this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit); + $TxtPos = $this->getTextBox(0,0,$DisplayFont,$DisplaySize,90,$Caption); + $TxtHeight = $TxtPos[0]["Y"] - $TxtPos[1]["Y"] + $TxtMargin; + + if ( $DisplayPos == LABEL_POS_INSIDE && abs($TxtHeight) < abs($BarHeight) ) + { + $CenterX = (($X+$XOffset+$XSize-$XSpace)-($X+$XOffset+$XSpace))/2 + $X+$XOffset+$XSpace; + $CenterY = ($Y2-$Y1)/2 + $Y1; + + $this->drawText($CenterX,$CenterY,$Caption,array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontSize"=>$DisplaySize,"Angle"=>90)); + } + else + { + if ( $Serie["Data"][$Key] >= 0 ) { $Align = TEXT_ALIGN_BOTTOMMIDDLE; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_TOPMIDDLE; $Offset = -$DisplayOffset; } + $this->drawText($X+$XOffset+$XSize/2,$Y2-$Offset,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>$Align,"FontSize"=>$DisplaySize)); + } + + $this->Shadow = $RestoreShadow; + } + } + + $X = $X + $XStep; + $ID++; + } + } + else + { + if ( $YZero < $this->GraphAreaX1+1 ) { $YZero = $this->GraphAreaX1+1; } + if ( $YZero > $this->GraphAreaX2-1 ) { $YZero = $this->GraphAreaX2-1; } + + if ( $XDivs == 0 ) { $YStep = 0; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } + + $Y = $this->GraphAreaY1 + $XMargin; + + if ( $AroundZero ) { $X1 = $YZero; } else { $X1 = $this->GraphAreaX1+1; } + if ( $XDivs == 0 ) { $YSize = ($this->GraphAreaY2-$this->GraphAreaY1)/($SeriesCount+$Interleave); } else { $YSize = ($YStep / ($SeriesCount+$Interleave) ); } + + $YOffset = -($YSize*$SeriesCount)/2 + $CurrentSerie * $YSize; + if ( $Y + $YOffset <= $this->GraphAreaY1 ) { $YOffset = $this->GraphAreaY1 - $Y + 1 ; } + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = $YOffset + $YSize / 2; + + if ( $Rounded || $BorderR != -1 ) { $YSpace = 1; } else { $YSpace = 0; } + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + + $ID = 0 ; + foreach($PosArray as $Key => $X2) + { + if ( $Floating0Serie != NULL ) + { + if ( isset($Data["Series"][$Floating0Serie]["Data"][$Key]) ) + $Value = $Data["Series"][$Floating0Serie]["Data"][$Key]; + else { $Value = 0; } + + $YZero = $this->scaleComputeY($Value,array("AxisID"=>$Serie["Axis"])); + if ( $YZero < $this->GraphAreaX1+1 ) { $YZero = $this->GraphAreaX1+1; } + if ( $YZero > $this->GraphAreaX2-1 ) { $YZero = $this->GraphAreaX2-1; } + if ( $AroundZero ) { $X1 = $YZero; } else { $X1 = $this->GraphAreaX1+1; } + } + + if ( $OverrideColors != NULL ) + { if ( isset($OverrideColors[$ID]) ) { $Color = array("R"=>$OverrideColors[$ID]["R"],"G"=>$OverrideColors[$ID]["G"],"B"=>$OverrideColors[$ID]["B"],"Alpha"=>$OverrideColors[$ID]["Alpha"],"BorderR"=>$OverrideColors[$ID]["BorderR"],"BorderG"=>$OverrideColors[$ID]["BorderG"],"BorderB"=>$OverrideColors[$ID]["BorderB"]); } else { $Color = $this->getRandomColor(); } } + + if ( $X2 != VOID ) + { + $BarWidth = $X2 - $X1; + + if ( $Serie["Data"][$Key] == 0 ) + { + $this->drawLine($X1,$Y+$YOffset+$YSpace,$X1,$Y+$YOffset+$YSize-$YSpace,$Color); + if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X1-1).",".floor($Y+$YOffset+$YSpace).",".floor($X1+1).",".floor($Y+$YOffset+$YSize-$YSpace),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + } + else + { + if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X1).",".floor($Y+$YOffset+$YSpace).",".floor($X2).",".floor($Y+$YOffset+$YSize-$YSpace),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + + if ( $Rounded ) + $this->drawRoundedFilledRectangle($X1+1,$Y+$YOffset+$YSpace,$X2,$Y+$YOffset+$YSize-$YSpace,$RoundRadius,$Color); + else + { + $this->drawFilledRectangle($X1,$Y+$YOffset+$YSpace,$X2,$Y+$YOffset+$YSize-$YSpace,$Color); + + if ( $InnerColor != NULL ) { $this->drawRectangle(min($X1,$X2)+1,$Y+$YOffset+$YSpace+1,max($X1,$X2)-1,$Y+$YOffset+$YSize-$YSpace-1,$InnerColor); } + + if ( $Gradient ) + { + $this->Shadow = FALSE; + + if ( $GradientMode == GRADIENT_SIMPLE ) + { + if ( $Serie["Data"][$Key] >= 0 ) + $GradienColor = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha); + else + $GradienColor = array("StartR"=>$GradientEndR,"StartG"=>$GradientEndG,"StartB"=>$GradientEndB,"EndR"=>$GradientStartR,"EndG"=>$GradientStartG,"EndB"=>$GradientStartB,"Alpha"=>$GradientAlpha); + + $this->drawGradientArea($X1,$Y+$YOffset+$YSpace,$X2,$Y+$YOffset+$YSize-$YSpace,DIRECTION_HORIZONTAL,$GradienColor); + } + elseif ( $GradientMode == GRADIENT_EFFECT_CAN ) + { + $GradienColor1 = array("StartR"=>$GradientEndR,"StartG"=>$GradientEndG,"StartB"=>$GradientEndB,"EndR"=>$GradientStartR,"EndG"=>$GradientStartG,"EndB"=>$GradientStartB,"Alpha"=>$GradientAlpha); + $GradienColor2 = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha); + $YSpan = floor($YSize / 3); + + $this->drawGradientArea($X1,$Y+$YOffset+$YSpace,$X2,$Y+$YOffset+$YSpan-$YSpace,DIRECTION_VERTICAL,$GradienColor1); + $this->drawGradientArea($X1,$Y+$YOffset+$YSpan,$X2,$Y+$YOffset+$YSize-$YSpace,DIRECTION_VERTICAL,$GradienColor2); + } + $this->Shadow = $RestoreShadow; + } + } + + if ( $Draw0Line ) + { + $Line0Color = array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20); + + if ( abs($X1 - $X2) > 3 ) { $Line0Width = 3; } else { $Line0Width = 1; } + if ( $X2 - $X1 < 0 ) { $Line0Width = -$Line0Width; } + + $this->drawFilledRectangle(floor($X1),$Y+$YOffset+$YSpace,floor($X1)+$Line0Width,$Y+$YOffset+$YSize-$YSpace,$Line0Color); + $this->drawLine(floor($X1),$Y+$YOffset+$YSpace,floor($X1),$Y+$YOffset+$YSize-$YSpace,$Line0Color); + } + } + + if ( $DisplayValues && $Serie["Data"][$Key] != VOID ) + { + if ( $DisplayShadow ) { $this->Shadow = TRUE; } + + $Caption = $this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit); + $TxtPos = $this->getTextBox(0,0,$DisplayFont,$DisplaySize,0,$Caption); + $TxtWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"] + $TxtMargin; + + if ( $DisplayPos == LABEL_POS_INSIDE && abs($TxtWidth) < abs($BarWidth) ) + { + $CenterX = ($X2-$X1)/2 + $X1; + $CenterY = (($Y+$YOffset+$YSize-$YSpace)-($Y+$YOffset+$YSpace))/2 + ($Y+$YOffset+$YSpace); + + $this->drawText($CenterX,$CenterY,$Caption,array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontSize"=>$DisplaySize)); + } + else + { + if ( $Serie["Data"][$Key] >= 0 ) { $Align = TEXT_ALIGN_MIDDLELEFT; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_MIDDLERIGHT; $Offset = -$DisplayOffset; } + $this->drawText($X2+$Offset,$Y+$YOffset+$YSize/2,$Caption,array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>$Align,"FontSize"=>$DisplaySize)); + } + + $this->Shadow = $RestoreShadow; + } + } + $Y = $Y + $YStep; + $ID++; + } + } + $CurrentSerie++; + } + } + } + + /* Draw a bar chart */ + function drawStackedBarChart($Format=NULL) + { + $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : FALSE; + $DisplayOrientation = isset($Format["DisplayOrientation"]) ? $Format["DisplayOrientation"] : ORIENTATION_AUTO; + $DisplayRound = isset($Format["DisplayRound"]) ? $Format["DisplayRound"] : 0; + $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; + $DisplayFont = isset($Format["DisplayFont"]) ? $Format["DisplayFont"] : $this->FontName; + $DisplaySize = isset($Format["DisplaySize"]) ? $Format["DisplaySize"] : $this->FontSize; + $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; + $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; + $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; + $Interleave = isset($Format["Interleave"]) ? $Format["Interleave"] : .5; + $Rounded = isset($Format["Rounded"]) ? $Format["Rounded"] : FALSE; + $RoundRadius = isset($Format["RoundRadius"]) ? $Format["RoundRadius"] : 4; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1; + $Gradient = isset($Format["Gradient"]) ? $Format["Gradient"] : FALSE; + $GradientMode = isset($Format["GradientMode"]) ? $Format["GradientMode"] : GRADIENT_SIMPLE; + $GradientAlpha = isset($Format["GradientAlpha"]) ? $Format["GradientAlpha"] : 20; + $GradientStartR = isset($Format["GradientStartR"]) ? $Format["GradientStartR"] : 255; + $GradientStartG = isset($Format["GradientStartG"]) ? $Format["GradientStartG"] : 255; + $GradientStartB = isset($Format["GradientStartB"]) ? $Format["GradientStartB"] : 255; + $GradientEndR = isset($Format["GradientEndR"]) ? $Format["GradientEndR"] : 0; + $GradientEndG = isset($Format["GradientEndG"]) ? $Format["GradientEndG"] : 0; + $GradientEndB = isset($Format["GradientEndB"]) ? $Format["GradientEndB"] : 0; + $InnerSurrounding = isset($Format["InnerSurrounding"]) ? $Format["InnerSurrounding"] : NULL; + $InnerBorderR = isset($Format["InnerBorderR"]) ? $Format["InnerBorderR"] : -1; + $InnerBorderG = isset($Format["InnerBorderG"]) ? $Format["InnerBorderG"] : -1; + $InnerBorderB = isset($Format["InnerBorderB"]) ? $Format["InnerBorderB"] : -1; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; + $FontFactor = isset($Format["FontFactor"]) ? $Format["FontFactor"] : 8; + + $this->LastChartLayout = CHART_LAST_LAYOUT_STACKED; + + $Data = $this->DataSet->getData(); + list($XMargin,$XDivs) = $this->scaleGetXSettings(); + + $RestoreShadow = $this->Shadow; + + $LastX = ""; $LastY = ""; + foreach($Data["Series"] as $SerieName => $Serie) + { + if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) + { + $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; + if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = 255; $DisplayG = 255; $DisplayB = 255; } + if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; } + if ( $InnerSurrounding != NULL ) { $InnerBorderR = $R+$InnerSurrounding; $InnerBorderG = $G+$InnerSurrounding; $InnerBorderB = $B+$InnerSurrounding; } + if ( $InnerBorderR == -1 ) { $InnerColor = NULL; } else { $InnerColor = array("R"=>$InnerBorderR,"G"=>$InnerBorderG,"B"=>$InnerBorderB); } + + $AxisID = $Serie["Axis"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + if (isset($Serie["Description"])) { $SerieDescription = $Serie["Description"]; } else { $SerieDescription = $SerieName; } + + $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"]),TRUE); + $YZero = $this->scaleComputeY(0,array("AxisID"=>$Serie["Axis"])); + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; + + $Color = array("TransCorner"=>TRUE,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB); + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; } + if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; } + + if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } + $X = $this->GraphAreaX1 + $XMargin; + + $XSize = ($XStep / (1+$Interleave) ); + $XOffset = -($XSize/2); + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + foreach($PosArray as $Key => $Height) + { + if ( $Height != VOID && $Serie["Data"][$Key] != 0 ) + { + if ( $Serie["Data"][$Key] > 0 ) { $Pos = "+"; } else { $Pos = "-"; } + + if ( !isset($LastY[$Key] ) ) { $LastY[$Key] = ""; } + if ( !isset($LastY[$Key][$Pos] ) ) { $LastY[$Key][$Pos] = $YZero; } + + $Y1 = $LastY[$Key][$Pos]; + $Y2 = $Y1 - $Height; + + if ( ($Rounded || $BorderR != -1) && ($Pos == "+" && $Y1 != $YZero) ) { $YSpaceUp = 1; } else { $YSpaceUp = 0; } + if ( ($Rounded || $BorderR != -1) && ($Pos == "-" && $Y1 != $YZero) ) { $YSpaceDown = 1; } else { $YSpaceDown = 0; } + + if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X+$XOffset).",".floor($Y1-$YSpaceUp+$YSpaceDown).",".floor($X+$XOffset+$XSize).",".floor($Y2),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + + if ( $Rounded ) + $this->drawRoundedFilledRectangle($X+$XOffset,$Y1-$YSpaceUp+$YSpaceDown,$X+$XOffset+$XSize,$Y2,$RoundRadius,$Color); + else + { + $this->drawFilledRectangle($X+$XOffset,$Y1-$YSpaceUp+$YSpaceDown,$X+$XOffset+$XSize,$Y2,$Color); + + if ( $InnerColor != NULL ) { $RestoreShadow = $this->Shadow; $this->Shadow = FALSE; $this->drawRectangle(min($X+$XOffset+1,$X+$XOffset+$XSize),min($Y1-$YSpaceUp+$YSpaceDown,$Y2)+1,max($X+$XOffset+1,$X+$XOffset+$XSize)-1,max($Y1-$YSpaceUp+$YSpaceDown,$Y2)-1,$InnerColor); $this->Shadow = $RestoreShadow;} + + if ( $Gradient ) + { + $this->Shadow = FALSE; + + if ( $GradientMode == GRADIENT_SIMPLE ) + { + $GradientColor = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha); + $this->drawGradientArea($X+$XOffset,$Y1-1-$YSpaceUp+$YSpaceDown,$X+$XOffset+$XSize,$Y2+1,DIRECTION_VERTICAL,$GradientColor); + } + elseif ( $GradientMode == GRADIENT_EFFECT_CAN ) + { + $GradientColor1 = array("StartR"=>$GradientEndR,"StartG"=>$GradientEndG,"StartB"=>$GradientEndB,"EndR"=>$GradientStartR,"EndG"=>$GradientStartG,"EndB"=>$GradientStartB,"Alpha"=>$GradientAlpha); + $GradientColor2 = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha); + $XSpan = floor($XSize / 3); + + $this->drawGradientArea($X+$XOffset-.5,$Y1-.5-$YSpaceUp+$YSpaceDown,$X+$XOffset+$XSpan,$Y2+.5,DIRECTION_HORIZONTAL,$GradientColor1); + $this->drawGradientArea($X+$XSpan+$XOffset-.5,$Y1-.5-$YSpaceUp+$YSpaceDown,$X+$XOffset+$XSize,$Y2+.5,DIRECTION_HORIZONTAL,$GradientColor2); + } + $this->Shadow = $RestoreShadow; + } + } + + if ( $DisplayValues ) + { + $BarHeight = abs($Y2-$Y1)-2; + $BarWidth = $XSize+($XOffset/2)-$FontFactor; + + $Caption = $this->scaleFormat(round($Serie["Data"][$Key],$DisplayRound),$Mode,$Format,$Unit); + $TxtPos = $this->getTextBox(0,0,$DisplayFont,$DisplaySize,0,$Caption); + $TxtHeight = abs($TxtPos[2]["Y"] - $TxtPos[0]["Y"]); + $TxtWidth = abs($TxtPos[1]["X"] - $TxtPos[0]["X"]); + + $XCenter = ( ($X+$XOffset+$XSize) - ($X+$XOffset) ) / 2 + $X+$XOffset; + $YCenter = ( ($Y2) - ($Y1-$YSpaceUp+$YSpaceDown) ) / 2 + $Y1-$YSpaceUp+$YSpaceDown; + + $Done = FALSE; + if ( $DisplayOrientation == ORIENTATION_HORIZONTAL || $DisplayOrientation == ORIENTATION_AUTO ) + { + if ( $TxtHeight < $BarHeight && $TxtWidth < $BarWidth ) + { + $this->drawText($XCenter,$YCenter,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontSize"=>$DisplaySize,"FontName"=>$DisplayFont)); + $Done = TRUE; + } + } + + if ( $DisplayOrientation == ORIENTATION_VERTICAL || ( $DisplayOrientation == ORIENTATION_AUTO && !$Done) ) + { + if ( $TxtHeight < $BarWidth && $TxtWidth < $BarHeight ) + $this->drawText($XCenter,$YCenter,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Angle"=>90,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontSize"=>$DisplaySize,"FontName"=>$DisplayFont)); + } + } + + $LastY[$Key][$Pos] = $Y2; + } + + $X = $X + $XStep; + } + } + else + { + if ( $YZero < $this->GraphAreaX1+1 ) { $YZero = $this->GraphAreaX1+1; } + if ( $YZero > $this->GraphAreaX2-1 ) { $YZero = $this->GraphAreaX2-1; } + + if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } + $Y = $this->GraphAreaY1 + $XMargin; + + $YSize = $YStep / (1+$Interleave); + $YOffset = -($YSize/2); + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + foreach($PosArray as $Key => $Width) + { + if ( $Width != VOID && $Serie["Data"][$Key] != 0 ) + { + if ( $Serie["Data"][$Key] > 0 ) { $Pos = "+"; } else { $Pos = "-"; } + + if ( !isset($LastX[$Key] ) ) { $LastX[$Key] = ""; } + if ( !isset($LastX[$Key][$Pos] ) ) { $LastX[$Key][$Pos] = $YZero; } + + $X1 = $LastX[$Key][$Pos]; + $X2 = $X1 + $Width; + + if ( ($Rounded || $BorderR != -1) && ($Pos == "+" && $X1 != $YZero) ) { $XSpaceLeft = 2; } else { $XSpaceLeft = 0; } + if ( ($Rounded || $BorderR != -1) && ($Pos == "-" && $X1 != $YZero) ) { $XSpaceRight = 2; } else { $XSpaceRight = 0; } + + if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X1+$XSpaceLeft).",".floor($Y+$YOffset).",".floor($X2-$XSpaceRight).",".floor($Y+$YOffset+$YSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } + + if ( $Rounded ) + $this->drawRoundedFilledRectangle($X1+$XSpaceLeft,$Y+$YOffset,$X2-$XSpaceRight,$Y+$YOffset+$YSize,$RoundRadius,$Color); + else + { + $this->drawFilledRectangle($X1+$XSpaceLeft,$Y+$YOffset,$X2-$XSpaceRight,$Y+$YOffset+$YSize,$Color); + + if ( $InnerColor != NULL ) { $RestoreShadow = $this->Shadow; $this->Shadow = FALSE; $this->drawRectangle(min($X1+$XSpaceLeft,$X2-$XSpaceRight)+1,min($Y+$YOffset,$Y+$YOffset+$YSize)+1,max($X1+$XSpaceLeft,$X2-$XSpaceRight)-1,max($Y+$YOffset,$Y+$YOffset+$YSize)-1,$InnerColor); $this->Shadow = $RestoreShadow;} + + if ( $Gradient ) + { + $this->Shadow = FALSE; + + if ( $GradientMode == GRADIENT_SIMPLE ) + { + $GradientColor = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha); + $this->drawGradientArea($X1+$XSpaceLeft,$Y+$YOffset,$X2-$XSpaceRight,$Y+$YOffset+$YSize,DIRECTION_HORIZONTAL,$GradientColor); + } + elseif ( $GradientMode == GRADIENT_EFFECT_CAN ) + { + $GradientColor1 = array("StartR"=>$GradientEndR,"StartG"=>$GradientEndG,"StartB"=>$GradientEndB,"EndR"=>$GradientStartR,"EndG"=>$GradientStartG,"EndB"=>$GradientStartB,"Alpha"=>$GradientAlpha); + $GradientColor2 = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha); + $YSpan = floor($YSize / 3); + + $this->drawGradientArea($X1+$XSpaceLeft,$Y+$YOffset,$X2-$XSpaceRight,$Y+$YOffset+$YSpan,DIRECTION_VERTICAL,$GradientColor1); + $this->drawGradientArea($X1+$XSpaceLeft,$Y+$YOffset+$YSpan,$X2-$XSpaceRight,$Y+$YOffset+$YSize,DIRECTION_VERTICAL,$GradientColor2); + } + $this->Shadow = $RestoreShadow; + } + } + + if ( $DisplayValues ) + { + $BarWidth = abs($X2-$X1)-$FontFactor; + $BarHeight = $YSize+($YOffset/2)-$FontFactor/2; + $Caption = $this->scaleFormat(round($Serie["Data"][$Key],$DisplayRound),$Mode,$Format,$Unit); + $TxtPos = $this->getTextBox(0,0,$DisplayFont,$DisplaySize,0,$Caption); + $TxtHeight = abs($TxtPos[2]["Y"] - $TxtPos[0]["Y"]); + $TxtWidth = abs($TxtPos[1]["X"] - $TxtPos[0]["X"]); + + $XCenter = ( $X2 - $X1 ) / 2 + $X1; + $YCenter = ( ($Y+$YOffset+$YSize) - ($Y+$YOffset) ) / 2 + $Y+$YOffset; + + $Done = FALSE; + if ( $DisplayOrientation == ORIENTATION_HORIZONTAL || $DisplayOrientation == ORIENTATION_AUTO ) + { + if ( $TxtHeight < $BarHeight && $TxtWidth < $BarWidth ) + { + $this->drawText($XCenter,$YCenter,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontSize"=>$DisplaySize,"FontName"=>$DisplayFont)); + $Done = TRUE; + } + } + + if ( $DisplayOrientation == ORIENTATION_VERTICAL || ( $DisplayOrientation == ORIENTATION_AUTO && !$Done) ) + { + if ( $TxtHeight < $BarWidth && $TxtWidth < $BarHeight ) + $this->drawText($XCenter,$YCenter,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Angle"=>90,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontSize"=>$DisplaySize,"FontName"=>$DisplayFont)); + } + } + + $LastX[$Key][$Pos] = $X2; + } + + $Y = $Y + $YStep; + } + } + } + } + } + + /* Draw a stacked area chart */ + function drawStackedAreaChart($Format=NULL) + { + $DrawLine = isset($Format["DrawLine"]) ? $Format["DrawLine"] : FALSE; + $LineSurrounding = isset($Format["LineSurrounding"]) ? $Format["LineSurrounding"] : NULL; + $LineR = isset($Format["LineR"]) ? $Format["LineR"] : VOID; + $LineG = isset($Format["LineG"]) ? $Format["LineG"] : VOID; + $LineB = isset($Format["LineB"]) ? $Format["LineB"] : VOID; + $LineAlpha = isset($Format["LineAlpha"]) ? $Format["LineAlpha"] : 100; + $DrawPlot = isset($Format["DrawPlot"]) ? $Format["DrawPlot"] : FALSE; + $PlotRadius = isset($Format["PlotRadius"]) ? $Format["PlotRadius"] : 2; + $PlotBorder = isset($Format["PlotBorder"]) ? $Format["PlotBorder"] : 1; + $PlotBorderSurrounding = isset($Format["PlotBorderSurrounding"]) ? $Format["PlotBorderSurrounding"] : NULL; + $PlotBorderR = isset($Format["PlotBorderR"]) ? $Format["PlotBorderR"] : 0; + $PlotBorderG = isset($Format["PlotBorderG"]) ? $Format["PlotBorderG"] : 0; + $PlotBorderB = isset($Format["PlotBorderB"]) ? $Format["PlotBorderB"] : 0; + $PlotBorderAlpha = isset($Format["PlotBorderAlpha"]) ? $Format["PlotBorderAlpha"] : 50; + $ForceTransparency = isset($Format["ForceTransparency"]) ? $Format["ForceTransparency"] : NULL; + + $this->LastChartLayout = CHART_LAST_LAYOUT_STACKED; + + $Data = $this->DataSet->getData(); + list($XMargin,$XDivs) = $this->scaleGetXSettings(); + + $RestoreShadow = $this->Shadow; + $this->Shadow = FALSE; + + /* Build the offset data series */ + $OffsetData = ""; + $OverallOffset = ""; + $SerieOrder = ""; + foreach($Data["Series"] as $SerieName => $Serie) + { + if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) + { + $SerieOrder[] = $SerieName; + + foreach($Serie["Data"] as $Key => $Value) + { + if ( $Value == VOID ) { $Value = 0; } + if ($Value >= 0) { $Sign = "+"; } else { $Sign = "-"; } + if ( !isset($OverallOffset[$Key]) || !isset($OverallOffset[$Key][$Sign]) ) { $OverallOffset[$Key][$Sign] = 0; } + + if ( $Sign == "+" ) + { $Data["Series"][$SerieName]["Data"][$Key] = $Value + $OverallOffset[$Key][$Sign]; } + else + { $Data["Series"][$SerieName]["Data"][$Key] = $Value - $OverallOffset[$Key][$Sign]; } + + $OverallOffset[$Key][$Sign] = $OverallOffset[$Key][$Sign] + abs($Value); + } + } + } + $SerieOrder = array_reverse($SerieOrder); + + $LastX = ""; $LastY = ""; + foreach($SerieOrder as $Key => $SerieName) + { + $Serie = $Data["Series"][$SerieName]; + if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) + { + $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; + if ( $ForceTransparency != NULL ) { $Alpha = $ForceTransparency; } + + $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); + + if ( $LineSurrounding != NULL ) + $LineColor = array("R"=>$R+$LineSurrounding,"G"=>$G+$LineSurrounding,"B"=>$B+$LineSurrounding,"Alpha"=>$Alpha); + elseif ( $LineR != VOID ) + $LineColor = array("R"=>$LineR,"G"=>$LineG,"B"=>$LineB,"Alpha"=>$LineAlpha); + else + $LineColor = $Color; + + if ( $PlotBorderSurrounding != NULL ) + $PlotBorderColor = array("R"=>$R+$PlotBorderSurrounding,"G"=>$G+$PlotBorderSurrounding,"B"=>$B+$PlotBorderSurrounding,"Alpha"=>$PlotBorderAlpha); + else + $PlotBorderColor = array("R"=>$PlotBorderR,"G"=>$PlotBorderG,"B"=>$PlotBorderB,"Alpha"=>$PlotBorderAlpha); + + $AxisID = $Serie["Axis"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"]),TRUE); + $YZero = $this->scaleComputeY(0,array("AxisID"=>$Serie["Axis"])); + + $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + if ( $YZero < $this->GraphAreaY1+1 ) { $YZero = $this->GraphAreaY1+1; } + if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; } + + if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } + $X = $this->GraphAreaX1 + $XMargin; + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + + $Plots = ""; $Plots[] = $X; $Plots[] = $YZero; + foreach($PosArray as $Key => $Height) + { + if ( $Height != VOID ) { $Plots[] = $X; $Plots[] = $YZero-$Height; } + $X = $X + $XStep; + } + $Plots[] = $X-$XStep; $Plots[] = $YZero; + + $this->drawPolygon($Plots,$Color); + + $this->Shadow = $RestoreShadow; + if ( $DrawLine ) { for($i=2; $i<=count($Plots)-6; $i=$i+2) { $this->drawLine($Plots[$i],$Plots[$i+1],$Plots[$i+2],$Plots[$i+3],$LineColor); } } + if ( $DrawPlot ) + { + for($i=2; $i<=count($Plots)-4; $i=$i+2) + { + if ( $PlotBorder != 0 ) + { $this->drawFilledCircle($Plots[$i],$Plots[$i+1],$PlotRadius+$PlotBorder,$PlotBorderColor); } + + $this->drawFilledCircle($Plots[$i],$Plots[$i+1],$PlotRadius,$Color); + } + } + $this->Shadow = FALSE; + } + elseif ( $Data["Orientation"] == SCALE_POS_TOPBOTTOM ) + { + if ( $YZero < $this->GraphAreaX1+1 ) { $YZero = $this->GraphAreaX1+1; } + if ( $YZero > $this->GraphAreaX2-1 ) { $YZero = $this->GraphAreaX2-1; } + + if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } + $Y = $this->GraphAreaY1 + $XMargin; + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + + $Plots = ""; $Plots[] = $YZero; $Plots[] = $Y; + foreach($PosArray as $Key => $Height) + { + if ( $Height != VOID ) { $Plots[] = $YZero+$Height; $Plots[] = $Y; } + $Y = $Y + $YStep; + } + $Plots[] = $YZero; $Plots[] = $Y-$YStep; + + $this->drawPolygon($Plots,$Color); + + $this->Shadow = $RestoreShadow; + if ( $DrawLine ) { for($i=2; $i<=count($Plots)-6; $i=$i+2) { $this->drawLine($Plots[$i],$Plots[$i+1],$Plots[$i+2],$Plots[$i+3],$LineColor); } } + if ( $DrawPlot ) + { + for($i=2; $i<=count($Plots)-4; $i=$i+2) + { + if ( $PlotBorder != 0 ) + { $this->drawFilledCircle($Plots[$i],$Plots[$i+1],$PlotRadius+$PlotBorder,$PlotBorderColor); } + + $this->drawFilledCircle($Plots[$i],$Plots[$i+1],$PlotRadius,$Color); + } + } + $this->Shadow = FALSE; + } + } + } + $this->Shadow = $RestoreShadow; + } + + /* Returns a random color */ + function getRandomColor($Alpha=100) + { return(array("R"=>rand(0,255),"G"=>rand(0,255),"B"=>rand(0,255),"Alpha"=>$Alpha)); } + + /* Validate a palette */ + function validatePalette($Colors,$Surrounding=NULL) + { + $Result = ""; + + if ( !is_array($Colors) ) { return($this->getRandomColor()); } + + foreach($Colors as $Key => $Values) + { + if ( isset($Values["R"]) ) { $Result[$Key]["R"] = $Values["R"]; } else { $Result[$Key]["R"] = rand(0,255); } + if ( isset($Values["G"]) ) { $Result[$Key]["G"] = $Values["G"]; } else { $Result[$Key]["G"] = rand(0,255); } + if ( isset($Values["B"]) ) { $Result[$Key]["B"] = $Values["B"]; } else { $Result[$Key]["B"] = rand(0,255); } + if ( isset($Values["Alpha"]) ) { $Result[$Key]["Alpha"] = $Values["Alpha"]; } else { $Result[$Key]["Alpha"] = 100; } + + if ( $Surrounding != NULL ) + { + $Result[$Key]["BorderR"] = $Result[$Key]["R"] + $Surrounding; + $Result[$Key]["BorderG"] = $Result[$Key]["G"] + $Surrounding; + $Result[$Key]["BorderB"] = $Result[$Key]["B"] + $Surrounding; + } + else + { + if ( isset($Values["BorderR"]) ) { $Result[$Key]["BorderR"] = $Values["BorderR"]; } else { $Result[$Key]["BorderR"] = $Result[$Key]["R"]; } + if ( isset($Values["BorderG"]) ) { $Result[$Key]["BorderG"] = $Values["BorderG"]; } else { $Result[$Key]["BorderG"] = $Result[$Key]["G"]; } + if ( isset($Values["BorderB"]) ) { $Result[$Key]["BorderB"] = $Values["BorderB"]; } else { $Result[$Key]["BorderB"] = $Result[$Key]["B"]; } + if ( isset($Values["BorderAlpha"]) ) { $Result[$Key]["BorderAlpha"] = $Values["BorderAlpha"]; } else { $Result[$Key]["BorderAlpha"] = $Result[$Key]["Alpha"]; } + } + } + + return($Result); + } + + /* Draw the derivative chart associated to the data series */ + function drawDerivative($Format=NULL) + { + $Offset = isset($Format["Offset"]) ? $Format["Offset"] : 10; + $SerieSpacing = isset($Format["SerieSpacing"]) ? $Format["SerieSpacing"] : 3; + $DerivativeHeight = isset($Format["DerivativeHeight"]) ? $Format["DerivativeHeight"] : 4; + $ShadedSlopeBox = isset($Format["ShadedSlopeBox"]) ? $Format["ShadedSlopeBox"] : FALSE; + $DrawBackground = isset($Format["DrawBackground"]) ? $Format["DrawBackground"] : TRUE; + $BackgroundR = isset($Format["BackgroundR"]) ? $Format["BackgroundR"] : 255; + $BackgroundG = isset($Format["BackgroundG"]) ? $Format["BackgroundG"] : 255; + $BackgroundB = isset($Format["BackgroundB"]) ? $Format["BackgroundB"] : 255; + $BackgroundAlpha = isset($Format["BackgroundAlpha"]) ? $Format["BackgroundAlpha"] : 20; + $DrawBorder = isset($Format["DrawBorder"]) ? $Format["DrawBorder"] : TRUE; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 0; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 0; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 0; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 100; + $Caption = isset($Format["Caption"]) ? $Format["Caption"] : TRUE; + $CaptionHeight = isset($Format["CaptionHeight"]) ? $Format["CaptionHeight"] : 10; + $CaptionWidth = isset($Format["CaptionWidth"]) ? $Format["CaptionWidth"] : 20; + $CaptionMargin = isset($Format["CaptionMargin"]) ? $Format["CaptionMargin"] : 4; + $CaptionLine = isset($Format["CaptionLine"]) ? $Format["CaptionLine"] : FALSE; + $CaptionBox = isset($Format["CaptionBox"]) ? $Format["CaptionBox"] : FALSE; + $CaptionBorderR = isset($Format["CaptionBorderR"]) ? $Format["CaptionBorderR"] : 0; + $CaptionBorderG = isset($Format["CaptionBorderG"]) ? $Format["CaptionBorderG"] : 0; + $CaptionBorderB = isset($Format["CaptionBorderB"]) ? $Format["CaptionBorderB"] : 0; + $CaptionFillR = isset($Format["CaptionFillR"]) ? $Format["CaptionFillR"] : 255; + $CaptionFillG = isset($Format["CaptionFillG"]) ? $Format["CaptionFillG"] : 255; + $CaptionFillB = isset($Format["CaptionFillB"]) ? $Format["CaptionFillB"] : 255; + $CaptionFillAlpha = isset($Format["CaptionFillAlpha"]) ? $Format["CaptionFillAlpha"] : 80; + $PositiveSlopeStartR = isset($Format["PositiveSlopeStartR"]) ? $Format["PositiveSlopeStartR"] : 184; + $PositiveSlopeStartG = isset($Format["PositiveSlopeStartG"]) ? $Format["PositiveSlopeStartG"] : 234; + $PositiveSlopeStartB = isset($Format["PositiveSlopeStartB"]) ? $Format["PositiveSlopeStartB"] : 88; + $PositiveSlopeEndR = isset($Format["PositiveSlopeStartR"]) ? $Format["PositiveSlopeStartR"] : 239; + $PositiveSlopeEndG = isset($Format["PositiveSlopeStartG"]) ? $Format["PositiveSlopeStartG"] : 31; + $PositiveSlopeEndB = isset($Format["PositiveSlopeStartB"]) ? $Format["PositiveSlopeStartB"] : 36; + $NegativeSlopeStartR = isset($Format["NegativeSlopeStartR"]) ? $Format["NegativeSlopeStartR"] : 184; + $NegativeSlopeStartG = isset($Format["NegativeSlopeStartG"]) ? $Format["NegativeSlopeStartG"] : 234; + $NegativeSlopeStartB = isset($Format["NegativeSlopeStartB"]) ? $Format["NegativeSlopeStartB"] : 88; + $NegativeSlopeEndR = isset($Format["NegativeSlopeStartR"]) ? $Format["NegativeSlopeStartR"] : 67; + $NegativeSlopeEndG = isset($Format["NegativeSlopeStartG"]) ? $Format["NegativeSlopeStartG"] : 124; + $NegativeSlopeEndB = isset($Format["NegativeSlopeStartB"]) ? $Format["NegativeSlopeStartB"] : 227; + + $Data = $this->DataSet->getData(); + + list($XMargin,$XDivs) = $this->scaleGetXSettings(); + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + $YPos = $this->DataSet->Data["GraphArea"]["Y2"] + $Offset; + else + $XPos = $this->DataSet->Data["GraphArea"]["X2"] + $Offset; + + foreach($Data["Series"] as $SerieName => $Serie) + { + if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) + { + $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; $Weight = $Serie["Weight"]; + + $AxisID = $Serie["Axis"]; + $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + if ( $Caption ) + { + if ( $CaptionLine ) + { + $StartX = floor($this->GraphAreaX1-$CaptionWidth+$XMargin-$CaptionMargin); + $EndX = floor($this->GraphAreaX1-$CaptionMargin+$XMargin); + + $CaptionSettings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight); + if ( $CaptionBox ) { $this->drawFilledRectangle($StartX,$YPos,$EndX,$YPos+$CaptionHeight,array("R"=>$CaptionFillR,"G"=>$CaptionFillG,"B"=>$CaptionFillB,"BorderR"=>$CaptionBorderR,"BorderG"=>$CaptionBorderG,"BorderB"=>$CaptionBorderB,"Alpha"=>$CaptionFillAlpha)); } + $this->drawLine($StartX+2,$YPos+($CaptionHeight/2),$EndX-2,$YPos+($CaptionHeight/2),$CaptionSettings); + } + else + $this->drawFilledRectangle($this->GraphAreaX1-$CaptionWidth+$XMargin-$CaptionMargin,$YPos,$this->GraphAreaX1-$CaptionMargin+$XMargin,$YPos+$CaptionHeight,array("R"=>$R,"G"=>$G,"B"=>$B,"BorderR"=>$CaptionBorderR,"BorderG"=>$CaptionBorderG,"BorderB"=>$CaptionBorderB)); + } + + if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } + $X = $this->GraphAreaX1 + $XMargin; + + $TopY = $YPos + ($CaptionHeight/2) - ($DerivativeHeight/2); + $BottomY = $YPos + ($CaptionHeight/2) + ($DerivativeHeight/2); + + $StartX = floor($this->GraphAreaX1+$XMargin); + $EndX = floor($this->GraphAreaX2-$XMargin); + + if ( $DrawBackground ) { $this->drawFilledRectangle($StartX-1,$TopY-1,$EndX+1,$BottomY+1,array("R"=>$BackgroundR,"G"=>$BackgroundG,"B"=>$BackgroundB,"Alpha"=>$BackgroundAlpha)); } + if ( $DrawBorder ) { $this->drawRectangle($StartX-1,$TopY-1,$EndX+1,$BottomY+1,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha)); } + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + + $RestoreShadow = $this->Shadow; + $this->Shadow = FALSE; + + /* Determine the Max slope index */ + $LastX = NULL; $LastY = NULL; $MinSlope = 0; $MaxSlope = 1; + foreach($PosArray as $Key => $Y) + { + if ( $Y != VOID && $LastX != NULL ) + { $Slope = ($LastY - $Y); if ( $Slope > $MaxSlope ) { $MaxSlope = $Slope; } if ( $Slope < $MinSlope ) { $MinSlope = $Slope; } } + + if ( $Y == VOID ) + { $LastX = NULL; $LastY = NULL; } + else + { $LastX = $X; $LastY = $Y; } + } + + $LastX = NULL; $LastY = NULL; $LastColor = NULL; + foreach($PosArray as $Key => $Y) + { + if ( $Y != VOID && $LastY != NULL ) + { + $Slope = ($LastY - $Y); + + if ( $Slope >= 0 ) + { + $SlopeIndex = (100 / $MaxSlope) * $Slope; + $R = (($PositiveSlopeEndR - $PositiveSlopeStartR)/100)*$SlopeIndex+$PositiveSlopeStartR; + $G = (($PositiveSlopeEndG - $PositiveSlopeStartG)/100)*$SlopeIndex+$PositiveSlopeStartG; + $B = (($PositiveSlopeEndB - $PositiveSlopeStartB)/100)*$SlopeIndex+$PositiveSlopeStartB; + } + elseif ( $Slope < 0 ) + { + $SlopeIndex = (100 / abs($MinSlope)) * abs($Slope); + $R = (($NegativeSlopeEndR - $NegativeSlopeStartR)/100)*$SlopeIndex+$NegativeSlopeStartR; + $G = (($NegativeSlopeEndG - $NegativeSlopeStartG)/100)*$SlopeIndex+$NegativeSlopeStartG; + $B = (($NegativeSlopeEndB - $NegativeSlopeStartB)/100)*$SlopeIndex+$NegativeSlopeStartB; + } + + $Color = array("R"=>$R,"G"=>$G,"B"=>$B); + + if ( $ShadedSlopeBox && $LastColor != NULL ) // && $Slope != 0 + { + $GradientSettings = array("StartR"=>$LastColor["R"],"StartG"=>$LastColor["G"],"StartB"=>$LastColor["B"],"EndR"=>$R,"EndG"=>$G,"EndB"=>$B); + $this->drawGradientArea($LastX,$TopY,$X,$BottomY,DIRECTION_HORIZONTAL,$GradientSettings); + } + elseif ( !$ShadedSlopeBox || $LastColor == NULL ) // || $Slope == 0 + $this->drawFilledRectangle(floor($LastX),$TopY,floor($X),$BottomY,$Color); + + $LastColor = $Color; + } + + if ( $Y == VOID ) + { $LastY = NULL; } + else + { $LastX = $X; $LastY = $Y; } + + $X = $X + $XStep; + } + + $YPos = $YPos + $CaptionHeight + $SerieSpacing; + } + else + { + if ( $Caption ) + { + $StartY = floor($this->GraphAreaY1-$CaptionWidth+$XMargin-$CaptionMargin); + $EndY = floor($this->GraphAreaY1-$CaptionMargin+$XMargin); + if ( $CaptionLine ) + { + $CaptionSettings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight); + if ( $CaptionBox ) { $this->drawFilledRectangle($XPos,$StartY,$XPos+$CaptionHeight,$EndY,array("R"=>$CaptionFillR,"G"=>$CaptionFillG,"B"=>$CaptionFillB,"BorderR"=>$CaptionBorderR,"BorderG"=>$CaptionBorderG,"BorderB"=>$CaptionBorderB,"Alpha"=>$CaptionFillAlpha)); } + $this->drawLine($XPos+($CaptionHeight/2),$StartY+2,$XPos+($CaptionHeight/2),$EndY-2,$CaptionSettings); + } + else + $this->drawFilledRectangle($XPos,$StartY,$XPos+$CaptionHeight,$EndY,array("R"=>$R,"G"=>$G,"B"=>$B,"BorderR"=>$CaptionBorderR,"BorderG"=>$CaptionBorderG,"BorderB"=>$CaptionBorderB)); + } + + + if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $XStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } + $Y = $this->GraphAreaY1 + $XMargin; + + $TopX = $XPos + ($CaptionHeight/2) - ($DerivativeHeight/2); + $BottomX = $XPos + ($CaptionHeight/2) + ($DerivativeHeight/2); + + $StartY = floor($this->GraphAreaY1+$XMargin); + $EndY = floor($this->GraphAreaY2-$XMargin); + + if ( $DrawBackground ) { $this->drawFilledRectangle($TopX-1,$StartY-1,$BottomX+1,$EndY+1,array("R"=>$BackgroundR,"G"=>$BackgroundG,"B"=>$BackgroundB,"Alpha"=>$BackgroundAlpha)); } + if ( $DrawBorder ) { $this->drawRectangle($TopX-1,$StartY-1,$BottomX+1,$EndY+1,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha)); } + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + + $RestoreShadow = $this->Shadow; + $this->Shadow = FALSE; + + /* Determine the Max slope index */ + $LastX = NULL; $LastY = NULL; $MinSlope = 0; $MaxSlope = 1; + foreach($PosArray as $Key => $X) + { + if ( $X != VOID && $LastX != NULL ) + { $Slope = ($X - $LastX); if ( $Slope > $MaxSlope ) { $MaxSlope = $Slope; } if ( $Slope < $MinSlope ) { $MinSlope = $Slope; } } + + if ( $X == VOID ) + { $LastX = NULL; } + else + { $LastX = $X; } + } + + $LastX = NULL; $LastY = NULL; $LastColor = NULL; + foreach($PosArray as $Key => $X) + { + if ( $X != VOID && $LastX != NULL ) + { + $Slope = ($X - $LastX); + + if ( $Slope >= 0 ) + { + $SlopeIndex = (100 / $MaxSlope) * $Slope; + $R = (($PositiveSlopeEndR - $PositiveSlopeStartR)/100)*$SlopeIndex+$PositiveSlopeStartR; + $G = (($PositiveSlopeEndG - $PositiveSlopeStartG)/100)*$SlopeIndex+$PositiveSlopeStartG; + $B = (($PositiveSlopeEndB - $PositiveSlopeStartB)/100)*$SlopeIndex+$PositiveSlopeStartB; + } + elseif ( $Slope < 0 ) + { + $SlopeIndex = (100 / abs($MinSlope)) * abs($Slope); + $R = (($NegativeSlopeEndR - $NegativeSlopeStartR)/100)*$SlopeIndex+$NegativeSlopeStartR; + $G = (($NegativeSlopeEndG - $NegativeSlopeStartG)/100)*$SlopeIndex+$NegativeSlopeStartG; + $B = (($NegativeSlopeEndB - $NegativeSlopeStartB)/100)*$SlopeIndex+$NegativeSlopeStartB; + } + + $Color = array("R"=>$R,"G"=>$G,"B"=>$B); + + if ( $ShadedSlopeBox && $LastColor != NULL ) + { + $GradientSettings = array("StartR"=>$LastColor["R"],"StartG"=>$LastColor["G"],"StartB"=>$LastColor["B"],"EndR"=>$R,"EndG"=>$G,"EndB"=>$B); + + $this->drawGradientArea($TopX,$LastY,$BottomX,$Y,DIRECTION_VERTICAL,$GradientSettings); + } + elseif ( !$ShadedSlopeBox || $LastColor == NULL ) + $this->drawFilledRectangle($TopX,floor($LastY),$BottomX,floor($Y),$Color); + + $LastColor = $Color; + } + + if ( $X == VOID ) + { $LastX = NULL; } + else + { $LastX = $X; $LastY = $Y; } + + $Y = $Y + $XStep; + } + + $XPos = $XPos + $CaptionHeight + $SerieSpacing; + } + + $this->Shadow = $RestoreShadow; + } + } + } + + /* Draw the line of best fit */ + function drawBestFit($Format="") + { + $OverrideTicks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL; + $OverrideR = isset($Format["R"]) ? $Format["R"] : VOID; + $OverrideG = isset($Format["G"]) ? $Format["G"] : VOID; + $OverrideB = isset($Format["B"]) ? $Format["B"] : VOID; + $OverrideAlpha = isset($Format["Alpha"]) ? $Format["Alpha"] : VOID; + + $Data = $this->DataSet->getData(); + list($XMargin,$XDivs) = $this->scaleGetXSettings(); + + foreach($Data["Series"] as $SerieName => $Serie) + { + if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) + { + if ( $OverrideR != VOID && $OverrideG != VOID && $OverrideB != VOID ) { $R = $OverrideR; $G = $OverrideG; $B = $OverrideB; } else { $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; } + if ( $OverrideTicks == NULL ) { $Ticks = $Serie["Ticks"]; } else { $Ticks = $OverrideTicks; } + if ( $OverrideAlpha == VOID ) { $Alpha = $Serie["Color"]["Alpha"]; } else { $Alpha = $OverrideAlpha; } + + $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks); + + $AxisID = $Serie["Axis"]; + $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } + $X = $this->GraphAreaX1 + $XMargin; + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + $Sxy = 0; $Sx = 0; $Sy = 0; $Sxx = 0; + foreach($PosArray as $Key => $Y) + { + if ( $Y != VOID ) + { + $Sxy = $Sxy + $X*$Y; + $Sx = $Sx + $X; + $Sy = $Sy + $Y; + $Sxx = $Sxx + $X*$X; + } + + $X = $X + $XStep; + } + $n = count($this->DataSet->stripVOID($PosArray)); //$n = count($PosArray); + $M = (($n*$Sxy)-($Sx*$Sy)) / (($n*$Sxx)-($Sx*$Sx)); + $B = (($Sy)-($M*$Sx))/($n); + + $X1 = $this->GraphAreaX1 + $XMargin; + $Y1 = $M * $X1 + $B; + $X2 = $this->GraphAreaX2 - $XMargin; + $Y2 = $M * $X2 + $B; + + if ( $Y1 < $this->GraphAreaY1 ) { $X1 = $X1 + ($this->GraphAreaY1-$Y1); $Y1 = $this->GraphAreaY1; } + if ( $Y1 > $this->GraphAreaY2 ) { $X1 = $X1 + ($Y1-$this->GraphAreaY2); $Y1 = $this->GraphAreaY2; } + if ( $Y2 < $this->GraphAreaY1 ) { $X2 = $X2 - ($this->GraphAreaY1-$Y2); $Y2 = $this->GraphAreaY1; } + if ( $Y2 > $this->GraphAreaY2 ) { $X2 = $X2 - ($Y2-$this->GraphAreaY2); $Y2 = $this->GraphAreaY2; } + + $this->drawLine($X1,$Y1,$X2,$Y2,$Color); + } + else + { + if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } + $Y = $this->GraphAreaY1 + $XMargin; + + if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; } + $Sxy = 0; $Sx = 0; $Sy = 0; $Sxx = 0; + foreach($PosArray as $Key => $X) + { + if ( $X != VOID ) + { + $Sxy = $Sxy + $X*$Y; + $Sx = $Sx + $Y; + $Sy = $Sy + $X; + $Sxx = $Sxx + $Y*$Y; + } + + $Y = $Y + $YStep; + } + $n = count($this->DataSet->stripVOID($PosArray)); //$n = count($PosArray); + $M = (($n*$Sxy)-($Sx*$Sy)) / (($n*$Sxx)-($Sx*$Sx)); + $B = (($Sy)-($M*$Sx))/($n); + + $Y1 = $this->GraphAreaY1 + $XMargin; + $X1 = $M * $Y1 + $B; + $Y2 = $this->GraphAreaY2 - $XMargin; + $X2 = $M * $Y2 + $B; + + if ( $X1 < $this->GraphAreaX1 ) { $Y1 = $Y1 + ($this->GraphAreaX1-$X1); $X1 = $this->GraphAreaX1; } + if ( $X1 > $this->GraphAreaX2 ) { $Y1 = $Y1 + ($X1-$this->GraphAreaX2); $X1 = $this->GraphAreaX2; } + if ( $X2 < $this->GraphAreaX1 ) { $Y2 = $Y2 - ($this->GraphAreaY1-$X2); $X2 = $this->GraphAreaX1; } + if ( $X2 > $this->GraphAreaX2 ) { $Y2 = $Y2 - ($X2-$this->GraphAreaX2); $X2 = $this->GraphAreaX2; } + + $this->drawLine($X1,$Y1,$X2,$Y2,$Color); + } + } + } + } + + /* Write labels */ + function writeLabel($SeriesName,$Indexes,$Format="") + { + $OverrideTitle = isset($Format["OverrideTitle"]) ? $Format["OverrideTitle"] : NULL; + $ForceLabels = isset($Format["ForceLabels"]) ? $Format["ForceLabels"] : NULL; + $DrawPoint = isset($Format["DrawPoint"]) ? $Format["DrawPoint"] : LABEL_POINT_BOX; + $DrawVerticalLine = isset($Format["DrawVerticalLine"]) ? $Format["DrawVerticalLine"] : FALSE; + $VerticalLineR = isset($Format["VerticalLineR"]) ? $Format["VerticalLineR"] : 0; + $VerticalLineG = isset($Format["VerticalLineG"]) ? $Format["VerticalLineG"] : 0; + $VerticalLineB = isset($Format["VerticalLineB"]) ? $Format["VerticalLineB"] : 0; + $VerticalLineAlpha = isset($Format["VerticalLineAlpha"]) ? $Format["VerticalLineAlpha"] : 40; + $VerticalLineTicks = isset($Format["VerticalLineTicks"]) ? $Format["VerticalLineTicks"] : 2; + + $Data = $this->DataSet->getData(); + list($XMargin,$XDivs) = $this->scaleGetXSettings(); + + if ( !is_array($Indexes) ) { $Index = $Indexes; $Indexes = ""; $Indexes[] = $Index; } + if ( !is_array($SeriesName) ) { $SerieName = $SeriesName; $SeriesName = ""; $SeriesName[] = $SerieName; } + if ( $ForceLabels != NULL && !is_array($ForceLabels) ) { $ForceLabel = $ForceLabels; $ForceLabels = ""; $ForceLabels[] = $ForceLabel; } + + foreach ($Indexes as $Key => $Index) + { + $Series = ""; + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } + $X = $this->GraphAreaX1 + $XMargin + $Index * $XStep; + + if ( $DrawVerticalLine ) { $this->drawLine($X,$this->GraphAreaY1+$Data["YMargin"],$X,$this->GraphAreaY2-$Data["YMargin"],array("R"=>$VerticalLineR,"G"=>$VerticalLineG,"B"=>$VerticalLineB,"Alpha"=>$VerticalLineAlpha,"Ticks"=>$VerticalLineTicks)); } + + $MinY = $this->GraphAreaY2; + foreach ($SeriesName as $iKey => $SerieName) + { + if ( isset($Data["Series"][$SerieName]["Data"][$Index]) ) + { + $AxisID = $Data["Series"][$SerieName]["Axis"]; + $XAxisMode = $Data["XAxisDisplay"]; + $XAxisFormat = $Data["XAxisFormat"]; + $XAxisUnit = $Data["XAxisUnit"]; + $AxisMode = $Data["Axis"][$AxisID]["Display"]; + $AxisFormat = $Data["Axis"][$AxisID]["Format"]; + $AxisUnit = $Data["Axis"][$AxisID]["Unit"]; + + if ( isset($Data["Abscissa"]) && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) ) + $XLabel = $this->scaleFormat($Data["Series"][$Data["Abscissa"]]["Data"][$Index],$XAxisMode,$XAxisFormat,$XAxisUnit); + else + $XLabel = ""; + + if ( $OverrideTitle != NULL) + $Description = $OverrideTitle; + elseif ( count($SeriesName) == 1 ) + { + $Description = $Data["Series"][$SerieName]["Description"]." - ".$XLabel; + } + elseif ( isset($Data["Abscissa"]) && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) ) + $Description = $XLabel; + + $Serie = ""; + $Serie["R"] = $Data["Series"][$SerieName]["Color"]["R"]; + $Serie["G"] = $Data["Series"][$SerieName]["Color"]["G"]; + $Serie["B"] = $Data["Series"][$SerieName]["Color"]["B"]; + $Serie["Alpha"] = $Data["Series"][$SerieName]["Color"]["Alpha"]; + + if ( count($SeriesName) == 1 && isset($Data["Series"][$SerieName]["XOffset"]) ) + $SerieOffset = $Data["Series"][$SerieName]["XOffset"]; + else + $SerieOffset = 0; + + $Value = $Data["Series"][$SerieName]["Data"][$Index]; + if ( $Value == VOID ) { $Value = "NaN"; } + + if ( $ForceLabels != NULL ) + $Caption = isset($ForceLabels[$Key]) ? $ForceLabels[$Key] : "Not set"; + else + $Caption = $this->scaleFormat($Value,$AxisMode,$AxisFormat,$AxisUnit); + + if ( $this->LastChartLayout == CHART_LAST_LAYOUT_STACKED ) + { + if ( $Value >=0 ) { $LookFor = "+"; } else { $LookFor = "-"; } + + $Value = 0; $Done = FALSE; + foreach($Data["Series"] as $Name => $SerieLookup) + { + if ( $SerieLookup["isDrawable"] == TRUE && $Name != $Data["Abscissa"] && !$Done ) + { + if ( isset($Data["Series"][$Name]["Data"][$Index]) && $Data["Series"][$Name]["Data"][$Index] != VOID ) + { + if ($Data["Series"][$Name]["Data"][$Index] >= 0 && $LookFor == "+" ) { $Value = $Value + $Data["Series"][$Name]["Data"][$Index]; } + if ($Data["Series"][$Name]["Data"][$Index] < 0 && $LookFor == "-" ) { $Value = $Value - $Data["Series"][$Name]["Data"][$Index]; } + if ($Name == $SerieName ) { $Done = TRUE; } + } + } + } + } + + $X = floor($this->GraphAreaX1 + $XMargin + $Index * $XStep + $SerieOffset); + $Y = floor($this->scaleComputeY($Value,array("AxisID"=>$AxisID))); + + if ($Y < $MinY) { $MinY = $Y; } + + if ( $DrawPoint == LABEL_POINT_CIRCLE ) + $this->drawFilledCircle($X,$Y,3,array("R"=>255,"G"=>255,"B"=>255,"BorderR"=>0,"BorderG"=>0,"BorderB"=>0)); + elseif ( $DrawPoint == LABEL_POINT_BOX ) + $this->drawFilledRectangle($X-2,$Y-2,$X+2,$Y+2,array("R"=>255,"G"=>255,"B"=>255,"BorderR"=>0,"BorderG"=>0,"BorderB"=>0)); + + $Series[] = array("Format"=>$Serie,"Caption"=>$Caption); + } + } + $this->drawLabelBox($X,$MinY-3,$Description,$Series,$Format); + + } + else + { + if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $XStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; } + $Y = $this->GraphAreaY1 + $XMargin + $Index * $XStep; + + if ( $DrawVerticalLine ) { $this->drawLine($this->GraphAreaX1+$Data["YMargin"],$Y,$this->GraphAreaX2-$Data["YMargin"],$Y,array("R"=>$VerticalLineR,"G"=>$VerticalLineG,"B"=>$VerticalLineB,"Alpha"=>$VerticalLineAlpha,"Ticks"=>$VerticalLineTicks)); } + + $MinX = $this->GraphAreaX2; + foreach ($SeriesName as $Key => $SerieName) + { + if ( isset($Data["Series"][$SerieName]["Data"][$Index]) ) + { + $AxisID = $Data["Series"][$SerieName]["Axis"]; + $XAxisMode = $Data["XAxisDisplay"]; + $XAxisFormat = $Data["XAxisFormat"]; + $XAxisUnit = $Data["XAxisUnit"]; + $AxisMode = $Data["Axis"][$AxisID]["Display"]; + $AxisFormat = $Data["Axis"][$AxisID]["Format"]; + $AxisUnit = $Data["Axis"][$AxisID]["Unit"]; + + if ( isset($Data["Abscissa"]) && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) ) + $XLabel = $this->scaleFormat($Data["Series"][$Data["Abscissa"]]["Data"][$Index],$XAxisMode,$XAxisFormat,$XAxisUnit); + else + $XLabel = ""; + + if ( $OverrideTitle != NULL) + $Description = $OverrideTitle; + elseif ( count($SeriesName) == 1 ) + { + if ( isset($Data["Abscissa"]) && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) ) + $Description = $Data["Series"][$SerieName]["Description"]." - ".$XLabel; + } + elseif ( isset($Data["Abscissa"]) && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) ) + $Description = $XLabel; + + $Serie = ""; + if ( isset($Data["Extended"]["Palette"][$Index] ) ) + { + $Serie["R"] = $Data["Extended"]["Palette"][$Index]["R"]; + $Serie["G"] = $Data["Extended"]["Palette"][$Index]["G"]; + $Serie["B"] = $Data["Extended"]["Palette"][$Index]["B"]; + $Serie["Alpha"] = $Data["Extended"]["Palette"][$Index]["Alpha"]; + } + else + { + $Serie["R"] = $Data["Series"][$SerieName]["Color"]["R"]; + $Serie["G"] = $Data["Series"][$SerieName]["Color"]["G"]; + $Serie["B"] = $Data["Series"][$SerieName]["Color"]["B"]; + $Serie["Alpha"] = $Data["Series"][$SerieName]["Color"]["Alpha"]; + } + + if ( count($SeriesName) == 1 && isset($Data["Series"][$SerieName]["XOffset"]) ) + $SerieOffset = $Data["Series"][$SerieName]["XOffset"]; + else + $SerieOffset = 0; + + $Value = $Data["Series"][$SerieName]["Data"][$Index]; + if ( $ForceLabels != NULL ) + $Caption = isset($ForceLabels[$Key]) ? $ForceLabels[$Key] : "Not set"; + else + $Caption = $this->scaleFormat($Value,$AxisMode,$AxisFormat,$AxisUnit); + if ( $Value == VOID ) { $Value = "NaN"; } + + if ( $this->LastChartLayout == CHART_LAST_LAYOUT_STACKED ) + { + if ( $Value >=0 ) { $LookFor = "+"; } else { $LookFor = "-"; } + + $Value = 0; $Done = FALSE; + foreach($Data["Series"] as $Name => $SerieLookup) + { + if ( $SerieLookup["isDrawable"] == TRUE && $Name != $Data["Abscissa"] && !$Done ) + { + if ( isset($Data["Series"][$Name]["Data"][$Index]) && $Data["Series"][$Name]["Data"][$Index] != VOID ) + { + if ($Data["Series"][$Name]["Data"][$Index] >= 0 && $LookFor == "+" ) { $Value = $Value + $Data["Series"][$Name]["Data"][$Index]; } + if ($Data["Series"][$Name]["Data"][$Index] < 0 && $LookFor == "-" ) { $Value = $Value - $Data["Series"][$Name]["Data"][$Index]; } + if ($Name == $SerieName ) { $Done = TRUE; } + } + } + } + } + + $X = floor($this->scaleComputeY($Value,array("AxisID"=>$AxisID))); + $Y = floor($this->GraphAreaY1 + $XMargin + $Index * $XStep + $SerieOffset); + + if ($X < $MinX) { $MinX = $X; } + + if ( $DrawPoint == LABEL_POINT_CIRCLE ) + $this->drawFilledCircle($X,$Y,3,array("R"=>255,"G"=>255,"B"=>255,"BorderR"=>0,"BorderG"=>0,"BorderB"=>0)); + elseif ( $DrawPoint == LABEL_POINT_BOX ) + $this->drawFilledRectangle($X-2,$Y-2,$X+2,$Y+2,array("R"=>255,"G"=>255,"B"=>255,"BorderR"=>0,"BorderG"=>0,"BorderB"=>0)); + + $Series[] = array("Format"=>$Serie,"Caption"=>$Caption); + } + } + $this->drawLabelBox($MinX,$Y-3,$Description,$Series,$Format); + + } + } + } + + /* Draw a label box */ + function drawLabelBox($X,$Y,$Title,$Captions,$Format="") + { + $NoTitle = isset($Format["NoTitle"]) ? $Format["NoTitle"] : NULL; + $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 50; + $DrawSerieColor = isset($Format["DrawSerieColor"]) ? $Format["DrawSerieColor"] : TRUE; + $SerieR = isset($Format["SerieR"]) ? $Format["SerieR"] : NULL; + $SerieG = isset($Format["SerieG"]) ? $Format["SerieG"] : NULL; + $SerieB = isset($Format["SerieB"]) ? $Format["SerieB"] : NULL; + $SerieAlpha = isset($Format["SerieAlpha"]) ? $Format["SerieAlpha"] : NULL; + $SerieBoxSize = isset($Format["SerieBoxSize"]) ? $Format["SerieBoxSize"] : 6; + $SerieBoxSpacing = isset($Format["SerieBoxSpacing"]) ? $Format["SerieBoxSpacing"] : 4; + $VerticalMargin = isset($Format["VerticalMargin"]) ? $Format["VerticalMargin"] : 10; + $HorizontalMargin = isset($Format["HorizontalMargin"]) ? $Format["HorizontalMargin"] : 8; + $R = isset($Format["R"]) ? $Format["R"] : $this->FontColorR; + $G = isset($Format["G"]) ? $Format["G"] : $this->FontColorG; + $B = isset($Format["B"]) ? $Format["B"] : $this->FontColorB; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : $this->FontColorA; + $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->FontName; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize; + $TitleMode = isset($Format["TitleMode"]) ? $Format["TitleMode"] : LABEL_TITLE_NOBACKGROUND; + $TitleR = isset($Format["TitleR"]) ? $Format["TitleR"] : $R; + $TitleG = isset($Format["TitleG"]) ? $Format["TitleG"] : $G; + $TitleB = isset($Format["TitleB"]) ? $Format["TitleB"] : $B; + $TitleAlpha = isset($Format["TitleAlpha"]) ? $Format["TitleAlpha"] : 100; + $TitleBackgroundR = isset($Format["TitleBackgroundR"]) ? $Format["TitleBackgroundR"] : 0; + $TitleBackgroundG = isset($Format["TitleBackgroundG"]) ? $Format["TitleBackgroundG"] : 0; + $TitleBackgroundB = isset($Format["TitleBackgroundB"]) ? $Format["TitleBackgroundB"] : 0; + $TitleBackgroundAlpha = isset($Format["TitleBackgroundAlpha"]) ? $Format["TitleBackgroundAlpha"] : 100; + $GradientStartR = isset($Format["GradientStartR"]) ? $Format["GradientStartR"] : 255; + $GradientStartG = isset($Format["GradientStartG"]) ? $Format["GradientStartG"] : 255; + $GradientStartB = isset($Format["GradientStartB"]) ? $Format["GradientStartB"] : 255; + $GradientEndR = isset($Format["GradientEndR"]) ? $Format["GradientEndR"] : 220; + $GradientEndG = isset($Format["GradientEndG"]) ? $Format["GradientEndG"] : 220; + $GradientEndB = isset($Format["GradientEndB"]) ? $Format["GradientEndB"] : 220; + $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 100; + + if ( !$DrawSerieColor ) { $SerieBoxSize = 0; $SerieBoxSpacing = 0; } + + $TxtPos = $this->getTextBox($X,$Y,$FontName,$FontSize,0,$Title); + $TitleWidth = ($TxtPos[1]["X"] - $TxtPos[0]["X"])+$VerticalMargin*2; + $TitleHeight = ($TxtPos[0]["Y"] - $TxtPos[2]["Y"]); + + if ( $NoTitle ) { $TitleWidth = 0; $TitleHeight = 0; } + + $CaptionWidth = 0; $CaptionHeight = -$HorizontalMargin; + foreach($Captions as $Key =>$Caption) + { + $TxtPos = $this->getTextBox($X,$Y,$FontName,$FontSize,0,$Caption["Caption"]); + $CaptionWidth = max($CaptionWidth,($TxtPos[1]["X"] - $TxtPos[0]["X"])+$VerticalMargin*2); + $CaptionHeight = $CaptionHeight + max(($TxtPos[0]["Y"] - $TxtPos[2]["Y"]),($SerieBoxSize+2)) + $HorizontalMargin; + } + + if ( $CaptionHeight <= 5 ) { $CaptionHeight = $CaptionHeight + $HorizontalMargin/2; } + + if ( $DrawSerieColor ) { $CaptionWidth = $CaptionWidth + $SerieBoxSize + $SerieBoxSpacing; } + + $BoxWidth = max($BoxWidth,$TitleWidth,$CaptionWidth); + + $XMin = $X - 5 - floor(($BoxWidth-10) / 2); + $XMax = $X + 5 + floor(($BoxWidth-10) / 2); + + $RestoreShadow = $this->Shadow; + if ( $this->Shadow == TRUE ) + { + $this->Shadow = FALSE; + + $Poly = ""; + $Poly[] = $X+$this->ShadowX; $Poly[] = $Y+$this->ShadowX; + $Poly[] = $X+5+$this->ShadowX; $Poly[] = $Y-5+$this->ShadowX; + $Poly[] = $XMax+$this->ShadowX; $Poly[] = $Y-5+$this->ShadowX; + + if ( $NoTitle ) + { + $Poly[] = $XMax+$this->ShadowX; $Poly[] = $Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*2+$this->ShadowX; + $Poly[] = $XMin+$this->ShadowX; $Poly[] = $Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*2+$this->ShadowX; + } + else + { + $Poly[] = $XMax+$this->ShadowX; $Poly[] = $Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3+$this->ShadowX; + $Poly[] = $XMin+$this->ShadowX; $Poly[] = $Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3+$this->ShadowX; + } + + $Poly[] = $XMin+$this->ShadowX; $Poly[] = $Y-5+$this->ShadowX; + $Poly[] = $X-5+$this->ShadowX; $Poly[] = $Y-5+$this->ShadowX; + $this->drawPolygon($Poly,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa)); + } + + /* Draw the background */ + $GradientSettings = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$BoxAlpha); + if ( $NoTitle ) + $this->drawGradientArea($XMin,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$XMax,$Y-6,DIRECTION_VERTICAL,$GradientSettings); + else + $this->drawGradientArea($XMin,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMax,$Y-6,DIRECTION_VERTICAL,$GradientSettings); + $Poly = ""; $Poly[] = $X; $Poly[] = $Y; $Poly[] = $X-5; $Poly[] = $Y-5; $Poly[] = $X+5; $Poly[] = $Y-5; + $this->drawPolygon($Poly,array("R"=>$GradientEndR,"G"=>$GradientEndG,"B"=>$GradientEndB,"Alpha"=>$BoxAlpha,"NoBorder"=>TRUE)); + + /* Outer border */ + $OuterBorderColor = $this->allocateColor($this->Picture,100,100,100,$BoxAlpha); + imageline($this->Picture,$XMin,$Y-5,$X-5,$Y-5,$OuterBorderColor); + imageline($this->Picture,$X,$Y,$X-5,$Y-5,$OuterBorderColor); + imageline($this->Picture,$X,$Y,$X+5,$Y-5,$OuterBorderColor); + imageline($this->Picture,$X+5,$Y-5,$XMax,$Y-5,$OuterBorderColor); + if ( $NoTitle ) + { + imageline($this->Picture,$XMin,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$XMin,$Y-5,$OuterBorderColor); + imageline($this->Picture,$XMax,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$XMax,$Y-5,$OuterBorderColor); + imageline($this->Picture,$XMin,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$XMax,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$OuterBorderColor); + } + else + { + imageline($this->Picture,$XMin,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMin,$Y-5,$OuterBorderColor); + imageline($this->Picture,$XMax,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMax,$Y-5,$OuterBorderColor); + imageline($this->Picture,$XMin,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMax,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$OuterBorderColor); + } + + /* Inner border */ + $InnerBorderColor = $this->allocateColor($this->Picture,255,255,255,$BoxAlpha); + imageline($this->Picture,$XMin+1,$Y-6,$X-5,$Y-6,$InnerBorderColor); + imageline($this->Picture,$X,$Y-1,$X-5,$Y-6,$InnerBorderColor); + imageline($this->Picture,$X,$Y-1,$X+5,$Y-6,$InnerBorderColor); + imageline($this->Picture,$X+5,$Y-6,$XMax-1,$Y-6,$InnerBorderColor); + if ( $NoTitle ) + { + imageline($this->Picture,$XMin+1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$XMin+1,$Y-6,$InnerBorderColor); + imageline($this->Picture,$XMax-1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$XMax-1,$Y-6,$InnerBorderColor); + imageline($this->Picture,$XMin+1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$XMax-1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$InnerBorderColor); + } + else + { + imageline($this->Picture,$XMin+1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMin+1,$Y-6,$InnerBorderColor); + imageline($this->Picture,$XMax-1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMax-1,$Y-6,$InnerBorderColor); + imageline($this->Picture,$XMin+1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMax-1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$InnerBorderColor); + } + + /* Draw the separator line */ + if ( $TitleMode == LABEL_TITLE_NOBACKGROUND && !$NoTitle ) + { + $YPos = $Y-7-$CaptionHeight-$HorizontalMargin-$HorizontalMargin/2; + $XMargin = $VerticalMargin / 2; + $this->drawLine($XMin+$XMargin,$YPos+1,$XMax-$XMargin,$YPos+1,array("R"=>$GradientEndR,"G"=>$GradientEndG,"B"=>$GradientEndB,"Alpha"=>$BoxAlpha)); + $this->drawLine($XMin+$XMargin,$YPos,$XMax-$XMargin,$YPos,array("R"=>$GradientStartR,"G"=>$GradientStartG,"B"=>$GradientStartB,"Alpha"=>$BoxAlpha)); + } + elseif ( $TitleMode == LABEL_TITLE_BACKGROUND ) + { + $this->drawFilledRectangle($XMin,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMax,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin+$HorizontalMargin/2,array("R"=>$TitleBackgroundR,"G"=>$TitleBackgroundG,"B"=>$TitleBackgroundB,"Alpha"=>$BoxAlpha)); + imageline($this->Picture,$XMin+1,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin+$HorizontalMargin/2+1,$XMax-1,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin+$HorizontalMargin/2+1,$InnerBorderColor); + } + + /* Write the description */ + if ( !$NoTitle ) + $this->drawText($XMin+$VerticalMargin,$Y-7-$CaptionHeight-$HorizontalMargin*2,$Title,array("Align"=>TEXT_ALIGN_BOTTOMLEFT,"R"=>$TitleR,"G"=>$TitleG,"B"=>$TitleB)); + + /* Write the value */ + $YPos = $Y-5-$HorizontalMargin; $XPos = $XMin+$VerticalMargin+$SerieBoxSize+$SerieBoxSpacing; + foreach($Captions as $Key => $Caption) + { + $CaptionTxt = $Caption["Caption"]; + $TxtPos = $this->getTextBox($XPos,$YPos,$FontName,$FontSize,0,$CaptionTxt); + $CaptionHeight = ($TxtPos[0]["Y"] - $TxtPos[2]["Y"]); + + /* Write the serie color if needed */ + if ( $DrawSerieColor ) + { + $BoxSettings = array("R"=>$Caption["Format"]["R"],"G"=>$Caption["Format"]["G"],"B"=>$Caption["Format"]["B"],"Alpha"=>$Caption["Format"]["Alpha"],"BorderR"=>0,"BorderG"=>0,"BorderB"=>0); + $this->drawFilledRectangle($XMin+$VerticalMargin,$YPos-$SerieBoxSize,$XMin+$VerticalMargin+$SerieBoxSize,$YPos,$BoxSettings); + } + + $this->drawText($XPos,$YPos,$CaptionTxt,array("Align"=>TEXT_ALIGN_BOTTOMLEFT)); + + $YPos = $YPos - $CaptionHeight - $HorizontalMargin; + } + + $this->Shadow = $RestoreShadow; + } + + /* Draw a basic shape */ + function drawShape($X,$Y,$Shape,$PlotSize,$PlotBorder,$BorderSize,$R,$G,$B,$Alpha,$BorderR,$BorderG,$BorderB,$BorderAlpha) + { + if ( $Shape == SERIE_SHAPE_FILLEDCIRCLE ) + { + if ( $PlotBorder ) { $this->drawFilledCircle($X,$Y,$PlotSize+$BorderSize,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha)); } + $this->drawFilledCircle($X,$Y,$PlotSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); + } + elseif ( $Shape == SERIE_SHAPE_FILLEDSQUARE ) + { + if ( $PlotBorder ) { $this->drawFilledRectangle($X-$PlotSize-$BorderSize,$Y-$PlotSize-$BorderSize,$X+$PlotSize+$BorderSize,$Y+$PlotSize+$BorderSize,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha)); } + $this->drawFilledRectangle($X-$PlotSize,$Y-$PlotSize,$X+$PlotSize,$Y+$PlotSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); + } + elseif ( $Shape == SERIE_SHAPE_FILLEDTRIANGLE ) + { + if ( $PlotBorder ) + { + $Pos = ""; $Pos[]=$X; $Pos[]=$Y-$PlotSize-$BorderSize; $Pos[]=$X-$PlotSize-$BorderSize; $Pos[]=$Y+$PlotSize+$BorderSize; $Pos[]=$X+$PlotSize+$BorderSize; $Pos[]=$Y+$PlotSize+$BorderSize; + $this->drawPolygon($Pos,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha)); + } + + $Pos = ""; $Pos[]=$X; $Pos[]=$Y-$PlotSize; $Pos[]=$X-$PlotSize; $Pos[]=$Y+$PlotSize; $Pos[]=$X+$PlotSize; $Pos[]=$Y+$PlotSize; + $this->drawPolygon($Pos,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); + } + elseif ( $Shape == SERIE_SHAPE_TRIANGLE ) + { + $this->drawLine($X,$Y-$PlotSize,$X-$PlotSize,$Y+$PlotSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); + $this->drawLine($X-$PlotSize,$Y+$PlotSize,$X+$PlotSize,$Y+$PlotSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); + $this->drawLine($X+$PlotSize,$Y+$PlotSize,$X,$Y-$PlotSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); + } + elseif ( $Shape == SERIE_SHAPE_SQUARE ) + $this->drawRectangle($X-$PlotSize,$Y-$PlotSize,$X+$PlotSize,$Y+$PlotSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); + elseif ( $Shape == SERIE_SHAPE_CIRCLE ) + $this->drawCircle($X,$Y,$PlotSize,$PlotSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); + elseif ( $Shape == SERIE_SHAPE_DIAMOND ) + { + $Pos = ""; $Pos[]=$X-$PlotSize; $Pos[]=$Y; $Pos[]=$X; $Pos[]=$Y-$PlotSize; $Pos[]=$X+$PlotSize; $Pos[]=$Y; $Pos[]=$X; $Pos[]=$Y+$PlotSize; + $this->drawPolygon($Pos,array("NoFill"=>TRUE,"BorderR"=>$R,"BorderG"=>$G,"BorderB"=>$B,"BorderAlpha"=>$Alpha)); + } + elseif ( $Shape == SERIE_SHAPE_FILLEDDIAMOND ) + { + if ( $PlotBorder ) + { + $Pos = ""; $Pos[]=$X-$PlotSize-$BorderSize; $Pos[]=$Y; $Pos[]=$X; $Pos[]=$Y-$PlotSize-$BorderSize; $Pos[]=$X+$PlotSize+$BorderSize; $Pos[]=$Y; $Pos[]=$X; $Pos[]=$Y+$PlotSize+$BorderSize; + $this->drawPolygon($Pos,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha)); + } + + $Pos = ""; $Pos[]=$X-$PlotSize; $Pos[]=$Y; $Pos[]=$X; $Pos[]=$Y-$PlotSize; $Pos[]=$X+$PlotSize; $Pos[]=$Y; $Pos[]=$X; $Pos[]=$Y+$PlotSize; + $this->drawPolygon($Pos,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); + } + } + + function drawPolygonChart($Points,$Format="") + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $NoFill = isset($Format["NoFill"]) ? $Format["NoFill"] : FALSE; + $NoBorder = isset($Format["NoBorder"]) ? $Format["NoBorder"] : FALSE; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha / 2; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; + $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : NULL; + + if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; } + + $RestoreShadow = $this->Shadow; + $this->Shadow = FALSE; + + $AllIntegers = TRUE; + for($i=0;$i<=count($Points)-2;$i=$i+2) + { if ( $this->getFirstDecimal($Points[$i+1]) != 0 ) { $AllIntegers = FALSE; } } + + /* Convert polygon to segments */ + $Segments = ""; + for($i=2;$i<=count($Points)-2;$i=$i+2) + { $Segments[] = array("X1"=>$Points[$i-2],"Y1"=>$Points[$i-1],"X2"=>$Points[$i],"Y2"=>$Points[$i+1]); } + $Segments[] = array("X1"=>$Points[$i-2],"Y1"=>$Points[$i-1],"X2"=>$Points[0],"Y2"=>$Points[1]); + + /* Simplify straight lines */ + $Result = ""; $inHorizon = FALSE; $LastX = VOID; + foreach($Segments as $Key => $Pos) + { + if ( $Pos["Y1"] != $Pos["Y2"] ) + { + if ( $inHorizon ) { $inHorizon = FALSE; $Result[] = array("X1"=>$LastX,"Y1"=>$Pos["Y1"],"X2"=>$Pos["X1"],"Y2"=>$Pos["Y1"]); } + + $Result[] = array("X1"=>$Pos["X1"],"Y1"=>$Pos["Y1"],"X2"=>$Pos["X2"],"Y2"=>$Pos["Y2"]); + } + else { if ( !$inHorizon ) { $inHorizon = TRUE; $LastX = $Pos["X1"];} } + } + $Segments = $Result; + + /* Do we have something to draw */ + if ( $Segments == "" ) { return(0); } + + /* For segments debugging purpose */ + //foreach($Segments as $Key => $Pos) + // echo $Pos["X1"].",".$Pos["Y1"].",".$Pos["X2"].",".$Pos["Y2"]."\r\n"; + + /* Find out the min & max Y boundaries */ + $MinY = OUT_OF_SIGHT; $MaxY = OUT_OF_SIGHT; + foreach($Segments as $Key => $Coords) + { + if ( $MinY == OUT_OF_SIGHT || $MinY > min($Coords["Y1"],$Coords["Y2"]) ) { $MinY = min($Coords["Y1"],$Coords["Y2"]); } + if ( $MaxY == OUT_OF_SIGHT || $MaxY < max($Coords["Y1"],$Coords["Y2"]) ) { $MaxY = max($Coords["Y1"],$Coords["Y2"]); } + } + + if ( $AllIntegers ) { $YStep = 1; } else { $YStep = .5; } + + $MinY = floor($MinY); $MaxY = floor($MaxY); + + /* Scan each Y lines */ + $DefaultColor = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); + $DebugLine = 0; $DebugColor = $this->allocateColor($this->Picture,255,0,0,100); + + $MinY = floor($MinY); $MaxY = floor($MaxY); $YStep = 1; + + if ( !$NoFill ) + { + //if ( $DebugLine ) { $MinY = $DebugLine; $MaxY = $DebugLine; } + for($Y=$MinY;$Y<=$MaxY;$Y=$Y+$YStep) + { + $Intersections = ""; $LastSlope = NULL; $RestoreLast = "-"; + foreach($Segments as $Key => $Coords) + { + $X1 = $Coords["X1"]; $X2 = $Coords["X2"]; $Y1 = $Coords["Y1"]; $Y2 = $Coords["Y2"]; + + if ( min($Y1,$Y2) <= $Y && max($Y1,$Y2) >= $Y ) + { + if ( $Y1 == $Y2 ) + { $X = $X1; } + else + { $X = $X1 + ( ($Y-$Y1)*$X2 - ($Y-$Y1)*$X1 ) / ($Y2-$Y1); } + + $X = floor($X); + + if ( $X2 == $X1 ) + { $Slope = "!"; } + else + { + $SlopeC = ($Y2 - $Y1) / ($X2 - $X1); + if( $SlopeC == 0 ) + { $Slope = "="; } + elseif( $SlopeC > 0 ) + { $Slope = "+"; } + elseif ( $SlopeC < 0 ) + { $Slope = "-"; } + } + + if ( !is_array($Intersections) ) + { $Intersections[] = $X; } + elseif( !in_array($X,$Intersections) ) + { $Intersections[] = $X; } + elseif( in_array($X,$Intersections) ) + { + if ($Y == $DebugLine) { echo $Slope."/".$LastSlope."(".$X.") "; } + + if ( $Slope == "=" && $LastSlope == "-" ) { $Intersections[] = $X; } + if ( $Slope != $LastSlope && $LastSlope != "!" && $LastSlope != "=" ) { $Intersections[] = $X; } + if ( $Slope != $LastSlope && $LastSlope == "!" && $Slope == "+" ) { $Intersections[] = $X; } + } + + if ( is_array($Intersections) && in_array($X,$Intersections) && $LastSlope == "=" && ($Slope == "-" )) { $Intersections[] = $X; } + + $LastSlope = $Slope; + } + } + if ( $RestoreLast != "-" ) { $Intersections[] = $RestoreLast; echo "@".$Y."\r\n"; } + + if ( is_array($Intersections) ) + { + sort($Intersections); + + if ($Y == $DebugLine) { print_r($Intersections); } + + /* Remove NULL plots */ + $Result = ""; + for($i=0;$i<=count($Intersections)-1;$i=$i+2) + { + if ( isset($Intersections[$i+1]) ) + { if ( $Intersections[$i] != $Intersections[$i+1] ) { $Result[] = $Intersections[$i]; $Result[] = $Intersections[$i+1]; } } + } + + if ( is_array($Result) ) + { + $Intersections = $Result; + + $LastX = OUT_OF_SIGHT; + foreach($Intersections as $Key => $X) + { + if ( $LastX == OUT_OF_SIGHT ) + $LastX = $X; + elseif ( $LastX != OUT_OF_SIGHT ) + { + if ( $this->getFirstDecimal($LastX) > 1 ) { $LastX++; } + + $Color = $DefaultColor; + if ( $Threshold != NULL ) + { + foreach($Threshold as $Key => $Parameters) + { + if ( $Y <= $Parameters["MinX"] && $Y >= $Parameters["MaxX"]) + { + if ( isset($Parameters["R"]) ) { $R = $Parameters["R"]; } else { $R = 0; } + if ( isset($Parameters["G"]) ) { $G = $Parameters["G"]; } else { $G = 0; } + if ( isset($Parameters["B"]) ) { $B = $Parameters["B"]; } else { $B = 0; } + if ( isset($Parameters["Alpha"]) ) { $Alpha = $Parameters["Alpha"]; } else { $Alpha = 100; } + $Color = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha); + } + } + } + + imageline($this->Picture,$LastX,$Y,$X,$Y,$Color); + + if ( $Y == $DebugLine) { imageline($this->Picture,$LastX,$Y,$X,$Y,$DebugColor); } + + $LastX = OUT_OF_SIGHT; + } + } + } + } + } + } + + /* Draw the polygon border, if required */ + if ( !$NoBorder) + { + foreach($Segments as $Key => $Coords) + $this->drawLine($Coords["X1"],$Coords["Y1"],$Coords["X2"],$Coords["Y2"],array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Threshold"=>$Threshold)); + } + + $this->Shadow = $RestoreShadow; + } + + /* Return the abscissa margin */ + function getAbscissaMargin($Data) + { + foreach($Data["Axis"] as $AxisID => $Values) { if ( $Values["Identity"] == AXIS_X ) { return($Values["Margin"]); } } + return(0); + } + + } +?> \ No newline at end of file diff --git a/www/libs/pChart2.1.3/class/pImage.class.php b/www/libs/pChart2.1.4/class/pImage.class.php similarity index 96% rename from www/libs/pChart2.1.3/class/pImage.class.php rename to www/libs/pChart2.1.4/class/pImage.class.php index 27b262e5..a21466af 100644 --- a/www/libs/pChart2.1.3/class/pImage.class.php +++ b/www/libs/pChart2.1.4/class/pImage.class.php @@ -1,472 +1,472 @@ -TransparentBackground = $TransparentBackground; - - if ( $DataSet != NULL ) { $this->DataSet = $DataSet; } - - $this->XSize = $XSize; - $this->YSize = $YSize; - $this->Picture = imagecreatetruecolor($XSize,$YSize); - - if ( $this->TransparentBackground ) - { - imagealphablending($this->Picture,FALSE); - imagefilledrectangle($this->Picture, 0,0,$XSize, $YSize, imagecolorallocatealpha($this->Picture, 255, 255, 255, 127)); - imagealphablending($this->Picture,TRUE); - imagesavealpha($this->Picture,true); - } - else - { - $C_White = $this->AllocateColor($this->Picture,255,255,255); - imagefilledrectangle($this->Picture,0,0,$XSize,$YSize,$C_White); - } - } - - /* Enable / Disable and set shadow properties */ - function setShadow($Enabled=TRUE,$Format="") - { - $X = isset($Format["X"]) ? $Format["X"] : 2; - $Y = isset($Format["Y"]) ? $Format["Y"] : 2; - $R = isset($Format["R"]) ? $Format["R"] : 0; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 10; - - $this->Shadow = $Enabled; - $this->ShadowX = $X; - $this->ShadowY = $Y; - $this->ShadowR = $R; - $this->ShadowG = $G; - $this->ShadowB = $B; - $this->Shadowa = $Alpha; - } - - /* Set the graph area position */ - function setGraphArea($X1,$Y1,$X2,$Y2) - { - if ( $X2 < $X1 || $X1 == $X2 || $Y2 < $Y1 || $Y1 == $Y2 ) { return(-1); } - - $this->GraphAreaX1 = $X1; $this->DataSet->Data["GraphArea"]["X1"] = $X1; - $this->GraphAreaY1 = $Y1; $this->DataSet->Data["GraphArea"]["Y1"] = $Y1; - $this->GraphAreaX2 = $X2; $this->DataSet->Data["GraphArea"]["X2"] = $X2; - $this->GraphAreaY2 = $Y2; $this->DataSet->Data["GraphArea"]["Y2"] = $Y2; - } - - /* Return the width of the picture */ - function getWidth() - { return($this->XSize); } - - /* Return the heigth of the picture */ - function getHeight() - { return($this->YSize); } - - /* Render the picture to a file */ - function render($FileName) - { - if ( $this->TransparentBackground ) { imagealphablending($this->Picture,false); imagesavealpha($this->Picture,true); } - imagepng($this->Picture,$FileName); - } - - /* Render the picture to a web browser stream */ - function stroke($BrowserExpire=FALSE) - { - if ( $this->TransparentBackground ) { imagealphablending($this->Picture,false); imagesavealpha($this->Picture,true); } - - if ( $BrowserExpire ) - { - header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); - header("Cache-Control: no-cache"); - header("Pragma: no-cache"); - } - - header('Content-type: image/png'); - imagepng($this->Picture); - } - - /* Automatic output method based on the calling interface */ - function autoOutput($FileName="output.png") - { - if (php_sapi_name() == "cli") - $this->Render($FileName); - else - $this->Stroke(); - } - - /* Return the length between two points */ - function getLength($X1,$Y1,$X2,$Y2) - { return(sqrt(pow(max($X1,$X2)-min($X1,$X2),2)+pow(max($Y1,$Y2)-min($Y1,$Y2),2))); } - - /* Return the orientation of a line */ - function getAngle($X1,$Y1,$X2,$Y2) - { - $Opposite = $Y2 - $Y1; $Adjacent = $X2 - $X1;$Angle = rad2deg(atan2($Opposite,$Adjacent)); - if ($Angle > 0) { return($Angle); } else { return(360-abs($Angle)); } - } - - /* Return the surrounding box of text area */ - function getTextBox_deprecated($X,$Y,$FontName,$FontSize,$Angle,$Text) - { - $Size = imagettfbbox($FontSize,$Angle,$FontName,$Text); - $Width = $this->getLength($Size[0],$Size[1],$Size[2],$Size[3])+1; - $Height = $this->getLength($Size[2],$Size[3],$Size[4],$Size[5])+1; - - $RealPos[0]["X"] = $X; $RealPos[0]["Y"] = $Y; - $RealPos[1]["X"] = cos((360-$Angle)*PI/180)*$Width + $RealPos[0]["X"]; $RealPos[1]["Y"] = sin((360-$Angle)*PI/180)*$Width + $RealPos[0]["Y"]; - $RealPos[2]["X"] = cos((270-$Angle)*PI/180)*$Height + $RealPos[1]["X"]; $RealPos[2]["Y"] = sin((270-$Angle)*PI/180)*$Height + $RealPos[1]["Y"]; - $RealPos[3]["X"] = cos((180-$Angle)*PI/180)*$Width + $RealPos[2]["X"]; $RealPos[3]["Y"] = sin((180-$Angle)*PI/180)*$Width + $RealPos[2]["Y"]; - - $RealPos[TEXT_ALIGN_BOTTOMLEFT]["X"] = $RealPos[0]["X"]; $RealPos[TEXT_ALIGN_BOTTOMLEFT]["Y"] = $RealPos[0]["Y"]; - $RealPos[TEXT_ALIGN_BOTTOMRIGHT]["X"] = $RealPos[1]["X"]; $RealPos[TEXT_ALIGN_BOTTOMRIGHT]["Y"] = $RealPos[1]["Y"]; - - return($RealPos); - } - - /* Return the surrounding box of text area */ - function getTextBox($X,$Y,$FontName,$FontSize,$Angle,$Text) - { - $coords = imagettfbbox($FontSize, 0, $FontName, $Text); - - $a = deg2rad($Angle); $ca = cos($a); $sa = sin($a); $RealPos = array(); - for($i = 0; $i < 7; $i += 2) - { - $RealPos[$i/2]["X"] = $X + round($coords[$i] * $ca + $coords[$i+1] * $sa); - $RealPos[$i/2]["Y"] = $Y + round($coords[$i+1] * $ca - $coords[$i] * $sa); - } - - $RealPos[TEXT_ALIGN_BOTTOMLEFT]["X"] = $RealPos[0]["X"]; $RealPos[TEXT_ALIGN_BOTTOMLEFT]["Y"] = $RealPos[0]["Y"]; - $RealPos[TEXT_ALIGN_BOTTOMRIGHT]["X"] = $RealPos[1]["X"]; $RealPos[TEXT_ALIGN_BOTTOMRIGHT]["Y"] = $RealPos[1]["Y"]; - $RealPos[TEXT_ALIGN_TOPLEFT]["X"] = $RealPos[3]["X"]; $RealPos[TEXT_ALIGN_TOPLEFT]["Y"] = $RealPos[3]["Y"]; - $RealPos[TEXT_ALIGN_TOPRIGHT]["X"] = $RealPos[2]["X"]; $RealPos[TEXT_ALIGN_TOPRIGHT]["Y"] = $RealPos[2]["Y"]; - $RealPos[TEXT_ALIGN_BOTTOMMIDDLE]["X"] = ($RealPos[1]["X"]-$RealPos[0]["X"])/2+$RealPos[0]["X"]; $RealPos[TEXT_ALIGN_BOTTOMMIDDLE]["Y"] = ($RealPos[0]["Y"]-$RealPos[1]["Y"])/2+$RealPos[1]["Y"]; - $RealPos[TEXT_ALIGN_TOPMIDDLE]["X"] = ($RealPos[2]["X"]-$RealPos[3]["X"])/2+$RealPos[3]["X"]; $RealPos[TEXT_ALIGN_TOPMIDDLE]["Y"] = ($RealPos[3]["Y"]-$RealPos[2]["Y"])/2+$RealPos[2]["Y"]; - $RealPos[TEXT_ALIGN_MIDDLELEFT]["X"] = ($RealPos[0]["X"]-$RealPos[3]["X"])/2+$RealPos[3]["X"]; $RealPos[TEXT_ALIGN_MIDDLELEFT]["Y"] = ($RealPos[0]["Y"]-$RealPos[3]["Y"])/2+$RealPos[3]["Y"]; - $RealPos[TEXT_ALIGN_MIDDLERIGHT]["X"] = ($RealPos[1]["X"]-$RealPos[2]["X"])/2+$RealPos[2]["X"]; $RealPos[TEXT_ALIGN_MIDDLERIGHT]["Y"] = ($RealPos[1]["Y"]-$RealPos[2]["Y"])/2+$RealPos[2]["Y"]; - $RealPos[TEXT_ALIGN_MIDDLEMIDDLE]["X"] = ($RealPos[1]["X"]-$RealPos[3]["X"])/2+$RealPos[3]["X"]; $RealPos[TEXT_ALIGN_MIDDLEMIDDLE]["Y"] = ($RealPos[0]["Y"]-$RealPos[2]["Y"])/2+$RealPos[2]["Y"]; - - return($RealPos); - } - - /* Set current font properties */ - function setFontProperties($Format="") - { - $R = isset($Format["R"]) ? $Format["R"] : -1; - $G = isset($Format["G"]) ? $Format["G"] : -1; - $B = isset($Format["B"]) ? $Format["B"] : -1; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $FontName = isset($Format["FontName"]) ? $Format["FontName"] : NULL; - $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : NULL; - - if ( $R != -1) { $this->FontColorR = $R; } - if ( $G != -1) { $this->FontColorG = $G; } - if ( $B != -1) { $this->FontColorB = $B; } - if ( $Alpha != NULL) { $this->FontColorA = $Alpha; } - - if ( $FontName != NULL ) - $this->FontName = $FontName; - - if ( $FontSize != NULL ) - $this->FontSize = $FontSize; - } - - /* Returns the 1st decimal values (used to correct AA bugs) */ - function getFirstDecimal($Value) - { - $Values = preg_split("/\./",$Value); - if ( isset($Values[1]) ) { return(substr($Values[1],0,1)); } else { return(0); } - } - - /* Attach a dataset to your pChart Object */ - function setDataSet(&$DataSet) - { $this->DataSet = $DataSet; } - - /* Print attached dataset contents to STDOUT */ - function printDataSet() - { print_r($this->DataSet); } - - /* Initialise the image map methods */ - function initialiseImageMap($Name="pChart",$StorageMode=IMAGE_MAP_STORAGE_SESSION,$UniqueID="imageMap",$StorageFolder="tmp") - { - $this->ImageMapIndex = $Name; - $this->ImageMapStorageMode = $StorageMode; - - if ($StorageMode == IMAGE_MAP_STORAGE_SESSION) - { - if(!isset($_SESSION)) { session_start(); } - $_SESSION[$this->ImageMapIndex] = NULL; - } - elseif($StorageMode == IMAGE_MAP_STORAGE_FILE) - { - $this->ImageMapFileName = $UniqueID; - $this->ImageMapStorageFolder = $StorageFolder; - - if (file_exists($StorageFolder."/".$UniqueID.".map")) { unlink($StorageFolder."/".$UniqueID.".map"); } - } - } - - /* Add a zone to the image map */ - function addToImageMap($Type,$Plots,$Color=NULL,$Title=NULL,$Message=NULL,$HTMLEncode=FALSE) - { - if ( $this->ImageMapStorageMode == NULL ) { $this->initialiseImageMap(); } - - /* Encode the characters in the imagemap in HTML standards */ - $Title = str_replace("€","\u20AC",$Title); - $Title = htmlentities($Title,ENT_QUOTES,"ISO-8859-15"); - if ( $HTMLEncode ) - { - $Message = htmlentities($Message,ENT_QUOTES,"ISO-8859-15"); - $Message = str_replace("<","<",$Message); - $Message = str_replace(">",">",$Message); - } - - if ( $this->ImageMapStorageMode == IMAGE_MAP_STORAGE_SESSION ) - { - if(!isset($_SESSION)) { $this->initialiseImageMap(); } - $_SESSION[$this->ImageMapIndex][] = array($Type,$Plots,$Color,$Title,$Message); - } - elseif($this->ImageMapStorageMode == IMAGE_MAP_STORAGE_FILE) - { - $Handle = fopen($this->ImageMapStorageFolder."/".$this->ImageMapFileName.".map", 'a'); - fwrite($Handle, $Type.IMAGE_MAP_DELIMITER.$Plots.IMAGE_MAP_DELIMITER.$Color.IMAGE_MAP_DELIMITER.$Title.IMAGE_MAP_DELIMITER.$Message."\r\n"); - fclose($Handle); - } - } - - /* Remove VOID values from an imagemap custom values array */ - function removeVOIDFromArray($SerieName, $Values) - { - if ( !isset($this->DataSet->Data["Series"][$SerieName]) ) { return(-1); } - - $Result = ""; - foreach($this->DataSet->Data["Series"][$SerieName]["Data"] as $Key => $Value) - { if ( $Value != VOID && isset($Values[$Key]) ) { $Result[] = $Values[$Key]; } } - return($Result); - } - - /* Replace the title of one image map serie */ - function replaceImageMapTitle($OldTitle, $NewTitle) - { - if ( $this->ImageMapStorageMode == NULL ) { return(-1); } - - if ( is_array($NewTitle) ) { $NewTitle = $this->removeVOIDFromArray($OldTitle, $NewTitle); } - - if ( $this->ImageMapStorageMode == IMAGE_MAP_STORAGE_SESSION ) - { - if(!isset($_SESSION)) { return(-1); } - if ( is_array($NewTitle) ) - { $ID = 0; foreach($_SESSION[$this->ImageMapIndex] as $Key => $Settings) { if ( $Settings[3] == $OldTitle && isset($NewTitle[$ID])) { $_SESSION[$this->ImageMapIndex][$Key][3] = $NewTitle[$ID]; $ID++; } } } - else - { foreach($_SESSION[$this->ImageMapIndex] as $Key => $Settings) { if ( $Settings[3] == $OldTitle ) { $_SESSION[$this->ImageMapIndex][$Key][3] = $NewTitle; } } } - } - elseif( $this->ImageMapStorageMode == IMAGE_MAP_STORAGE_FILE ) - { - $TempArray = ""; - $Handle = @fopen($this->ImageMapStorageFolder."/".$this->ImageMapFileName.".map", "r"); - if ($Handle) - { - while (($Buffer = fgets($Handle, 4096)) !== false) - { - $Fields = split(IMAGE_MAP_DELIMITER,str_replace(array(chr(10),chr(13)),"",$Buffer)); - $TempArray[] = array($Fields[0],$Fields[1],$Fields[2],$Fields[3],$Fields[4]); - } - fclose($Handle); - - if ( is_array($NewTitle) ) - { $ID = 0; foreach($TempArray as $Key => $Settings) { if ( $Settings[3] == $OldTitle && isset($NewTitle[$ID]) ) { $TempArray[$Key][3] = $NewTitle[$ID]; $ID++; } } } - else - { foreach($TempArray as $Key => $Settings) { if ( $Settings[3] == $OldTitle ) { $TempArray[$Key][3] = $NewTitle; } } } - - $Handle = fopen($this->ImageMapStorageFolder."/".$this->ImageMapFileName.".map", 'w'); - foreach($TempArray as $Key => $Settings) - { fwrite($Handle, $Settings[0].IMAGE_MAP_DELIMITER.$Settings[1].IMAGE_MAP_DELIMITER.$Settings[2].IMAGE_MAP_DELIMITER.$Settings[3].IMAGE_MAP_DELIMITER.$Settings[4]."\r\n"); } - fclose($Handle); - } - } - } - - /* Replace the values of the image map contents */ - function replaceImageMapValues($Title, $Values) - { - if ( $this->ImageMapStorageMode == NULL ) { return(-1); } - - $Values = $this->removeVOIDFromArray($Title, $Values); - $ID = 0; - if ( $this->ImageMapStorageMode == IMAGE_MAP_STORAGE_SESSION ) - { - if(!isset($_SESSION)) { return(-1); } - foreach($_SESSION[$this->ImageMapIndex] as $Key => $Settings) { if ( $Settings[3] == $Title ) { if ( isset($Values[$ID]) ) { $_SESSION[$this->ImageMapIndex][$Key][4] = $Values[$ID]; } $ID++; } } - } - elseif( $this->ImageMapStorageMode == IMAGE_MAP_STORAGE_FILE ) - { - $TempArray = ""; - $Handle = @fopen($this->ImageMapStorageFolder."/".$this->ImageMapFileName.".map", "r"); - if ($Handle) - { - while (($Buffer = fgets($Handle, 4096)) !== false) - { - $Fields = split(IMAGE_MAP_DELIMITER,str_replace(array(chr(10),chr(13)),"",$Buffer)); - $TempArray[] = array($Fields[0],$Fields[1],$Fields[2],$Fields[3],$Fields[4]); - } - fclose($Handle); - - foreach($TempArray as $Key => $Settings) { if ( $Settings[3] == $Title ) { if ( isset($Values[$ID]) ) { $TempArray[$Key][4] = $Values[$ID]; } $ID++; } } - - $Handle = fopen($this->ImageMapStorageFolder."/".$this->ImageMapFileName.".map", 'w'); - foreach($TempArray as $Key => $Settings) - { fwrite($Handle, $Settings[0].IMAGE_MAP_DELIMITER.$Settings[1].IMAGE_MAP_DELIMITER.$Settings[2].IMAGE_MAP_DELIMITER.$Settings[3].IMAGE_MAP_DELIMITER.$Settings[4]."\r\n"); } - fclose($Handle); - } - } - } - - /* Dump the image map */ - function dumpImageMap($Name="pChart",$StorageMode=IMAGE_MAP_STORAGE_SESSION,$UniqueID="imageMap",$StorageFolder="tmp") - { - $this->ImageMapIndex = $Name; - $this->ImageMapStorageMode = $StorageMode; - - if ( $this->ImageMapStorageMode == IMAGE_MAP_STORAGE_SESSION ) - { - if(!isset($_SESSION)) { session_start(); } - if ( $_SESSION[$Name] != NULL ) - { - foreach($_SESSION[$Name] as $Key => $Params) - { echo $Params[0].IMAGE_MAP_DELIMITER.$Params[1].IMAGE_MAP_DELIMITER.$Params[2].IMAGE_MAP_DELIMITER.$Params[3].IMAGE_MAP_DELIMITER.$Params[4]."\r\n"; } - } - } - elseif( $this->ImageMapStorageMode == IMAGE_MAP_STORAGE_FILE ) - { - if (file_exists($StorageFolder."/".$UniqueID.".map")) - { - $Handle = @fopen($StorageFolder."/".$UniqueID.".map", "r"); - if ($Handle) { while (($Buffer = fgets($Handle, 4096)) !== false) { echo $Buffer; } } - fclose($Handle); - - if ( $this->ImageMapAutoDelete ) { unlink($StorageFolder."/".$UniqueID.".map"); } - } - } - - /* When the image map is returned to the client, the script ends */ - exit(); - } - - /* Return the HTML converted color from the RGB composite values */ - function toHTMLColor($R,$G,$B) - { - $R=intval($R); $G=intval($G); $B=intval($B); - $R=dechex($R<0?0:($R>255?255:$R)); $G=dechex($G<0?0:($G>255?255:$G));$B=dechex($B<0?0:($B>255?255:$B)); - $Color="#".(strlen($R) < 2?'0':'').$R; $Color.=(strlen($G) < 2?'0':'').$G; $Color.= (strlen($B) < 2?'0':'').$B; - return($Color); - } - - /* Reverse an array of points */ - function reversePlots($Plots) - { - $Result = ""; - for($i=count($Plots)-2;$i>=0;$i=$i-2) { $Result[] = $Plots[$i]; $Result[] = $Plots[$i+1]; } - return($Result); - } - - /* Mirror Effect */ - function drawAreaMirror($X,$Y,$Width,$Height,$Format="") - { - $StartAlpha = isset($Format["StartAlpha"]) ? $Format["StartAlpha"] : 80; - $EndAlpha = isset($Format["EndAlpha"]) ? $Format["EndAlpha"] : 0; - - $AlphaStep = ($StartAlpha-$EndAlpha)/$Height; - - $Picture = imagecreatetruecolor($this->XSize,$this->YSize); - imagecopy($Picture,$this->Picture,0,0,0,0,$this->XSize,$this->YSize); - - for($i=1;$i<=$Height;$i++) - { - if ( $Y+($i-1) < $this->YSize && $Y-$i > 0 ) { imagecopymerge($Picture,$this->Picture,$X,$Y+($i-1),$X,$Y-$i,$Width,1,$StartAlpha-$AlphaStep*$i); } - } - - imagecopy($this->Picture,$Picture,0,0,0,0,$this->XSize,$this->YSize); - } - } +TransparentBackground = $TransparentBackground; + + if ( $DataSet != NULL ) { $this->DataSet = $DataSet; } + + $this->XSize = $XSize; + $this->YSize = $YSize; + $this->Picture = imagecreatetruecolor($XSize,$YSize); + + if ( $this->TransparentBackground ) + { + imagealphablending($this->Picture,FALSE); + imagefilledrectangle($this->Picture, 0,0,$XSize, $YSize, imagecolorallocatealpha($this->Picture, 255, 255, 255, 127)); + imagealphablending($this->Picture,TRUE); + imagesavealpha($this->Picture,true); + } + else + { + $C_White = $this->AllocateColor($this->Picture,255,255,255); + imagefilledrectangle($this->Picture,0,0,$XSize,$YSize,$C_White); + } + } + + /* Enable / Disable and set shadow properties */ + function setShadow($Enabled=TRUE,$Format="") + { + $X = isset($Format["X"]) ? $Format["X"] : 2; + $Y = isset($Format["Y"]) ? $Format["Y"] : 2; + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 10; + + $this->Shadow = $Enabled; + $this->ShadowX = $X; + $this->ShadowY = $Y; + $this->ShadowR = $R; + $this->ShadowG = $G; + $this->ShadowB = $B; + $this->Shadowa = $Alpha; + } + + /* Set the graph area position */ + function setGraphArea($X1,$Y1,$X2,$Y2) + { + if ( $X2 < $X1 || $X1 == $X2 || $Y2 < $Y1 || $Y1 == $Y2 ) { return(-1); } + + $this->GraphAreaX1 = $X1; $this->DataSet->Data["GraphArea"]["X1"] = $X1; + $this->GraphAreaY1 = $Y1; $this->DataSet->Data["GraphArea"]["Y1"] = $Y1; + $this->GraphAreaX2 = $X2; $this->DataSet->Data["GraphArea"]["X2"] = $X2; + $this->GraphAreaY2 = $Y2; $this->DataSet->Data["GraphArea"]["Y2"] = $Y2; + } + + /* Return the width of the picture */ + function getWidth() + { return($this->XSize); } + + /* Return the heigth of the picture */ + function getHeight() + { return($this->YSize); } + + /* Render the picture to a file */ + function render($FileName) + { + if ( $this->TransparentBackground ) { imagealphablending($this->Picture,false); imagesavealpha($this->Picture,true); } + imagepng($this->Picture,$FileName); + } + + /* Render the picture to a web browser stream */ + function stroke($BrowserExpire=FALSE) + { + if ( $this->TransparentBackground ) { imagealphablending($this->Picture,false); imagesavealpha($this->Picture,true); } + + if ( $BrowserExpire ) + { + header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); + header("Cache-Control: no-cache"); + header("Pragma: no-cache"); + } + + header('Content-type: image/png'); + imagepng($this->Picture); + } + + /* Automatic output method based on the calling interface */ + function autoOutput($FileName="output.png") + { + if (php_sapi_name() == "cli") + $this->Render($FileName); + else + $this->Stroke(); + } + + /* Return the length between two points */ + function getLength($X1,$Y1,$X2,$Y2) + { return(sqrt(pow(max($X1,$X2)-min($X1,$X2),2)+pow(max($Y1,$Y2)-min($Y1,$Y2),2))); } + + /* Return the orientation of a line */ + function getAngle($X1,$Y1,$X2,$Y2) + { + $Opposite = $Y2 - $Y1; $Adjacent = $X2 - $X1;$Angle = rad2deg(atan2($Opposite,$Adjacent)); + if ($Angle > 0) { return($Angle); } else { return(360-abs($Angle)); } + } + + /* Return the surrounding box of text area */ + function getTextBox_deprecated($X,$Y,$FontName,$FontSize,$Angle,$Text) + { + $Size = imagettfbbox($FontSize,$Angle,$FontName,$Text); + $Width = $this->getLength($Size[0],$Size[1],$Size[2],$Size[3])+1; + $Height = $this->getLength($Size[2],$Size[3],$Size[4],$Size[5])+1; + + $RealPos[0]["X"] = $X; $RealPos[0]["Y"] = $Y; + $RealPos[1]["X"] = cos((360-$Angle)*PI/180)*$Width + $RealPos[0]["X"]; $RealPos[1]["Y"] = sin((360-$Angle)*PI/180)*$Width + $RealPos[0]["Y"]; + $RealPos[2]["X"] = cos((270-$Angle)*PI/180)*$Height + $RealPos[1]["X"]; $RealPos[2]["Y"] = sin((270-$Angle)*PI/180)*$Height + $RealPos[1]["Y"]; + $RealPos[3]["X"] = cos((180-$Angle)*PI/180)*$Width + $RealPos[2]["X"]; $RealPos[3]["Y"] = sin((180-$Angle)*PI/180)*$Width + $RealPos[2]["Y"]; + + $RealPos[TEXT_ALIGN_BOTTOMLEFT]["X"] = $RealPos[0]["X"]; $RealPos[TEXT_ALIGN_BOTTOMLEFT]["Y"] = $RealPos[0]["Y"]; + $RealPos[TEXT_ALIGN_BOTTOMRIGHT]["X"] = $RealPos[1]["X"]; $RealPos[TEXT_ALIGN_BOTTOMRIGHT]["Y"] = $RealPos[1]["Y"]; + + return($RealPos); + } + + /* Return the surrounding box of text area */ + function getTextBox($X,$Y,$FontName,$FontSize,$Angle,$Text) + { + $coords = imagettfbbox($FontSize, 0, $FontName, $Text); + + $a = deg2rad($Angle); $ca = cos($a); $sa = sin($a); $RealPos = array(); + for($i = 0; $i < 7; $i += 2) + { + $RealPos[$i/2]["X"] = $X + round($coords[$i] * $ca + $coords[$i+1] * $sa); + $RealPos[$i/2]["Y"] = $Y + round($coords[$i+1] * $ca - $coords[$i] * $sa); + } + + $RealPos[TEXT_ALIGN_BOTTOMLEFT]["X"] = $RealPos[0]["X"]; $RealPos[TEXT_ALIGN_BOTTOMLEFT]["Y"] = $RealPos[0]["Y"]; + $RealPos[TEXT_ALIGN_BOTTOMRIGHT]["X"] = $RealPos[1]["X"]; $RealPos[TEXT_ALIGN_BOTTOMRIGHT]["Y"] = $RealPos[1]["Y"]; + $RealPos[TEXT_ALIGN_TOPLEFT]["X"] = $RealPos[3]["X"]; $RealPos[TEXT_ALIGN_TOPLEFT]["Y"] = $RealPos[3]["Y"]; + $RealPos[TEXT_ALIGN_TOPRIGHT]["X"] = $RealPos[2]["X"]; $RealPos[TEXT_ALIGN_TOPRIGHT]["Y"] = $RealPos[2]["Y"]; + $RealPos[TEXT_ALIGN_BOTTOMMIDDLE]["X"] = ($RealPos[1]["X"]-$RealPos[0]["X"])/2+$RealPos[0]["X"]; $RealPos[TEXT_ALIGN_BOTTOMMIDDLE]["Y"] = ($RealPos[0]["Y"]-$RealPos[1]["Y"])/2+$RealPos[1]["Y"]; + $RealPos[TEXT_ALIGN_TOPMIDDLE]["X"] = ($RealPos[2]["X"]-$RealPos[3]["X"])/2+$RealPos[3]["X"]; $RealPos[TEXT_ALIGN_TOPMIDDLE]["Y"] = ($RealPos[3]["Y"]-$RealPos[2]["Y"])/2+$RealPos[2]["Y"]; + $RealPos[TEXT_ALIGN_MIDDLELEFT]["X"] = ($RealPos[0]["X"]-$RealPos[3]["X"])/2+$RealPos[3]["X"]; $RealPos[TEXT_ALIGN_MIDDLELEFT]["Y"] = ($RealPos[0]["Y"]-$RealPos[3]["Y"])/2+$RealPos[3]["Y"]; + $RealPos[TEXT_ALIGN_MIDDLERIGHT]["X"] = ($RealPos[1]["X"]-$RealPos[2]["X"])/2+$RealPos[2]["X"]; $RealPos[TEXT_ALIGN_MIDDLERIGHT]["Y"] = ($RealPos[1]["Y"]-$RealPos[2]["Y"])/2+$RealPos[2]["Y"]; + $RealPos[TEXT_ALIGN_MIDDLEMIDDLE]["X"] = ($RealPos[1]["X"]-$RealPos[3]["X"])/2+$RealPos[3]["X"]; $RealPos[TEXT_ALIGN_MIDDLEMIDDLE]["Y"] = ($RealPos[0]["Y"]-$RealPos[2]["Y"])/2+$RealPos[2]["Y"]; + + return($RealPos); + } + + /* Set current font properties */ + function setFontProperties($Format="") + { + $R = isset($Format["R"]) ? $Format["R"] : -1; + $G = isset($Format["G"]) ? $Format["G"] : -1; + $B = isset($Format["B"]) ? $Format["B"] : -1; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $FontName = isset($Format["FontName"]) ? $Format["FontName"] : NULL; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : NULL; + + if ( $R != -1) { $this->FontColorR = $R; } + if ( $G != -1) { $this->FontColorG = $G; } + if ( $B != -1) { $this->FontColorB = $B; } + if ( $Alpha != NULL) { $this->FontColorA = $Alpha; } + + if ( $FontName != NULL ) + $this->FontName = $FontName; + + if ( $FontSize != NULL ) + $this->FontSize = $FontSize; + } + + /* Returns the 1st decimal values (used to correct AA bugs) */ + function getFirstDecimal($Value) + { + $Values = preg_split("/\./",$Value); + if ( isset($Values[1]) ) { return(substr($Values[1],0,1)); } else { return(0); } + } + + /* Attach a dataset to your pChart Object */ + function setDataSet(&$DataSet) + { $this->DataSet = $DataSet; } + + /* Print attached dataset contents to STDOUT */ + function printDataSet() + { print_r($this->DataSet); } + + /* Initialise the image map methods */ + function initialiseImageMap($Name="pChart",$StorageMode=IMAGE_MAP_STORAGE_SESSION,$UniqueID="imageMap",$StorageFolder="tmp") + { + $this->ImageMapIndex = $Name; + $this->ImageMapStorageMode = $StorageMode; + + if ($StorageMode == IMAGE_MAP_STORAGE_SESSION) + { + if(!isset($_SESSION)) { session_start(); } + $_SESSION[$this->ImageMapIndex] = NULL; + } + elseif($StorageMode == IMAGE_MAP_STORAGE_FILE) + { + $this->ImageMapFileName = $UniqueID; + $this->ImageMapStorageFolder = $StorageFolder; + + if (file_exists($StorageFolder."/".$UniqueID.".map")) { unlink($StorageFolder."/".$UniqueID.".map"); } + } + } + + /* Add a zone to the image map */ + function addToImageMap($Type,$Plots,$Color=NULL,$Title=NULL,$Message=NULL,$HTMLEncode=FALSE) + { + if ( $this->ImageMapStorageMode == NULL ) { $this->initialiseImageMap(); } + + /* Encode the characters in the imagemap in HTML standards */ + $Title = str_replace("€","\u20AC",$Title); + $Title = htmlentities($Title,ENT_QUOTES,"ISO-8859-15"); + if ( $HTMLEncode ) + { + $Message = htmlentities($Message,ENT_QUOTES,"ISO-8859-15"); + $Message = str_replace("<","<",$Message); + $Message = str_replace(">",">",$Message); + } + + if ( $this->ImageMapStorageMode == IMAGE_MAP_STORAGE_SESSION ) + { + if(!isset($_SESSION)) { $this->initialiseImageMap(); } + $_SESSION[$this->ImageMapIndex][] = array($Type,$Plots,$Color,$Title,$Message); + } + elseif($this->ImageMapStorageMode == IMAGE_MAP_STORAGE_FILE) + { + $Handle = fopen($this->ImageMapStorageFolder."/".$this->ImageMapFileName.".map", 'a'); + fwrite($Handle, $Type.IMAGE_MAP_DELIMITER.$Plots.IMAGE_MAP_DELIMITER.$Color.IMAGE_MAP_DELIMITER.$Title.IMAGE_MAP_DELIMITER.$Message."\r\n"); + fclose($Handle); + } + } + + /* Remove VOID values from an imagemap custom values array */ + function removeVOIDFromArray($SerieName, $Values) + { + if ( !isset($this->DataSet->Data["Series"][$SerieName]) ) { return(-1); } + + $Result = ""; + foreach($this->DataSet->Data["Series"][$SerieName]["Data"] as $Key => $Value) + { if ( $Value != VOID && isset($Values[$Key]) ) { $Result[] = $Values[$Key]; } } + return($Result); + } + + /* Replace the title of one image map serie */ + function replaceImageMapTitle($OldTitle, $NewTitle) + { + if ( $this->ImageMapStorageMode == NULL ) { return(-1); } + + if ( is_array($NewTitle) ) { $NewTitle = $this->removeVOIDFromArray($OldTitle, $NewTitle); } + + if ( $this->ImageMapStorageMode == IMAGE_MAP_STORAGE_SESSION ) + { + if(!isset($_SESSION)) { return(-1); } + if ( is_array($NewTitle) ) + { $ID = 0; foreach($_SESSION[$this->ImageMapIndex] as $Key => $Settings) { if ( $Settings[3] == $OldTitle && isset($NewTitle[$ID])) { $_SESSION[$this->ImageMapIndex][$Key][3] = $NewTitle[$ID]; $ID++; } } } + else + { foreach($_SESSION[$this->ImageMapIndex] as $Key => $Settings) { if ( $Settings[3] == $OldTitle ) { $_SESSION[$this->ImageMapIndex][$Key][3] = $NewTitle; } } } + } + elseif( $this->ImageMapStorageMode == IMAGE_MAP_STORAGE_FILE ) + { + $TempArray = ""; + $Handle = @fopen($this->ImageMapStorageFolder."/".$this->ImageMapFileName.".map", "r"); + if ($Handle) + { + while (($Buffer = fgets($Handle, 4096)) !== false) + { + $Fields = preg_split("/".IMAGE_MAP_DELIMITER."/",str_replace(array(chr(10),chr(13)),"",$Buffer)); + $TempArray[] = array($Fields[0],$Fields[1],$Fields[2],$Fields[3],$Fields[4]); + } + fclose($Handle); + + if ( is_array($NewTitle) ) + { $ID = 0; foreach($TempArray as $Key => $Settings) { if ( $Settings[3] == $OldTitle && isset($NewTitle[$ID]) ) { $TempArray[$Key][3] = $NewTitle[$ID]; $ID++; } } } + else + { foreach($TempArray as $Key => $Settings) { if ( $Settings[3] == $OldTitle ) { $TempArray[$Key][3] = $NewTitle; } } } + + $Handle = fopen($this->ImageMapStorageFolder."/".$this->ImageMapFileName.".map", 'w'); + foreach($TempArray as $Key => $Settings) + { fwrite($Handle, $Settings[0].IMAGE_MAP_DELIMITER.$Settings[1].IMAGE_MAP_DELIMITER.$Settings[2].IMAGE_MAP_DELIMITER.$Settings[3].IMAGE_MAP_DELIMITER.$Settings[4]."\r\n"); } + fclose($Handle); + } + } + } + + /* Replace the values of the image map contents */ + function replaceImageMapValues($Title, $Values) + { + if ( $this->ImageMapStorageMode == NULL ) { return(-1); } + + $Values = $this->removeVOIDFromArray($Title, $Values); + $ID = 0; + if ( $this->ImageMapStorageMode == IMAGE_MAP_STORAGE_SESSION ) + { + if(!isset($_SESSION)) { return(-1); } + foreach($_SESSION[$this->ImageMapIndex] as $Key => $Settings) { if ( $Settings[3] == $Title ) { if ( isset($Values[$ID]) ) { $_SESSION[$this->ImageMapIndex][$Key][4] = $Values[$ID]; } $ID++; } } + } + elseif( $this->ImageMapStorageMode == IMAGE_MAP_STORAGE_FILE ) + { + $TempArray = ""; + $Handle = @fopen($this->ImageMapStorageFolder."/".$this->ImageMapFileName.".map", "r"); + if ($Handle) + { + while (($Buffer = fgets($Handle, 4096)) !== false) + { + $Fields = preg_split("/".IMAGE_MAP_DELIMITER."/",str_replace(array(chr(10),chr(13)),"",$Buffer)); + $TempArray[] = array($Fields[0],$Fields[1],$Fields[2],$Fields[3],$Fields[4]); + } + fclose($Handle); + + foreach($TempArray as $Key => $Settings) { if ( $Settings[3] == $Title ) { if ( isset($Values[$ID]) ) { $TempArray[$Key][4] = $Values[$ID]; } $ID++; } } + + $Handle = fopen($this->ImageMapStorageFolder."/".$this->ImageMapFileName.".map", 'w'); + foreach($TempArray as $Key => $Settings) + { fwrite($Handle, $Settings[0].IMAGE_MAP_DELIMITER.$Settings[1].IMAGE_MAP_DELIMITER.$Settings[2].IMAGE_MAP_DELIMITER.$Settings[3].IMAGE_MAP_DELIMITER.$Settings[4]."\r\n"); } + fclose($Handle); + } + } + } + + /* Dump the image map */ + function dumpImageMap($Name="pChart",$StorageMode=IMAGE_MAP_STORAGE_SESSION,$UniqueID="imageMap",$StorageFolder="tmp") + { + $this->ImageMapIndex = $Name; + $this->ImageMapStorageMode = $StorageMode; + + if ( $this->ImageMapStorageMode == IMAGE_MAP_STORAGE_SESSION ) + { + if(!isset($_SESSION)) { session_start(); } + if ( $_SESSION[$Name] != NULL ) + { + foreach($_SESSION[$Name] as $Key => $Params) + { echo $Params[0].IMAGE_MAP_DELIMITER.$Params[1].IMAGE_MAP_DELIMITER.$Params[2].IMAGE_MAP_DELIMITER.$Params[3].IMAGE_MAP_DELIMITER.$Params[4]."\r\n"; } + } + } + elseif( $this->ImageMapStorageMode == IMAGE_MAP_STORAGE_FILE ) + { + if (file_exists($StorageFolder."/".$UniqueID.".map")) + { + $Handle = @fopen($StorageFolder."/".$UniqueID.".map", "r"); + if ($Handle) { while (($Buffer = fgets($Handle, 4096)) !== false) { echo $Buffer; } } + fclose($Handle); + + if ( $this->ImageMapAutoDelete ) { unlink($StorageFolder."/".$UniqueID.".map"); } + } + } + + /* When the image map is returned to the client, the script ends */ + exit(); + } + + /* Return the HTML converted color from the RGB composite values */ + function toHTMLColor($R,$G,$B) + { + $R=intval($R); $G=intval($G); $B=intval($B); + $R=dechex($R<0?0:($R>255?255:$R)); $G=dechex($G<0?0:($G>255?255:$G));$B=dechex($B<0?0:($B>255?255:$B)); + $Color="#".(strlen($R) < 2?'0':'').$R; $Color.=(strlen($G) < 2?'0':'').$G; $Color.= (strlen($B) < 2?'0':'').$B; + return($Color); + } + + /* Reverse an array of points */ + function reversePlots($Plots) + { + $Result = ""; + for($i=count($Plots)-2;$i>=0;$i=$i-2) { $Result[] = $Plots[$i]; $Result[] = $Plots[$i+1]; } + return($Result); + } + + /* Mirror Effect */ + function drawAreaMirror($X,$Y,$Width,$Height,$Format="") + { + $StartAlpha = isset($Format["StartAlpha"]) ? $Format["StartAlpha"] : 80; + $EndAlpha = isset($Format["EndAlpha"]) ? $Format["EndAlpha"] : 0; + + $AlphaStep = ($StartAlpha-$EndAlpha)/$Height; + + $Picture = imagecreatetruecolor($this->XSize,$this->YSize); + imagecopy($Picture,$this->Picture,0,0,0,0,$this->XSize,$this->YSize); + + for($i=1;$i<=$Height;$i++) + { + if ( $Y+($i-1) < $this->YSize && $Y-$i > 0 ) { imagecopymerge($Picture,$this->Picture,$X,$Y+($i-1),$X,$Y-$i,$Width,1,$StartAlpha-$AlphaStep*$i); } + } + + imagecopy($this->Picture,$Picture,0,0,0,0,$this->XSize,$this->YSize); + } + } ?> \ No newline at end of file diff --git a/www/libs/pChart2.1.3/class/pIndicator.class.php b/www/libs/pChart2.1.4/class/pIndicator.class.php similarity index 97% rename from www/libs/pChart2.1.3/class/pIndicator.class.php rename to www/libs/pChart2.1.4/class/pIndicator.class.php index 1e4c7c3c..1040af8f 100644 --- a/www/libs/pChart2.1.3/class/pIndicator.class.php +++ b/www/libs/pChart2.1.4/class/pIndicator.class.php @@ -1,241 +1,241 @@ -pChartObject = $pChartObject; - } - - /* Draw an indicator */ - function draw($X,$Y,$Width,$Height,$Format="") - { - $Values = isset($Format["Values"]) ? $Format["Values"] : VOID; - $IndicatorSections = isset($Format["IndicatorSections"]) ? $Format["IndicatorSections"] : NULL; - $ValueDisplay = isset($Format["ValueDisplay"]) ? $Format["ValueDisplay"] : INDICATOR_VALUE_BUBBLE; - $SectionsMargin = isset($Format["SectionsMargin"]) ? $Format["SectionsMargin"] : 4; - $DrawLeftHead = isset($Format["DrawLeftHead"]) ? $Format["DrawLeftHead"] : TRUE; - $DrawRightHead = isset($Format["DrawRightHead"]) ? $Format["DrawRightHead"] : TRUE; - $HeadSize = isset($Format["HeadSize"]) ? $Format["HeadSize"] : floor($Height/4); - $TextPadding = isset($Format["TextPadding"]) ? $Format["TextPadding"] : 4; - $CaptionLayout = isset($Format["CaptionLayout"]) ? $Format["CaptionLayout"] : INDICATOR_CAPTION_EXTENDED; - $CaptionPosition = isset($Format["CaptionPosition"]) ? $Format["CaptionPosition"] : INDICATOR_CAPTION_INSIDE; - $CaptionColorFactor = isset($Format["CaptionColorFactor"]) ? $Format["CaptionColorFactor"] : NULL; - $CaptionR = isset($Format["CaptionR"]) ? $Format["CaptionR"] : 255; - $CaptionG = isset($Format["CaptionG"]) ? $Format["CaptionG"] : 255; - $CaptionB = isset($Format["CaptionB"]) ? $Format["CaptionB"] : 255; - $CaptionAlpha = isset($Format["CaptionAlpha"]) ? $Format["CaptionAlpha"] : 100; - $SubCaptionColorFactor = isset($Format["SubCaptionColorFactor"]) ? $Format["SubCaptionColorFactor"] : NULL; - $SubCaptionR = isset($Format["SubCaptionR"]) ? $Format["SubCaptionR"] : 50; - $SubCaptionG = isset($Format["SubCaptionG"]) ? $Format["SubCaptionG"] : 50; - $SubCaptionB = isset($Format["SubCaptionB"]) ? $Format["SubCaptionB"] : 50; - $SubCaptionAlpha = isset($Format["SubCaptionAlpha"]) ? $Format["SubCaptionAlpha"] : 100; - $ValueFontName = isset($Format["ValueFontName"]) ? $Format["ValueFontName"] : $this->pChartObject->FontName; - $ValueFontSize = isset($Format["ValueFontSize"]) ? $Format["ValueFontSize"] : $this->pChartObject->FontSize; - $CaptionFontName = isset($Format["CaptionFontName"]) ? $Format["CaptionFontName"] : $this->pChartObject->FontName; - $CaptionFontSize = isset($Format["CaptionFontSize"]) ? $Format["CaptionFontSize"] : $this->pChartObject->FontSize; - $Unit = isset($Format["Unit"]) ? $Format["Unit"] : ""; - - /* Convert the Values to display to an array if needed */ - if ( !is_array($Values) ) { $Value = $Values; $Values = ""; $Values[] = $Value; } - - /* No section, let's die */ - if ( $IndicatorSections == NULL ) { return(0); } - - /* Determine indicator visual configuration */ - $OverallMin = $IndicatorSections[0]["End"]; $OverallMax = $IndicatorSections[0]["Start"]; - foreach ($IndicatorSections as $Key => $Settings) - { - if ( $Settings["End"] > $OverallMax ) { $OverallMax = $Settings["End"]; } - if ( $Settings["Start"] < $OverallMin ) { $OverallMin = $Settings["Start"]; } - } - $RealWidth = $Width - (count($IndicatorSections)-1)*$SectionsMargin; - $XScale = $RealWidth / ($OverallMax-$OverallMin); - - $X1 = $X; $ValuesPos = ""; - foreach ($IndicatorSections as $Key => $Settings) - { - $Color = array("R"=>$Settings["R"],"G"=>$Settings["G"],"B"=>$Settings["B"]); - $Caption = $Settings["Caption"]; - $SubCaption = $Settings["Start"]." - ".$Settings["End"]; - - $X2 = $X1 + ($Settings["End"] - $Settings["Start"]) * $XScale; - - if ( $Key == 0 && $DrawLeftHead ) - { - $Poly = ""; $Poly[] = $X1-1; $Poly[] = $Y; $Poly[] = $X1-1; $Poly[] = $Y+$Height; $Poly[] = $X1-1-$HeadSize; $Poly[] = $Y+($Height/2); - $this->pChartObject->drawPolygon($Poly,$Color); - $this->pChartObject->drawLine($X1-2,$Y,$X1-2-$HeadSize,$Y+($Height/2),$Color); - $this->pChartObject->drawLine($X1-2,$Y+$Height,$X1-2-$HeadSize,$Y+($Height/2),$Color); - } - - /* Determine the position of the breaks */ - $Break = ""; - foreach($Values as $iKey => $Value) - { - if ( $Value >= $Settings["Start"] && $Value <= $Settings["End"] ) - { - $XBreak = $X1 + ($Value - $Settings["Start"]) * $XScale; - $ValuesPos[$Value] = $XBreak; - $Break[] = floor($XBreak); - } - } - - if ( $ValueDisplay == INDICATOR_VALUE_LABEL ) - { - if ( $Break == "" ) - $this->pChartObject->drawFilledRectangle($X1,$Y,$X2,$Y+$Height,$Color); - else - { - sort($Break); - $Poly = ""; $Poly[] = $X1; $Poly[] = $Y; $LastPointWritten = FALSE; - foreach($Break as $iKey => $Value) - { - if ( $Value-5 >= $X1 ) - { $Poly[] = $Value-5; $Poly[] = $Y; } - elseif ($X1 - ($Value-5) > 0 ) - { - $Offset = $X1 - ($Value-5); - $Poly = ""; $Poly[] = $X1; $Poly[] = $Y + $Offset; - } - - $Poly[] = $Value; $Poly[] = $Y+5; - - if ( $Value+5 <= $X2 ) - { $Poly[] = $Value+5; $Poly[] = $Y; } - elseif (($Value+5) > $X2 ) - { - $Offset = ($Value+5) - $X2; - $Poly[] = $X2; $Poly[] = $Y + $Offset; - $LastPointWritten = TRUE; - } - - } - if ( !$LastPointWritten ) { $Poly[] = $X2; $Poly[] = $Y; } - $Poly[] = $X2; $Poly[] = $Y+$Height; - $Poly[] = $X1; $Poly[] = $Y+$Height; - - $this->pChartObject->drawPolygon($Poly,$Color); - } - } - else - $this->pChartObject->drawFilledRectangle($X1,$Y,$X2,$Y+$Height,$Color); - - if ( $Key == count($IndicatorSections)-1 && $DrawRightHead ) - { - $Poly = ""; $Poly[] = $X2+1; $Poly[] = $Y; $Poly[] = $X2+1; $Poly[] = $Y+$Height; $Poly[] = $X2+1+$HeadSize; $Poly[] = $Y+($Height/2); - $this->pChartObject->drawPolygon($Poly,$Color); - $this->pChartObject->drawLine($X2+1,$Y,$X2+1+$HeadSize,$Y+($Height/2),$Color); - $this->pChartObject->drawLine($X2+1,$Y+$Height,$X2+1+$HeadSize,$Y+($Height/2),$Color); - } - - if ( $CaptionPosition == INDICATOR_CAPTION_INSIDE ) - { - $TxtPos = $this->pChartObject->getTextBox($X1,$Y+$Height+$TextPadding,$CaptionFontName,$CaptionFontSize,0,$Caption); - $YOffset = ($TxtPos[0]["Y"] - $TxtPos[2]["Y"]) + $TextPadding; - - if ( $CaptionLayout == INDICATOR_CAPTION_EXTENDED ) - { - $TxtPos = $this->pChartObject->getTextBox($X1,$Y+$Height+$TextPadding,$CaptionFontName,$CaptionFontSize,0,$SubCaption); - $YOffset = $YOffset + ($TxtPos[0]["Y"] - $TxtPos[2]["Y"]) + $TextPadding*2; - } - - $XOffset = $TextPadding; - } - else - { $YOffset = 0; $XOffset = 0; } - - if ( $CaptionColorFactor == NULL ) - { $CaptionColor = array("Align"=>TEXT_ALIGN_TOPLEFT,"FontName"=>$CaptionFontName,"FontSize"=>$CaptionFontSize,"R"=>$CaptionR,"G"=>$CaptionG,"B"=>$CaptionB,"Alpha"=>$CaptionAlpha); } - else - { $CaptionColor = array("Align"=>TEXT_ALIGN_TOPLEFT,"FontName"=>$CaptionFontName,"FontSize"=>$CaptionFontSize,"R"=>$Settings["R"]+$CaptionColorFactor,"G"=>$Settings["G"]+$CaptionColorFactor,"B"=>$Settings["B"]+$CaptionColorFactor); } - - if ( $SubCaptionColorFactor == NULL ) - $SubCaptionColor = array("Align"=>TEXT_ALIGN_TOPLEFT,"FontName"=>$CaptionFontName,"FontSize"=>$CaptionFontSize,"R"=>$SubCaptionR,"G"=>$SubCaptionG,"B"=>$SubCaptionB,"Alpha"=>$SubCaptionAlpha); - else - $SubCaptionColor = array("Align"=>TEXT_ALIGN_TOPLEFT,"FontName"=>$CaptionFontName,"FontSize"=>$CaptionFontSize,"R"=>$Settings["R"]+$SubCaptionColorFactor,"G"=>$Settings["G"]+$SubCaptionColorFactor,"B"=>$Settings["B"]+$SubCaptionColorFactor); - - $RestoreShadow = $this->pChartObject->Shadow; - $this->pChartObject->Shadow = FALSE; - - if ( $CaptionLayout == INDICATOR_CAPTION_DEFAULT ) - $this->pChartObject->drawText($X1,$Y+$Height+$TextPadding,$Caption,$CaptionColor); - elseif ( $CaptionLayout == INDICATOR_CAPTION_EXTENDED ) - { - $TxtPos = $this->pChartObject->getTextBox($X1,$Y+$Height+$TextPadding,$CaptionFontName,$CaptionFontSize,0,$Caption); - $CaptionHeight = $TxtPos[0]["Y"] - $TxtPos[2]["Y"]; - - $this->pChartObject->drawText($X1+$XOffset,$Y+$Height-$YOffset+$TextPadding,$Caption,$CaptionColor); - $this->pChartObject->drawText($X1+$XOffset,$Y+$Height-$YOffset+$CaptionHeight+$TextPadding*2,$SubCaption,$SubCaptionColor); - } - - $this->pChartObject->Shadow = $RestoreShadow; - - $X1 = $X2 + $SectionsMargin; - } - - $RestoreShadow = $this->pChartObject->Shadow; - $this->pChartObject->Shadow = FALSE; - - foreach($Values as $Key => $Value) - { - if ( $Value >= $OverallMin && $Value <= $OverallMax ) - { - foreach ($IndicatorSections as $Key => $Settings) - { - if ( $Value >= $Settings["Start"] && $Value <= $Settings["End"] ) - { - $X1 = $ValuesPos[$Value]; //$X + $Key*$SectionsMargin + ($Value - $OverallMin) * $XScale; - - if ( $ValueDisplay == INDICATOR_VALUE_BUBBLE ) - { - $TxtPos = $this->pChartObject->getTextBox($X1,$Y,$ValueFontName,$ValueFontSize,0,$Value.$Unit); - $Radius = floor(($TxtPos[1]["X"] - $TxtPos[0]["X"] + $TextPadding*4)/2); - - $this->pChartObject->drawFilledCircle($X1,$Y,$Radius+4,array("R"=>$Settings["R"]+20,"G"=>$Settings["G"]+20,"B"=>$Settings["B"]+20)); - $this->pChartObject->drawFilledCircle($X1,$Y,$Radius,array("R"=>255,"G"=>255,"B"=>255)); - - $TextSettings = array("Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontName"=>$ValueFontName,"FontSize"=>$ValueFontSize); - $this->pChartObject->drawText($X1-1,$Y-1,$Value.$Unit,$TextSettings); - } - elseif( $ValueDisplay == INDICATOR_VALUE_LABEL ) - { - $Caption = ""; - $Caption[] = array("Format"=>array("R"=>$Settings["R"],"G"=>$Settings["G"],"B"=>$Settings["B"],"Alpha"=>100),"Caption"=>$Value.$Unit); - $this->pChartObject->drawLabelBox(floor($X1),floor($Y)+2,"Value - ".$Settings["Caption"],$Caption); - } - } - $X1 = $X2 + $SectionsMargin; - } - } - } - $this->pChartObject->Shadow = $RestoreShadow; - } - } +pChartObject = $pChartObject; + } + + /* Draw an indicator */ + function draw($X,$Y,$Width,$Height,$Format="") + { + $Values = isset($Format["Values"]) ? $Format["Values"] : VOID; + $IndicatorSections = isset($Format["IndicatorSections"]) ? $Format["IndicatorSections"] : NULL; + $ValueDisplay = isset($Format["ValueDisplay"]) ? $Format["ValueDisplay"] : INDICATOR_VALUE_BUBBLE; + $SectionsMargin = isset($Format["SectionsMargin"]) ? $Format["SectionsMargin"] : 4; + $DrawLeftHead = isset($Format["DrawLeftHead"]) ? $Format["DrawLeftHead"] : TRUE; + $DrawRightHead = isset($Format["DrawRightHead"]) ? $Format["DrawRightHead"] : TRUE; + $HeadSize = isset($Format["HeadSize"]) ? $Format["HeadSize"] : floor($Height/4); + $TextPadding = isset($Format["TextPadding"]) ? $Format["TextPadding"] : 4; + $CaptionLayout = isset($Format["CaptionLayout"]) ? $Format["CaptionLayout"] : INDICATOR_CAPTION_EXTENDED; + $CaptionPosition = isset($Format["CaptionPosition"]) ? $Format["CaptionPosition"] : INDICATOR_CAPTION_INSIDE; + $CaptionColorFactor = isset($Format["CaptionColorFactor"]) ? $Format["CaptionColorFactor"] : NULL; + $CaptionR = isset($Format["CaptionR"]) ? $Format["CaptionR"] : 255; + $CaptionG = isset($Format["CaptionG"]) ? $Format["CaptionG"] : 255; + $CaptionB = isset($Format["CaptionB"]) ? $Format["CaptionB"] : 255; + $CaptionAlpha = isset($Format["CaptionAlpha"]) ? $Format["CaptionAlpha"] : 100; + $SubCaptionColorFactor = isset($Format["SubCaptionColorFactor"]) ? $Format["SubCaptionColorFactor"] : NULL; + $SubCaptionR = isset($Format["SubCaptionR"]) ? $Format["SubCaptionR"] : 50; + $SubCaptionG = isset($Format["SubCaptionG"]) ? $Format["SubCaptionG"] : 50; + $SubCaptionB = isset($Format["SubCaptionB"]) ? $Format["SubCaptionB"] : 50; + $SubCaptionAlpha = isset($Format["SubCaptionAlpha"]) ? $Format["SubCaptionAlpha"] : 100; + $ValueFontName = isset($Format["ValueFontName"]) ? $Format["ValueFontName"] : $this->pChartObject->FontName; + $ValueFontSize = isset($Format["ValueFontSize"]) ? $Format["ValueFontSize"] : $this->pChartObject->FontSize; + $CaptionFontName = isset($Format["CaptionFontName"]) ? $Format["CaptionFontName"] : $this->pChartObject->FontName; + $CaptionFontSize = isset($Format["CaptionFontSize"]) ? $Format["CaptionFontSize"] : $this->pChartObject->FontSize; + $Unit = isset($Format["Unit"]) ? $Format["Unit"] : ""; + + /* Convert the Values to display to an array if needed */ + if ( !is_array($Values) ) { $Value = $Values; $Values = ""; $Values[] = $Value; } + + /* No section, let's die */ + if ( $IndicatorSections == NULL ) { return(0); } + + /* Determine indicator visual configuration */ + $OverallMin = $IndicatorSections[0]["End"]; $OverallMax = $IndicatorSections[0]["Start"]; + foreach ($IndicatorSections as $Key => $Settings) + { + if ( $Settings["End"] > $OverallMax ) { $OverallMax = $Settings["End"]; } + if ( $Settings["Start"] < $OverallMin ) { $OverallMin = $Settings["Start"]; } + } + $RealWidth = $Width - (count($IndicatorSections)-1)*$SectionsMargin; + $XScale = $RealWidth / ($OverallMax-$OverallMin); + + $X1 = $X; $ValuesPos = ""; + foreach ($IndicatorSections as $Key => $Settings) + { + $Color = array("R"=>$Settings["R"],"G"=>$Settings["G"],"B"=>$Settings["B"]); + $Caption = $Settings["Caption"]; + $SubCaption = $Settings["Start"]." - ".$Settings["End"]; + + $X2 = $X1 + ($Settings["End"] - $Settings["Start"]) * $XScale; + + if ( $Key == 0 && $DrawLeftHead ) + { + $Poly = ""; $Poly[] = $X1-1; $Poly[] = $Y; $Poly[] = $X1-1; $Poly[] = $Y+$Height; $Poly[] = $X1-1-$HeadSize; $Poly[] = $Y+($Height/2); + $this->pChartObject->drawPolygon($Poly,$Color); + $this->pChartObject->drawLine($X1-2,$Y,$X1-2-$HeadSize,$Y+($Height/2),$Color); + $this->pChartObject->drawLine($X1-2,$Y+$Height,$X1-2-$HeadSize,$Y+($Height/2),$Color); + } + + /* Determine the position of the breaks */ + $Break = ""; + foreach($Values as $iKey => $Value) + { + if ( $Value >= $Settings["Start"] && $Value <= $Settings["End"] ) + { + $XBreak = $X1 + ($Value - $Settings["Start"]) * $XScale; + $ValuesPos[$Value] = $XBreak; + $Break[] = floor($XBreak); + } + } + + if ( $ValueDisplay == INDICATOR_VALUE_LABEL ) + { + if ( $Break == "" ) + $this->pChartObject->drawFilledRectangle($X1,$Y,$X2,$Y+$Height,$Color); + else + { + sort($Break); + $Poly = ""; $Poly[] = $X1; $Poly[] = $Y; $LastPointWritten = FALSE; + foreach($Break as $iKey => $Value) + { + if ( $Value-5 >= $X1 ) + { $Poly[] = $Value-5; $Poly[] = $Y; } + elseif ($X1 - ($Value-5) > 0 ) + { + $Offset = $X1 - ($Value-5); + $Poly = ""; $Poly[] = $X1; $Poly[] = $Y + $Offset; + } + + $Poly[] = $Value; $Poly[] = $Y+5; + + if ( $Value+5 <= $X2 ) + { $Poly[] = $Value+5; $Poly[] = $Y; } + elseif (($Value+5) > $X2 ) + { + $Offset = ($Value+5) - $X2; + $Poly[] = $X2; $Poly[] = $Y + $Offset; + $LastPointWritten = TRUE; + } + + } + if ( !$LastPointWritten ) { $Poly[] = $X2; $Poly[] = $Y; } + $Poly[] = $X2; $Poly[] = $Y+$Height; + $Poly[] = $X1; $Poly[] = $Y+$Height; + + $this->pChartObject->drawPolygon($Poly,$Color); + } + } + else + $this->pChartObject->drawFilledRectangle($X1,$Y,$X2,$Y+$Height,$Color); + + if ( $Key == count($IndicatorSections)-1 && $DrawRightHead ) + { + $Poly = ""; $Poly[] = $X2+1; $Poly[] = $Y; $Poly[] = $X2+1; $Poly[] = $Y+$Height; $Poly[] = $X2+1+$HeadSize; $Poly[] = $Y+($Height/2); + $this->pChartObject->drawPolygon($Poly,$Color); + $this->pChartObject->drawLine($X2+1,$Y,$X2+1+$HeadSize,$Y+($Height/2),$Color); + $this->pChartObject->drawLine($X2+1,$Y+$Height,$X2+1+$HeadSize,$Y+($Height/2),$Color); + } + + if ( $CaptionPosition == INDICATOR_CAPTION_INSIDE ) + { + $TxtPos = $this->pChartObject->getTextBox($X1,$Y+$Height+$TextPadding,$CaptionFontName,$CaptionFontSize,0,$Caption); + $YOffset = ($TxtPos[0]["Y"] - $TxtPos[2]["Y"]) + $TextPadding; + + if ( $CaptionLayout == INDICATOR_CAPTION_EXTENDED ) + { + $TxtPos = $this->pChartObject->getTextBox($X1,$Y+$Height+$TextPadding,$CaptionFontName,$CaptionFontSize,0,$SubCaption); + $YOffset = $YOffset + ($TxtPos[0]["Y"] - $TxtPos[2]["Y"]) + $TextPadding*2; + } + + $XOffset = $TextPadding; + } + else + { $YOffset = 0; $XOffset = 0; } + + if ( $CaptionColorFactor == NULL ) + { $CaptionColor = array("Align"=>TEXT_ALIGN_TOPLEFT,"FontName"=>$CaptionFontName,"FontSize"=>$CaptionFontSize,"R"=>$CaptionR,"G"=>$CaptionG,"B"=>$CaptionB,"Alpha"=>$CaptionAlpha); } + else + { $CaptionColor = array("Align"=>TEXT_ALIGN_TOPLEFT,"FontName"=>$CaptionFontName,"FontSize"=>$CaptionFontSize,"R"=>$Settings["R"]+$CaptionColorFactor,"G"=>$Settings["G"]+$CaptionColorFactor,"B"=>$Settings["B"]+$CaptionColorFactor); } + + if ( $SubCaptionColorFactor == NULL ) + $SubCaptionColor = array("Align"=>TEXT_ALIGN_TOPLEFT,"FontName"=>$CaptionFontName,"FontSize"=>$CaptionFontSize,"R"=>$SubCaptionR,"G"=>$SubCaptionG,"B"=>$SubCaptionB,"Alpha"=>$SubCaptionAlpha); + else + $SubCaptionColor = array("Align"=>TEXT_ALIGN_TOPLEFT,"FontName"=>$CaptionFontName,"FontSize"=>$CaptionFontSize,"R"=>$Settings["R"]+$SubCaptionColorFactor,"G"=>$Settings["G"]+$SubCaptionColorFactor,"B"=>$Settings["B"]+$SubCaptionColorFactor); + + $RestoreShadow = $this->pChartObject->Shadow; + $this->pChartObject->Shadow = FALSE; + + if ( $CaptionLayout == INDICATOR_CAPTION_DEFAULT ) + $this->pChartObject->drawText($X1,$Y+$Height+$TextPadding,$Caption,$CaptionColor); + elseif ( $CaptionLayout == INDICATOR_CAPTION_EXTENDED ) + { + $TxtPos = $this->pChartObject->getTextBox($X1,$Y+$Height+$TextPadding,$CaptionFontName,$CaptionFontSize,0,$Caption); + $CaptionHeight = $TxtPos[0]["Y"] - $TxtPos[2]["Y"]; + + $this->pChartObject->drawText($X1+$XOffset,$Y+$Height-$YOffset+$TextPadding,$Caption,$CaptionColor); + $this->pChartObject->drawText($X1+$XOffset,$Y+$Height-$YOffset+$CaptionHeight+$TextPadding*2,$SubCaption,$SubCaptionColor); + } + + $this->pChartObject->Shadow = $RestoreShadow; + + $X1 = $X2 + $SectionsMargin; + } + + $RestoreShadow = $this->pChartObject->Shadow; + $this->pChartObject->Shadow = FALSE; + + foreach($Values as $Key => $Value) + { + if ( $Value >= $OverallMin && $Value <= $OverallMax ) + { + foreach ($IndicatorSections as $Key => $Settings) + { + if ( $Value >= $Settings["Start"] && $Value <= $Settings["End"] ) + { + $X1 = $ValuesPos[$Value]; //$X + $Key*$SectionsMargin + ($Value - $OverallMin) * $XScale; + + if ( $ValueDisplay == INDICATOR_VALUE_BUBBLE ) + { + $TxtPos = $this->pChartObject->getTextBox($X1,$Y,$ValueFontName,$ValueFontSize,0,$Value.$Unit); + $Radius = floor(($TxtPos[1]["X"] - $TxtPos[0]["X"] + $TextPadding*4)/2); + + $this->pChartObject->drawFilledCircle($X1,$Y,$Radius+4,array("R"=>$Settings["R"]+20,"G"=>$Settings["G"]+20,"B"=>$Settings["B"]+20)); + $this->pChartObject->drawFilledCircle($X1,$Y,$Radius,array("R"=>255,"G"=>255,"B"=>255)); + + $TextSettings = array("Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontName"=>$ValueFontName,"FontSize"=>$ValueFontSize); + $this->pChartObject->drawText($X1-1,$Y-1,$Value.$Unit,$TextSettings); + } + elseif( $ValueDisplay == INDICATOR_VALUE_LABEL ) + { + $Caption = ""; + $Caption[] = array("Format"=>array("R"=>$Settings["R"],"G"=>$Settings["G"],"B"=>$Settings["B"],"Alpha"=>100),"Caption"=>$Value.$Unit); + $this->pChartObject->drawLabelBox(floor($X1),floor($Y)+2,"Value - ".$Settings["Caption"],$Caption); + } + } + $X1 = $X2 + $SectionsMargin; + } + } + } + $this->pChartObject->Shadow = $RestoreShadow; + } + } ?> \ No newline at end of file diff --git a/www/libs/pChart2.1.3/class/pPie.class.php b/www/libs/pChart2.1.4/class/pPie.class.php similarity index 97% rename from www/libs/pChart2.1.3/class/pPie.class.php rename to www/libs/pChart2.1.4/class/pPie.class.php index 06db3cf2..209b7497 100644 --- a/www/libs/pChart2.1.3/class/pPie.class.php +++ b/www/libs/pChart2.1.4/class/pPie.class.php @@ -1,1500 +1,1500 @@ -pChartObject = $Object; - - /* Cache the pData object reference */ - $this->pDataObject = $pDataObject; - } - - /* Draw a pie chart */ - function draw2DPie($X,$Y,$Format="") - { - $Radius = isset($Format["Radius"]) ? $Format["Radius"] : 60; - $Precision = isset($Format["Precision"]) ? $Format["Precision"] : 0; - $DataGapAngle = isset($Format["DataGapAngle"]) ? $Format["DataGapAngle"] : 0; - $DataGapRadius = isset($Format["DataGapRadius"]) ? $Format["DataGapRadius"] : 0; - $SecondPass = isset($Format["SecondPass"]) ? $Format["SecondPass"] : TRUE; - $Border = isset($Format["Border"]) ? $Format["Border"] : FALSE; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255; - $Shadow = isset($Format["Shadow"]) ? $Format["Shadow"] : FALSE; - $DrawLabels = isset($Format["DrawLabels"]) ? $Format["DrawLabels"] : FALSE; - $LabelStacked = isset($Format["LabelStacked"]) ? $Format["LabelStacked"] : FALSE; - $LabelColor = isset($Format["LabelColor"]) ? $Format["LabelColor"] : PIE_LABEL_COLOR_MANUAL; - $LabelR = isset($Format["LabelR"]) ? $Format["LabelR"] : 0; - $LabelG = isset($Format["LabelG"]) ? $Format["LabelG"] : 0; - $LabelB = isset($Format["LabelB"]) ? $Format["LabelB"] : 0; - $LabelAlpha = isset($Format["LabelAlpha"]) ? $Format["LabelAlpha"] : 100; - $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : NULL; - $ValuePosition = isset($Format["ValuePosition"]) ? $Format["ValuePosition"] : PIE_VALUE_OUTSIDE; - $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 15; - $ValueSuffix = isset($Format["ValueSuffix"]) ? $Format["ValueSuffix"] : ""; - $ValueR = isset($Format["ValueR"]) ? $Format["ValueR"] : 255; - $ValueG = isset($Format["ValueG"]) ? $Format["ValueG"] : 255; - $ValueB = isset($Format["ValueB"]) ? $Format["ValueB"] : 255; - $ValueAlpha = isset($Format["ValueAlpha"]) ? $Format["ValueAlpha"] : 100; - $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; - - /* Data Processing */ - $Data = $this->pDataObject->getData(); - $Palette = $this->pDataObject->getPalette(); - - /* Do we have an abscissa serie defined? */ - if ( $Data["Abscissa"] == "" ) { return(PIE_NO_ABSCISSA); } - - /* Try to find the data serie */ - $DataSerie = ""; - foreach ($Data["Series"] as $SerieName => $SerieData) - { if ( $SerieName != $Data["Abscissa"]) { $DataSerie = $SerieName; } } - - /* Do we have data to compute? */ - if ( $DataSerie == "" ) { return(PIE_NO_DATASERIE); } - - /* Remove unused data */ - list($Data,$Palette) = $this->clean0Values($Data,$Palette,$DataSerie,$Data["Abscissa"]); - - /* Compute the pie sum */ - $SerieSum = $this->pDataObject->getSum($DataSerie); - - /* Do we have data to draw? */ - if ( $SerieSum == 0 ) { return(PIE_SUMISNULL); } - - /* Dump the real number of data to draw */ - $Values = ""; - foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) - { if ($Value != 0) { $Values[] = $Value; } } - - /* Compute the wasted angular space between series */ - if (count($Values)==1) { $WastedAngular = 0; } else { $WastedAngular = count($Values) * $DataGapAngle; } - - /* Compute the scale */ - $ScaleFactor = (360 - $WastedAngular) / $SerieSum; - - $RestoreShadow = $this->pChartObject->Shadow; - if ( $this->pChartObject->Shadow ) - { - $this->pChartObject->Shadow = FALSE; - - $ShadowFormat = $Format; $ShadowFormat["Shadow"] = TRUE; - $this->draw2DPie($X+$this->pChartObject->ShadowX,$Y+$this->pChartObject->ShadowY,$ShadowFormat); - } - - /* Draw the polygon pie elements */ - $Step = 360 / (2 * PI * $Radius); - $Offset = 0; $ID = 0; - foreach($Values as $Key => $Value) - { - if ( $Shadow ) - $Settings = array("R"=>$this->pChartObject->ShadowR,"G"=>$this->pChartObject->ShadowG,"B"=>$this->pChartObject->ShadowB,"Alpha"=>$this->pChartObject->Shadowa); - else - { - if ( !isset($Palette[$ID]["R"]) ) { $Color = $this->pChartObject->getRandomColor(); $Palette[$ID] = $Color; $this->pDataObject->savePalette($ID,$Color); } - $Settings = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]); - } - - if ( !$SecondPass && !$Shadow ) - { - if ( !$Border ) - $Settings["Surrounding"] = 10; - else - { $Settings["BorderR"] = $BorderR; $Settings["BorderG"] = $BorderG; $Settings["BorderB"] = $BorderB; } - } - - $Plots = ""; - $EndAngle = $Offset+($Value*$ScaleFactor); if ( $EndAngle > 360 ) { $EndAngle = 360; } - - $Angle = ($EndAngle - $Offset)/2 + $Offset; - if ($DataGapAngle == 0) - { $X0 = $X; $Y0 = $Y; } - else - { - $X0 = cos(($Angle-90)*PI/180) * $DataGapRadius + $X; - $Y0 = sin(($Angle-90)*PI/180) * $DataGapRadius + $Y; - } - - $Plots[] = $X0; $Plots[] = $Y0; - - - for($i=$Offset;$i<=$EndAngle;$i=$i+$Step) - { - $Xc = cos(($i-90)*PI/180) * $Radius + $X; - $Yc = sin(($i-90)*PI/180) * $Radius + $Y; - - if ( $SecondPass && ( $i<90 )) { $Yc++; } - if ( $SecondPass && ( $i>180 && $i<270 )) { $Xc++; } - if ( $SecondPass && ( $i>=270 )) { $Xc++; $Yc++; } - - $Plots[] = $Xc; $Plots[] = $Yc; - } - - $this->pChartObject->drawPolygon($Plots,$Settings); - if ( $RecordImageMap && !$Shadow ) { $this->pChartObject->addToImageMap("POLY",$this->arraySerialize($Plots),$this->pChartObject->toHTMLColor($Palette[$ID]["R"],$Palette[$ID]["G"],$Palette[$ID]["B"]),$Data["Series"][$Data["Abscissa"]]["Data"][$Key],$Value); } - - if ( $DrawLabels && !$Shadow && !$SecondPass ) - { - if ( $LabelColor == PIE_LABEL_COLOR_AUTO ) - { $Settings = array("FillR"=>$Palette[$ID]["R"],"FillG"=>$Palette[$ID]["G"],"FillB"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]);} - else - { $Settings = array("FillR"=>$LabelR,"FillG"=>$LabelG,"FillB"=>$LabelB,"Alpha"=>$LabelAlpha); } - - $Angle = ($EndAngle - $Offset)/2 + $Offset; - $Xc = cos(($Angle-90)*PI/180) * $Radius + $X; - $Yc = sin(($Angle-90)*PI/180) * $Radius + $Y; - - $Label = $Data["Series"][$Data["Abscissa"]]["Data"][$Key]; - - if ( $LabelStacked ) - $this->writePieLabel($Xc,$Yc,$Label,$Angle,$Settings,TRUE,$X,$Y,$Radius); - else - $this->writePieLabel($Xc,$Yc,$Label,$Angle,$Settings,FALSE); - } - - $Offset = $i + $DataGapAngle; $ID++; - } - - /* Second pass to smooth the angles */ - if ( $SecondPass ) - { - $Step = 360 / (2 * PI * $Radius); - $Offset = 0; $ID = 0; - foreach($Values as $Key => $Value) - { - $FirstPoint = TRUE; - if ( $Shadow ) - $Settings = array("R"=>$this->pChartObject->ShadowR,"G"=>$this->pChartObject->ShadowG,"B"=>$this->pChartObject->ShadowB,"Alpha"=>$this->pChartObject->Shadowa); - else - { - if ( $Border ) - $Settings = array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB); - else - $Settings = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]); - } - - $EndAngle = $Offset+($Value*$ScaleFactor); if ( $EndAngle > 360 ) { $EndAngle = 360; } - - if ($DataGapAngle == 0) - { $X0 = $X; $Y0 = $Y; } - else - { - $Angle = ($EndAngle - $Offset)/2 + $Offset; - $X0 = cos(($Angle-90)*PI/180) * $DataGapRadius + $X; - $Y0 = sin(($Angle-90)*PI/180) * $DataGapRadius + $Y; - } - $Plots[] = $X0; $Plots[] = $Y0; - - for($i=$Offset;$i<=$EndAngle;$i=$i+$Step) - { - $Xc = cos(($i-90)*PI/180) * $Radius + $X; - $Yc = sin(($i-90)*PI/180) * $Radius + $Y; - - if ( $FirstPoint ) { $this->pChartObject->drawLine($Xc,$Yc,$X0,$Y0,$Settings); } { $FirstPoint = FALSE; } - - $this->pChartObject->drawAntialiasPixel($Xc,$Yc,$Settings); - } - $this->pChartObject->drawLine($Xc,$Yc,$X0,$Y0,$Settings); - - if ( $DrawLabels && !$Shadow ) - { - if ( $LabelColor == PIE_LABEL_COLOR_AUTO ) - { $Settings = array("FillR"=>$Palette[$ID]["R"],"FillG"=>$Palette[$ID]["G"],"FillB"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]);} - else - { $Settings = array("FillR"=>$LabelR,"FillG"=>$LabelG,"FillB"=>$LabelB,"Alpha"=>$LabelAlpha); } - - $Angle = ($EndAngle - $Offset)/2 + $Offset; - $Xc = cos(($Angle-90)*PI/180) * $Radius + $X; - $Yc = sin(($Angle-90)*PI/180) * $Radius + $Y; - - $Label = $Data["Series"][$Data["Abscissa"]]["Data"][$Key]; - - if ( $LabelStacked ) - $this->writePieLabel($Xc,$Yc,$Label,$Angle,$Settings,TRUE,$X,$Y,$Radius); - else - $this->writePieLabel($Xc,$Yc,$Label,$Angle,$Settings,FALSE); - } - - $Offset = $i + $DataGapAngle; $ID++; - } - } - - if ( $WriteValues != NULL && !$Shadow ) - { - $Step = 360 / (2 * PI * $Radius); - $Offset = 0; $ID = count($Values)-1; - $Settings = array("Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"R"=>$ValueR,"G"=>$ValueG,"B"=>$ValueB,"Alpha"=>$ValueAlpha); - foreach($Values as $Key => $Value) - { - $EndAngle = ($Value*$ScaleFactor) + $Offset; if ( $EndAngle > 360 ) { $EndAngle = 0; } - $Angle = ($EndAngle - $Offset)/2 + $Offset; - - if ( $ValuePosition == PIE_VALUE_OUTSIDE ) - { - $Xc = cos(($Angle-90)*PI/180) * ($Radius+$ValuePadding) + $X; - $Yc = sin(($Angle-90)*PI/180) * ($Radius+$ValuePadding) + $Y; - } - else - { - $Xc = cos(($Angle-90)*PI/180) * ($Radius)/2 + $X; - $Yc = sin(($Angle-90)*PI/180) * ($Radius)/2 + $Y; - } - - if ( $WriteValues == PIE_VALUE_PERCENTAGE ) - $Display = round(( 100 / $SerieSum ) * $Value,$Precision)."%"; - elseif ( $WriteValues == PIE_VALUE_NATURAL ) - $Display = $Value.$ValueSuffix; - - $this->pChartObject->drawText($Xc,$Yc,$Display,$Settings); - - $Offset = $EndAngle + $DataGapAngle; $ID--; - } - } - - if ( $DrawLabels && $LabelStacked ) { $this->writeShiftedLabels(); } - - $this->pChartObject->Shadow = $RestoreShadow; - - return(PIE_RENDERED); - } - - /* Draw a 3D pie chart */ - function draw3DPie($X,$Y,$Format="") - { - /* Rendering layout */ - $Radius = isset($Format["Radius"]) ? $Format["Radius"] : 80; - $Precision = isset($Format["Precision"]) ? $Format["Precision"] : 0; - $SkewFactor = isset($Format["SkewFactor"]) ? $Format["SkewFactor"] : .5; - $SliceHeight = isset($Format["SliceHeight"]) ? $Format["SliceHeight"] : 20; - $DataGapAngle = isset($Format["DataGapAngle"]) ? $Format["DataGapAngle"] : 0; - $DataGapRadius = isset($Format["DataGapRadius"]) ? $Format["DataGapRadius"] : 0; - $SecondPass = isset($Format["SecondPass"]) ? $Format["SecondPass"] : TRUE; - $Border = isset($Format["Border"]) ? $Format["Border"] : FALSE; - $Shadow = isset($Format["Shadow"]) ? $Format["Shadow"] : FALSE; - $DrawLabels = isset($Format["DrawLabels"]) ? $Format["DrawLabels"] : FALSE; - $LabelStacked = isset($Format["LabelStacked"]) ? $Format["LabelStacked"] : FALSE; - $LabelColor = isset($Format["LabelColor"]) ? $Format["LabelColor"] : PIE_LABEL_COLOR_MANUAL; - $LabelR = isset($Format["LabelR"]) ? $Format["LabelR"] : 0; - $LabelG = isset($Format["LabelG"]) ? $Format["LabelG"] : 0; - $LabelB = isset($Format["LabelB"]) ? $Format["LabelB"] : 0; - $LabelAlpha = isset($Format["LabelAlpha"]) ? $Format["LabelAlpha"] : 100; - $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : NULL; //PIE_VALUE_PERCENTAGE - $ValuePosition = isset($Format["ValuePosition"]) ? $Format["ValuePosition"] : PIE_VALUE_INSIDE; - $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 15; - $ValueSuffix = isset($Format["ValueSuffix"]) ? $Format["ValueSuffix"] : ""; - $ValueR = isset($Format["ValueR"]) ? $Format["ValueR"] : 255; - $ValueG = isset($Format["ValueG"]) ? $Format["ValueG"] : 255; - $ValueB = isset($Format["ValueB"]) ? $Format["ValueB"] : 255; - $ValueAlpha = isset($Format["ValueAlpha"]) ? $Format["ValueAlpha"] : 100; - $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; - - /* Error correction for overlaying rounded corners */ - if ( $SkewFactor < .5 ) { $SkewFactor = .5; } - - /* Data Processing */ - $Data = $this->pDataObject->getData(); - $Palette = $this->pDataObject->getPalette(); - - /* Do we have an abscissa serie defined? */ - if ( $Data["Abscissa"] == "" ) { return(PIE_NO_ABSCISSA); } - - /* Try to find the data serie */ - $DataSerie = ""; - foreach ($Data["Series"] as $SerieName => $SerieData) - { if ( $SerieName != $Data["Abscissa"]) { $DataSerie = $SerieName; } } - - /* Do we have data to compute? */ - if ( $DataSerie == "" ) { return(PIE_NO_DATASERIE); } - - /* Remove unused data */ - list($Data,$Palette) = $this->clean0Values($Data,$Palette,$DataSerie,$Data["Abscissa"]); - - /* Compute the pie sum */ - $SerieSum = $this->pDataObject->getSum($DataSerie); - - /* Do we have data to draw? */ - if ( $SerieSum == 0 ) { return(PIE_SUMISNULL); } - - /* Dump the real number of data to draw */ - $Values = ""; - foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) - { if ($Value != 0) { $Values[] = $Value; } } - - /* Compute the wasted angular space between series */ - if (count($Values)==1) { $WastedAngular = 0; } else { $WastedAngular = count($Values) * $DataGapAngle; } - - /* Compute the scale */ - $ScaleFactor = (360 - $WastedAngular) / $SerieSum; - - $RestoreShadow = $this->pChartObject->Shadow; - if ( $this->pChartObject->Shadow ) { $this->pChartObject->Shadow = FALSE; } - - /* Draw the polygon pie elements */ - $Step = 360 / (2 * PI * $Radius); - $Offset = 360; $ID = count($Values)-1; - $Values = array_reverse($Values); - $Slice = 0; $Slices = ""; $SliceColors = ""; $Visible = ""; $SliceAngle = ""; - foreach($Values as $Key => $Value) - { - if ( !isset($Palette[$ID]["R"]) ) { $Color = $this->pChartObject->getRandomColor(); $Palette[$ID] = $Color; $this->pDataObject->savePalette($ID,$Color); } - $Settings = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]); - - $SliceColors[$Slice] = $Settings; - - $StartAngle = $Offset; - $EndAngle = $Offset-($Value*$ScaleFactor); if ( $EndAngle < 0 ) { $EndAngle = 0; } - - if ( $StartAngle > 180 ) { $Visible[$Slice]["Start"] = TRUE; } else { $Visible[$Slice]["Start"] = TRUE; } - if ( $EndAngle < 180 ) { $Visible[$Slice]["End"] = FALSE; } else { $Visible[$Slice]["End"] = TRUE; } - - if ($DataGapAngle == 0) - { $X0 = $X; $Y0 = $Y; } - else - { - $Angle = ($EndAngle - $Offset)/2 + $Offset; - $X0 = cos(($Angle-90)*PI/180) * $DataGapRadius + $X; - $Y0 = sin(($Angle-90)*PI/180) * $DataGapRadius*$SkewFactor + $Y; - } - $Slices[$Slice][] = $X0; $Slices[$Slice][] = $Y0; $SliceAngle[$Slice][] = 0; - - for($i=$Offset;$i>=$EndAngle;$i=$i-$Step) - { - $Xc = cos(($i-90)*PI/180) * $Radius + $X; - $Yc = sin(($i-90)*PI/180) * $Radius*$SkewFactor + $Y; - - if ( ($SecondPass || $RestoreShadow ) && ( $i<90 )) { $Yc++; } - if ( ($SecondPass || $RestoreShadow ) && ( $i>90 && $i<180 )) { $Xc++; } - if ( ($SecondPass || $RestoreShadow ) && ( $i>180 && $i<270 )) { $Xc++; } - if ( ($SecondPass || $RestoreShadow ) && ( $i>=270 )) { $Xc++; $Yc++; } - - $Slices[$Slice][] = $Xc; $Slices[$Slice][] = $Yc; $SliceAngle[$Slice][] = $i; - } - - $Offset = $i - $DataGapAngle; $ID--; $Slice++; - } - - /* Draw the bottom shadow if needed */ - if ( $RestoreShadow && ($this->pChartObject->ShadowX != 0 || $this->pChartObject->ShadowY !=0 )) - { - foreach($Slices as $SliceID => $Plots) - { - $ShadowPie = ""; - for($i=0;$ipChartObject->ShadowX; $ShadowPie[] = $Plots[$i+1]+$this->pChartObject->ShadowY; } - - $Settings = array("R"=>$this->pChartObject->ShadowR,"G"=>$this->pChartObject->ShadowG,"B"=>$this->pChartObject->ShadowB,"Alpha"=>$this->pChartObject->Shadowa,"NoBorder"=>TRUE); - $this->pChartObject->drawPolygon($ShadowPie,$Settings); - } - - $Step = 360 / (2 * PI * $Radius); - $Offset = 360; - foreach($Values as $Key => $Value) - { - $EndAngle = $Offset-($Value*$ScaleFactor); if ( $EndAngle < 0 ) { $EndAngle = 0; } - - for($i=$Offset;$i>=$EndAngle;$i=$i-$Step) - { - $Xc = cos(($i-90)*PI/180) * $Radius + $X + $this->pChartObject->ShadowX; - $Yc = sin(($i-90)*PI/180) * $Radius*$SkewFactor + $Y + $this->pChartObject->ShadowY; - - $this->pChartObject->drawAntialiasPixel($Xc,$Yc,$Settings); - } - - $Offset = $i - $DataGapAngle; $ID--; - } - } - - /* Draw the bottom pie splice */ - foreach($Slices as $SliceID => $Plots) - { - $Settings = $SliceColors[$SliceID]; $Settings["NoBorder"] = TRUE; - $this->pChartObject->drawPolygon($Plots,$Settings); - - if ( $SecondPass ) - { - $Settings = $SliceColors[$SliceID]; - if ( $Border ) - { $Settings["R"]+= 30; $Settings["G"]+= 30; $Settings["B"]+= 30;; } - - if ( isset($SliceAngle[$SliceID][1]) ) /* Empty error handling */ - { - $Angle = $SliceAngle[$SliceID][1]; - $Xc = cos(($Angle-90)*PI/180) * $Radius + $X; - $Yc = sin(($Angle-90)*PI/180) * $Radius*$SkewFactor + $Y; - $this->pChartObject->drawLine($Plots[0],$Plots[1],$Xc,$Yc,$Settings); - - $Angle = $SliceAngle[$SliceID][count($SliceAngle[$SliceID])-1]; - $Xc = cos(($Angle-90)*PI/180) * $Radius + $X; - $Yc = sin(($Angle-90)*PI/180) * $Radius*$SkewFactor + $Y; - $this->pChartObject->drawLine($Plots[0],$Plots[1],$Xc,$Yc,$Settings); - } - } - } - - /* Draw the two vertical edges */ - $Slices = array_reverse($Slices); - $SliceColors = array_reverse($SliceColors); - foreach($Slices as $SliceID => $Plots) - { - $Settings = $SliceColors[$SliceID]; - $Settings["R"]+= 10; $Settings["G"]+= 10; $Settings["B"]+= 10; $Settings["NoBorder"] = TRUE; - - if ( $Visible[$SliceID]["Start"] && isset($Plots[2])) /* Empty error handling */ - { - $this->pChartObject->drawLine($Plots[2],$Plots[3],$Plots[2],$Plots[3]- $SliceHeight,array("R"=>$Settings["R"],"G"=>$Settings["G"],"B"=>$Settings["B"])); - $Border = ""; - $Border[] = $Plots[0]; $Border[] = $Plots[1]; $Border[] = $Plots[0]; $Border[] = $Plots[1] - $SliceHeight; - $Border[] = $Plots[2]; $Border[] = $Plots[3] - $SliceHeight; $Border[] = $Plots[2]; $Border[] = $Plots[3]; - $this->pChartObject->drawPolygon($Border,$Settings); - } - } - - $Slices = array_reverse($Slices); - $SliceColors = array_reverse($SliceColors); - foreach($Slices as $SliceID => $Plots) - { - $Settings = $SliceColors[$SliceID]; - $Settings["R"]+= 10; $Settings["G"]+= 10; $Settings["B"]+= 10; $Settings["NoBorder"] = TRUE; - if ( $Visible[$SliceID]["End"] ) - { - $this->pChartObject->drawLine($Plots[count($Plots)-2],$Plots[count($Plots)-1],$Plots[count($Plots)-2],$Plots[count($Plots)-1]- $SliceHeight,array("R"=>$Settings["R"],"G"=>$Settings["G"],"B"=>$Settings["B"])); - - $Border = ""; - $Border[] = $Plots[0]; $Border[] = $Plots[1]; $Border[] = $Plots[0]; $Border[] = $Plots[1] - $SliceHeight; - $Border[] = $Plots[count($Plots)-2]; $Border[] = $Plots[count($Plots)-1] - $SliceHeight; $Border[] = $Plots[count($Plots)-2]; $Border[] = $Plots[count($Plots)-1]; - $this->pChartObject->drawPolygon($Border,$Settings); - } - } - - /* Draw the rounded edges */ - foreach($Slices as $SliceID => $Plots) - { - $Settings = $SliceColors[$SliceID]; - $Settings["R"]+= 10; $Settings["G"]+= 10; $Settings["B"]+= 10; $Settings["NoBorder"] = TRUE; - - for ($j=2;$j 90 ) - { - $Border = ""; - $Border[] = $Plots[$j]; $Border[] = $Plots[$j+1]; - $Border[] = $Plots[$j+2]; $Border[] = $Plots[$j+3]; - $Border[] = $Plots[$j+2]; $Border[] = $Plots[$j+3] - $SliceHeight; - $Border[] = $Plots[$j]; $Border[] = $Plots[$j+1] - $SliceHeight; - $this->pChartObject->drawPolygon($Border,$Settings); - } - } - - if ( $SecondPass ) - { - $Settings = $SliceColors[$SliceID]; - if ( $Border ) - { $Settings["R"]+= 30; $Settings["G"]+= 30; $Settings["B"]+= 30; } - - if ( isset($SliceAngle[$SliceID][1]) ) /* Empty error handling */ - { - $Angle = $SliceAngle[$SliceID][1]; - if ( $Angle < 270 && $Angle > 90 ) - { - $Xc = cos(($Angle-90)*PI/180) * $Radius + $X; - $Yc = sin(($Angle-90)*PI/180) * $Radius*$SkewFactor + $Y; - $this->pChartObject->drawLine($Xc,$Yc,$Xc,$Yc-$SliceHeight,$Settings); - } - } - - $Angle = $SliceAngle[$SliceID][count($SliceAngle[$SliceID])-1]; - if ( $Angle < 270 && $Angle > 90 ) - { - $Xc = cos(($Angle-90)*PI/180) * $Radius + $X; - $Yc = sin(($Angle-90)*PI/180) * $Radius*$SkewFactor + $Y; - $this->pChartObject->drawLine($Xc,$Yc,$Xc,$Yc-$SliceHeight,$Settings); - } - - if ( isset($SliceAngle[$SliceID][1]) && $SliceAngle[$SliceID][1] > 270 && $SliceAngle[$SliceID][count($SliceAngle[$SliceID])-1] < 270 ) - { - $Xc = cos((270-90)*PI/180) * $Radius + $X; - $Yc = sin((270-90)*PI/180) * $Radius*$SkewFactor + $Y; - $this->pChartObject->drawLine($Xc,$Yc,$Xc,$Yc-$SliceHeight,$Settings); - } - - if ( isset($SliceAngle[$SliceID][1]) && $SliceAngle[$SliceID][1] > 90 && $SliceAngle[$SliceID][count($SliceAngle[$SliceID])-1] < 90 ) - { - $Xc = cos((0)*PI/180) * $Radius + $X; - $Yc = sin((0)*PI/180) * $Radius*$SkewFactor + $Y; - $this->pChartObject->drawLine($Xc,$Yc,$Xc,$Yc-$SliceHeight,$Settings); - } - - } - } - - /* Draw the top splice */ - foreach($Slices as $SliceID => $Plots) - { - $Settings = $SliceColors[$SliceID]; - $Settings["R"]+= 20; $Settings["G"]+= 20; $Settings["B"]+= 20; - - $Top = ""; - for($j=0;$jpChartObject->drawPolygon($Top,$Settings); - - if ( $RecordImageMap && !$Shadow ) { $this->pChartObject->addToImageMap("POLY",$this->arraySerialize($Top),$this->pChartObject->toHTMLColor($Settings["R"],$Settings["G"],$Settings["B"]),$Data["Series"][$Data["Abscissa"]]["Data"][count($Slices)-$SliceID-1],$Values[$SliceID]); } - } - - - /* Second pass to smooth the angles */ - if ( $SecondPass ) - { - $Step = 360 / (2 * PI * $Radius); - $Offset = 360; $ID = count($Values)-1; - foreach($Values as $Key => $Value) - { - $FirstPoint = TRUE; - if ( $Shadow ) - $Settings = array("R"=>$this->pChartObject->ShadowR,"G"=>$this->pChartObject->ShadowG,"B"=>$this->pChartObject->ShadowB,"Alpha"=>$this->pChartObject->Shadowa); - else - { - if ( $Border ) - { $Settings = array("R"=>$Palette[$ID]["R"]+30,"G"=>$Palette[$ID]["G"]+30,"B"=>$Palette[$ID]["B"]+30,"Alpha"=>$Palette[$ID]["Alpha"]); } - else - $Settings = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]); - } - - $EndAngle = $Offset-($Value*$ScaleFactor); if ( $EndAngle < 0 ) { $EndAngle = 0; } - - if ($DataGapAngle == 0) - { $X0 = $X; $Y0 = $Y- $SliceHeight; } - else - { - $Angle = ($EndAngle - $Offset)/2 + $Offset; - $X0 = cos(($Angle-90)*PI/180) * $DataGapRadius + $X; - $Y0 = sin(($Angle-90)*PI/180) * $DataGapRadius*$SkewFactor + $Y - $SliceHeight; - } - $Plots[] = $X0; $Plots[] = $Y0; - - for($i=$Offset;$i>=$EndAngle;$i=$i-$Step) - { - $Xc = cos(($i-90)*PI/180) * $Radius + $X; - $Yc = sin(($i-90)*PI/180) * $Radius*$SkewFactor + $Y - $SliceHeight; - - if ( $FirstPoint ) { $this->pChartObject->drawLine($Xc,$Yc,$X0,$Y0,$Settings); } { $FirstPoint = FALSE; } - - $this->pChartObject->drawAntialiasPixel($Xc,$Yc,$Settings); - if ($i < 270 && $i > 90 ) { $this->pChartObject->drawAntialiasPixel($Xc,$Yc+$SliceHeight,$Settings); } - } - $this->pChartObject->drawLine($Xc,$Yc,$X0,$Y0,$Settings); - - $Offset = $i - $DataGapAngle; $ID--; - } - } - - if ( $WriteValues != NULL ) - { - $Step = 360 / (2 * PI * $Radius); - $Offset = 360; $ID = count($Values)-1; - $Settings = array("Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"R"=>$ValueR,"G"=>$ValueG,"B"=>$ValueB,"Alpha"=>$ValueAlpha); - foreach($Values as $Key => $Value) - { - $EndAngle = $Offset-($Value*$ScaleFactor); if ( $EndAngle < 0 ) { $EndAngle = 0; } - - $Angle = ($EndAngle - $Offset)/2 + $Offset; - - if ( $ValuePosition == PIE_VALUE_OUTSIDE ) - { - $Xc = cos(($Angle-90)*PI/180) * ($Radius+$ValuePadding) + $X; - $Yc = sin(($Angle-90)*PI/180) * (($Radius*$SkewFactor)+$ValuePadding) + $Y - $SliceHeight; - } - else - { - $Xc = cos(($Angle-90)*PI/180) * ($Radius)/2 + $X; - $Yc = sin(($Angle-90)*PI/180) * ($Radius*$SkewFactor)/2 + $Y - $SliceHeight; - } - - if ( $WriteValues == PIE_VALUE_PERCENTAGE ) - $Display = round(( 100 / $SerieSum ) * $Value,$Precision)."%"; - elseif ( $WriteValues == PIE_VALUE_NATURAL ) - $Display = $Value.$ValueSuffix; - - $this->pChartObject->drawText($Xc,$Yc,$Display,$Settings); - - $Offset = $EndAngle - $DataGapAngle; $ID--; - } - } - - if ( $DrawLabels ) - { - $Step = 360 / (2 * PI * $Radius); - $Offset = 360; $ID = count($Values)-1; - foreach($Values as $Key => $Value) - { - if ( $LabelColor == PIE_LABEL_COLOR_AUTO ) - { $Settings = array("FillR"=>$Palette[$ID]["R"],"FillG"=>$Palette[$ID]["G"],"FillB"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]);} - else - { $Settings = array("FillR"=>$LabelR,"FillG"=>$LabelG,"FillB"=>$LabelB,"Alpha"=>$LabelAlpha); } - - $EndAngle = $Offset-($Value*$ScaleFactor); if ( $EndAngle < 0 ) { $EndAngle = 0; } - - $Angle = ($EndAngle - $Offset)/2 + $Offset; - $Xc = cos(($Angle-90)*PI/180) * $Radius + $X; - $Yc = sin(($Angle-90)*PI/180) * $Radius*$SkewFactor + $Y - $SliceHeight; - - if ( isset($Data["Series"][$Data["Abscissa"]]["Data"][$ID]) ) - { - $Label = $Data["Series"][$Data["Abscissa"]]["Data"][$ID]; - - if ( $LabelStacked ) - $this->writePieLabel($Xc,$Yc,$Label,$Angle,$Settings,TRUE,$X,$Y,$Radius,TRUE); - else - $this->writePieLabel($Xc,$Yc,$Label,$Angle,$Settings,FALSE); - } - - $Offset = $EndAngle - $DataGapAngle; $ID--; - } - } - - if ( $DrawLabels && $LabelStacked ) { $this->writeShiftedLabels(); } - - $this->pChartObject->Shadow = $RestoreShadow; - - return(PIE_RENDERED); - } - - /* Draw the legend of pie chart */ - function drawPieLegend($X,$Y,$Format="") - { - $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->pChartObject->FontName; - $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->pChartObject->FontSize; - $FontR = isset($Format["FontR"]) ? $Format["FontR"] : $this->pChartObject->FontColorR; - $FontG = isset($Format["FontG"]) ? $Format["FontG"] : $this->pChartObject->FontColorG; - $FontB = isset($Format["FontB"]) ? $Format["FontB"] : $this->pChartObject->FontColorB; - $BoxSize = isset($Format["BoxSize"]) ? $Format["BoxSize"] : 5; - $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5; - $R = isset($Format["R"]) ? $Format["R"] : 200; - $G = isset($Format["G"]) ? $Format["G"] : 200; - $B = isset($Format["B"]) ? $Format["B"] : 200; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255; - $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; - $Style = isset($Format["Style"]) ? $Format["Style"] : LEGEND_ROUND; - $Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL; - - if ( $Surrounding != NULL ) { $BorderR = $R + $Surrounding; $BorderG = $G + $Surrounding; $BorderB = $B + $Surrounding; } - - $YStep = max($this->pChartObject->FontSize,$BoxSize) + 5; - $XStep = $BoxSize + 5; - - /* Data Processing */ - $Data = $this->pDataObject->getData(); - $Palette = $this->pDataObject->getPalette(); - - /* Do we have an abscissa serie defined? */ - if ( $Data["Abscissa"] == "" ) { return(PIE_NO_ABSCISSA); } - - $Boundaries = ""; $Boundaries["L"] = $X; $Boundaries["T"] = $Y; $Boundaries["R"] = 0; $Boundaries["B"] = 0; $vY = $Y; $vX = $X; - foreach($Data["Series"][$Data["Abscissa"]]["Data"] as $Key => $Value) - { - $BoxArray = $this->pChartObject->getTextBox($vX+$BoxSize+4,$vY+$BoxSize/2,$FontName,$FontSize,0,$Value); - - if ( $Mode == LEGEND_VERTICAL ) - { - if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$BoxSize/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$BoxSize/2; } - if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; } - if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$BoxSize/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$BoxSize/2; } - $vY=$vY+$YStep; - } - elseif ( $Mode == LEGEND_HORIZONTAL ) - { - if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$BoxSize/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$BoxSize/2; } - if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; } - if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$BoxSize/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$BoxSize/2; } - $vX=$Boundaries["R"]+$XStep; - } - } - $vY=$vY-$YStep; $vX=$vX-$XStep; - - $TopOffset = $Y - $Boundaries["T"]; - if ( $Boundaries["B"]-($vY+$BoxSize) < $TopOffset ) { $Boundaries["B"] = $vY+$BoxSize+$TopOffset; } - - if ( $Style == LEGEND_ROUND ) - $this->pChartObject->drawRoundedFilledRectangle($Boundaries["L"]-$Margin,$Boundaries["T"]-$Margin,$Boundaries["R"]+$Margin,$Boundaries["B"]+$Margin,$Margin,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB)); - elseif ( $Style == LEGEND_BOX ) - $this->pChartObject->drawFilledRectangle($Boundaries["L"]-$Margin,$Boundaries["T"]-$Margin,$Boundaries["R"]+$Margin,$Boundaries["B"]+$Margin,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB)); - - $RestoreShadow = $this->pChartObject->Shadow; $this->pChartObject->Shadow = FALSE; - foreach($Data["Series"][$Data["Abscissa"]]["Data"] as $Key => $Value) - { - $R = $Palette[$Key]["R"]; $G = $Palette[$Key]["G"]; $B = $Palette[$Key]["B"]; - - $this->pChartObject->drawFilledRectangle($X+1,$Y+1,$X+$BoxSize+1,$Y+$BoxSize+1,array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20)); - $this->pChartObject->drawFilledRectangle($X,$Y,$X+$BoxSize,$Y+$BoxSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Surrounding"=>20)); - if ( $Mode == LEGEND_VERTICAL ) - { - $this->pChartObject->drawText($X+$BoxSize+4,$Y+$BoxSize/2,$Value,array("R"=>$FontR,"G"=>$FontG,"B"=>$FontB,"Align"=>TEXT_ALIGN_MIDDLELEFT,"FontName"=>$FontName,"FontSize"=>$FontSize)); - $Y=$Y+$YStep; - } - elseif ( $Mode == LEGEND_HORIZONTAL ) - { - $BoxArray = $this->pChartObject->drawText($X+$BoxSize+4,$Y+$BoxSize/2,$Value,array("R"=>$FontR,"G"=>$FontG,"B"=>$FontB,"Align"=>TEXT_ALIGN_MIDDLELEFT,"FontName"=>$FontName,"FontSize"=>$FontSize)); - $X=$BoxArray[1]["X"]+2+$XStep; - } - } - - $this->Shadow = $RestoreShadow; - } - - /* Set the color of the specified slice */ - function setSliceColor($SliceID,$Format="") - { - $R = isset($Format["R"]) ? $Format["R"] : 0; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - - $this->pDataObject->Palette[$SliceID]["R"] = $R; - $this->pDataObject->Palette[$SliceID]["G"] = $G; - $this->pDataObject->Palette[$SliceID]["B"] = $B; - $this->pDataObject->Palette[$SliceID]["Alpha"] = $Alpha; - } - - /* Internally used compute the label positions */ - function writePieLabel($X,$Y,$Label,$Angle,$Settings,$Stacked,$Xc=0,$Yc=0,$Radius=0,$Reversed=FALSE) - { - $LabelOffset = 30; - $FontName = $this->pChartObject->FontName; - $FontSize = $this->pChartObject->FontSize; - - if ( !$Stacked ) - { - $Settings["Angle"] = 360-$Angle; - $Settings["Length"] = 25; - $Settings["Size"] = 8; - - $this->pChartObject->drawArrowLabel($X,$Y," ".$Label." ",$Settings); - } - else - { - $X2 = cos(deg2rad($Angle-90))*20+$X; - $Y2 = sin(deg2rad($Angle-90))*20+$Y; - - $TxtPos = $this->pChartObject->getTextBox($X,$Y,$FontName,$FontSize,0,$Label); - $Height = $TxtPos[0]["Y"] - $TxtPos[2]["Y"]; - $YTop = $Y2 - $Height/2 - 2; - $YBottom = $Y2 + $Height/2 + 2; - - if ( $this->LabelPos != "" ) - { - $Done = FALSE; - foreach($this->LabelPos as $Key => $Settings) - { - if ( !$Done ) - { - if ( $Angle <= 90 && (($YTop >= $Settings["YTop"] && $YTop <= $Settings["YBottom"]) || ($YBottom >= $Settings["YTop"] && $YBottom <= $Settings["YBottom"]))) - { $this->shift(0,180,-($Height+2),$Reversed); $Done = TRUE; } - if ( $Angle > 90 && $Angle <= 180 && (($YTop >= $Settings["YTop"] && $YTop <= $Settings["YBottom"]) || ($YBottom >= $Settings["YTop"] && $YBottom <= $Settings["YBottom"]))) - { $this->shift(0,180,-($Height+2),$Reversed); $Done = TRUE; } - if ( $Angle > 180 && $Angle <= 270 && (($YTop >= $Settings["YTop"] && $YTop <= $Settings["YBottom"]) || ($YBottom >= $Settings["YTop"] && $YBottom <= $Settings["YBottom"]))) - { $this->shift(180,360,($Height+2),$Reversed); $Done = TRUE; } - if ( $Angle > 270 && $Angle <= 360 && (($YTop >= $Settings["YTop"] && $YTop <= $Settings["YBottom"]) || ($YBottom >= $Settings["YTop"] && $YBottom <= $Settings["YBottom"]))) - { $this->shift(180,360,($Height+2),$Reversed); $Done = TRUE; } - } - } - } - - $LabelSettings = array("YTop"=>$YTop,"YBottom"=>$YBottom,"Label"=>$Label,"Angle"=>$Angle,"X1"=>$X,"Y1"=>$Y,"X2"=>$X2,"Y2"=>$Y2); - if ( $Angle <= 180 ) { $LabelSettings["X3"] = $Xc+$Radius+$LabelOffset; } - if ( $Angle > 180 ) { $LabelSettings["X3"] = $Xc-$Radius-$LabelOffset; } - $this->LabelPos[] = $LabelSettings; - } - } - - /* Internally used to shift label positions */ - function shift($StartAngle,$EndAngle,$Offset,$Reversed) - { - if ( $Reversed ) { $Offset = -$Offset; } - foreach($this->LabelPos as $Key => $Settings) - { - if ( $Settings["Angle"] > $StartAngle && $Settings["Angle"] <= $EndAngle ) { $this->LabelPos[$Key]["YTop"] = $Settings["YTop"] + $Offset; $this->LabelPos[$Key]["YBottom"] = $Settings["YBottom"] + $Offset; $this->LabelPos[$Key]["Y2"] = $Settings["Y2"] + $Offset; } - } - } - - /* Internally used to write the re-computed labels */ - function writeShiftedLabels() - { - if ( $this->LabelPos == "" ) { return(0); } - foreach($this->LabelPos as $Key => $Settings) - { - $X1 = $Settings["X1"]; $Y1 = $Settings["Y1"]; - $X2 = $Settings["X2"]; $Y2 = $Settings["Y2"]; - $X3 = $Settings["X3"]; - $Angle = $Settings["Angle"]; - $Label = $Settings["Label"]; - - $this->pChartObject->drawArrow($X2,$Y2,$X1,$Y1,array("Size"=>8)); - if ( $Angle <= 180 ) - { - $this->pChartObject->drawLine($X2,$Y2,$X3,$Y2); - $this->pChartObject->drawText($X3+2,$Y2,$Label,array("Align"=>TEXT_ALIGN_MIDDLELEFT)); - } - else - { - $this->pChartObject->drawLine($X2,$Y2,$X3,$Y2); - $this->pChartObject->drawText($X3-2,$Y2,$Label,array("Align"=>TEXT_ALIGN_MIDDLERIGHT)); - } - } - } - - /* Draw a ring chart */ - function draw2DRing($X,$Y,$Format="") - { - $OuterRadius = isset($Format["Radius"]) ? $Format["Radius"] : 60; - $Precision = isset($Format["Precision"]) ? $Format["Precision"] : 0; - $InnerRadius = isset($Format["Radius"]) ? $Format["Radius"] : 30; - $Border = isset($Format["Border"]) ? $Format["Border"] : FALSE; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255; - $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 100; - $Shadow = isset($Format["Shadow"]) ? $Format["Shadow"] : FALSE; - $DrawLabels = isset($Format["DrawLabels"]) ? $Format["DrawLabels"] : FALSE; - $LabelStacked = isset($Format["LabelStacked"]) ? $Format["LabelStacked"] : FALSE; - $LabelColor = isset($Format["LabelColor"]) ? $Format["LabelColor"] : PIE_LABEL_COLOR_MANUAL; - $LabelR = isset($Format["LabelR"]) ? $Format["LabelR"] : 0; - $LabelG = isset($Format["LabelG"]) ? $Format["LabelG"] : 0; - $LabelB = isset($Format["LabelB"]) ? $Format["LabelB"] : 0; - $LabelAlpha = isset($Format["LabelAlpha"]) ? $Format["LabelAlpha"] : 100; - $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : NULL; //PIE_VALUE_PERCENTAGE - $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 5; - $ValuePosition = isset($Format["ValuePosition"]) ? $Format["ValuePosition"] : PIE_VALUE_OUTSIDE; - $ValueSuffix = isset($Format["ValueSuffix"]) ? $Format["ValueSuffix"] : ""; - $ValueR = isset($Format["ValueR"]) ? $Format["ValueR"] : 255; - $ValueG = isset($Format["ValueG"]) ? $Format["ValueG"] : 255; - $ValueB = isset($Format["ValueB"]) ? $Format["ValueB"] : 255; - $ValueAlpha = isset($Format["ValueAlpha"]) ? $Format["ValueAlpha"] : 100; - $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; - - /* Data Processing */ - $Data = $this->pDataObject->getData(); - $Palette = $this->pDataObject->getPalette(); - - /* Do we have an abscissa serie defined? */ - if ( $Data["Abscissa"] == "" ) { return(PIE_NO_ABSCISSA); } - - /* Try to find the data serie */ - $DataSerie = ""; - foreach ($Data["Series"] as $SerieName => $SerieData) - { if ( $SerieName != $Data["Abscissa"]) { $DataSerie = $SerieName; } } - - /* Do we have data to compute? */ - if ( $DataSerie == "" ) { return(PIE_NO_DATASERIE); } - - /* Remove unused data */ - list($Data,$Palette) = $this->clean0Values($Data,$Palette,$DataSerie,$Data["Abscissa"]); - - /* Compute the pie sum */ - $SerieSum = $this->pDataObject->getSum($DataSerie); - - /* Do we have data to draw? */ - if ( $SerieSum == 0 ) { return(PIE_SUMISNULL); } - - /* Dump the real number of data to draw */ - $Values = ""; - foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) - { if ($Value != 0) { $Values[] = $Value; } } - - /* Compute the wasted angular space between series */ - if (count($Values)==1) { $WastedAngular = 0; } else { $WastedAngular = 0; } // count($Values) - - /* Compute the scale */ - $ScaleFactor = (360 - $WastedAngular) / $SerieSum; - - $RestoreShadow = $this->pChartObject->Shadow; - if ( $this->pChartObject->Shadow ) - { - $this->pChartObject->Shadow = FALSE; - - $ShadowFormat = $Format; $ShadowFormat["Shadow"] = TRUE; - $this->draw2DRing($X+$this->pChartObject->ShadowX,$Y+$this->pChartObject->ShadowY,$ShadowFormat); - } - - /* Draw the polygon pie elements */ - $Step = 360 / (2 * PI * $OuterRadius); - $Offset = 0; $ID = 0; - foreach($Values as $Key => $Value) - { - if ( $Shadow ) - { - $Settings = array("R"=>$this->pChartObject->ShadowR,"G"=>$this->pChartObject->ShadowG,"B"=>$this->pChartObject->ShadowB,"Alpha"=>$this->pChartObject->Shadowa); - $BorderColor = $Settings; - } - else - { - if ( !isset($Palette[$ID]["R"]) ) { $Color = $this->pChartObject->getRandomColor(); $Palette[$ID] = $Color; $this->pDataObject->savePalette($ID,$Color); } - $Settings = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]); - - if ( $Border ) - $BorderColor = array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha); - else - $BorderColor = $Settings; - } - - $Plots = ""; $Boundaries = ""; $AAPixels = ""; - $EndAngle = $Offset+($Value*$ScaleFactor); if ( $EndAngle > 360 ) { $EndAngle = 360; } - for($i=$Offset;$i<=$EndAngle;$i=$i+$Step) - { - $Xc = cos(($i-90)*PI/180) * $OuterRadius + $X; - $Yc = sin(($i-90)*PI/180) * $OuterRadius + $Y; - - if ( !isset($Boundaries[0]["X1"]) ) { $Boundaries[0]["X1"] = $Xc; $Boundaries[0]["Y1"] = $Yc; } - $AAPixels[] = array($Xc,$Yc); - - if ( $i<90 ) { $Yc++; } - if ( $i>180 && $i<270 ) { $Xc++; } - if ( $i>=270 ) { $Xc++; $Yc++; } - - $Plots[] = $Xc; $Plots[] = $Yc; - } - $Boundaries[1]["X1"] = $Xc; $Boundaries[1]["Y1"] = $Yc; - $Lasti = $EndAngle; - - for($i=$EndAngle;$i>=$Offset;$i=$i-$Step) - { - $Xc = cos(($i-90)*PI/180) * ($InnerRadius-1) + $X; - $Yc = sin(($i-90)*PI/180) * ($InnerRadius-1) + $Y; - - if ( !isset($Boundaries[1]["X2"]) ) { $Boundaries[1]["X2"] = $Xc; $Boundaries[1]["Y2"] = $Yc; } - $AAPixels[] = array($Xc,$Yc); - - $Xc = cos(($i-90)*PI/180) * $InnerRadius + $X; - $Yc = sin(($i-90)*PI/180) * $InnerRadius + $Y; - - if ( $i<90 ) { $Yc++; } - if ( $i>180 && $i<270 ) { $Xc++; } - if ( $i>=270 ) { $Xc++; $Yc++; } - - $Plots[] = $Xc; $Plots[] = $Yc; - } - $Boundaries[0]["X2"] = $Xc; $Boundaries[0]["Y2"] = $Yc; - - /* Draw the polygon */ - $this->pChartObject->drawPolygon($Plots,$Settings); - if ( $RecordImageMap && !$Shadow ) { $this->pChartObject->addToImageMap("POLY",$this->arraySerialize($Plots),$this->pChartObject->toHTMLColor($Palette[$ID]["R"],$Palette[$ID]["G"],$Palette[$ID]["B"]),$Data["Series"][$Data["Abscissa"]]["Data"][$Key],$Value); } - - /* Smooth the edges using AA */ - foreach($AAPixels as $iKey => $Pos ) { $this->pChartObject->drawAntialiasPixel($Pos[0],$Pos[1],$BorderColor); } - $this->pChartObject->drawLine($Boundaries[0]["X1"],$Boundaries[0]["Y1"],$Boundaries[0]["X2"],$Boundaries[0]["Y2"],$BorderColor); - $this->pChartObject->drawLine($Boundaries[1]["X1"],$Boundaries[1]["Y1"],$Boundaries[1]["X2"],$Boundaries[1]["Y2"],$BorderColor); - - if ( $DrawLabels && !$Shadow ) - { - if ( $LabelColor == PIE_LABEL_COLOR_AUTO ) - { $Settings = array("FillR"=>$Palette[$ID]["R"],"FillG"=>$Palette[$ID]["G"],"FillB"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]);} - else - { $Settings = array("FillR"=>$LabelR,"FillG"=>$LabelG,"FillB"=>$LabelB,"Alpha"=>$LabelAlpha); } - - $Angle = ($EndAngle - $Offset)/2 + $Offset; - $Xc = cos(($Angle-90)*PI/180) * $OuterRadius + $X; - $Yc = sin(($Angle-90)*PI/180) * $OuterRadius + $Y; - - $Label = $Data["Series"][$Data["Abscissa"]]["Data"][$Key]; - - if ( $LabelStacked ) - $this->writePieLabel($Xc,$Yc,$Label,$Angle,$Settings,TRUE,$X,$Y,$OuterRadius); - else - $this->writePieLabel($Xc,$Yc,$Label,$Angle,$Settings,FALSE); - } - - $Offset = $Lasti; $ID++; - } - - if ( $DrawLabels && $LabelStacked ) { $this->writeShiftedLabels(); } - - if ( $WriteValues && !$Shadow ) - { - $Step = 360 / (2 * PI * $OuterRadius); - $Offset = 0; - foreach($Values as $Key => $Value) - { - $EndAngle = $Offset+($Value*$ScaleFactor); - if ( $EndAngle > 360 ) { $EndAngle = 360; } - - $Angle = $Offset+($Value*$ScaleFactor)/2; - if ( $ValuePosition == PIE_VALUE_OUTSIDE ) - { - $Xc = cos(($Angle-90)*PI/180) * ($OuterRadius+$ValuePadding) + $X; - $Yc = sin(($Angle-90)*PI/180) * ($OuterRadius+$ValuePadding) + $Y; - if ( $Angle >=0 && $Angle <= 90 ) { $Align = TEXT_ALIGN_BOTTOMLEFT; } - if ( $Angle > 90 && $Angle <= 180 ) { $Align = TEXT_ALIGN_TOPLEFT; } - if ( $Angle > 180 && $Angle <= 270 ) { $Align = TEXT_ALIGN_TOPRIGHT; } - if ( $Angle > 270 ) { $Align = TEXT_ALIGN_BOTTOMRIGHT; } - } - else - { - $Xc = cos(($Angle-90)*PI/180) * (($OuterRadius-$InnerRadius)/2+$InnerRadius) + $X; - $Yc = sin(($Angle-90)*PI/180) * (($OuterRadius-$InnerRadius)/2+$InnerRadius) + $Y; - $Align = TEXT_ALIGN_MIDDLEMIDDLE; - } - - if ( $WriteValues == PIE_VALUE_PERCENTAGE ) - $Display = round(( 100 / $SerieSum ) * $Value,$Precision)."%"; - elseif ( $WriteValues == PIE_VALUE_NATURAL ) - $Display = $Value.$ValueSuffix; - else - $Label = ""; - - $this->pChartObject->drawText($Xc,$Yc,$Display,array("Align"=>$Align,"R"=>$ValueR,"G"=>$ValueG,"B"=>$ValueB)); - $Offset = $EndAngle; - } - } - - $this->pChartObject->Shadow = $RestoreShadow; - - return(PIE_RENDERED); - } - - /* Draw a 3D ring chart */ - function draw3DRing($X,$Y,$Format="") - { - $OuterRadius = isset($Format["OuterRadius"]) ? $Format["OuterRadius"] : 100; - $Precision = isset($Format["Precision"]) ? $Format["Precision"] : 0; - $InnerRadius = isset($Format["InnerRadius"]) ? $Format["InnerRadius"] : 30; - $SkewFactor = isset($Format["SkewFactor"]) ? $Format["SkewFactor"] : .6; - $SliceHeight = isset($Format["SliceHeight"]) ? $Format["SliceHeight"] : 10; - $DataGapAngle = isset($Format["DataGapAngle"]) ? $Format["DataGapAngle"] : 10; - $DataGapRadius = isset($Format["DataGapRadius"]) ? $Format["DataGapRadius"] : 10; - $Border = isset($Format["Border"]) ? $Format["Border"] : FALSE; - $Shadow = isset($Format["Shadow"]) ? $Format["Shadow"] : FALSE; - $DrawLabels = isset($Format["DrawLabels"]) ? $Format["DrawLabels"] : FALSE; - $LabelStacked = isset($Format["LabelStacked"]) ? $Format["LabelStacked"] : FALSE; - $LabelColor = isset($Format["LabelColor"]) ? $Format["LabelColor"] : PIE_LABEL_COLOR_MANUAL; - $LabelR = isset($Format["LabelR"]) ? $Format["LabelR"] : 0; - $LabelG = isset($Format["LabelG"]) ? $Format["LabelG"] : 0; - $LabelB = isset($Format["LabelB"]) ? $Format["LabelB"] : 0; - $LabelAlpha = isset($Format["LabelAlpha"]) ? $Format["LabelAlpha"] : 100; - $Cf = isset($Format["Cf"]) ? $Format["Cf"] : 20; - $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : PIE_VALUE_NATURAL; - $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : $SliceHeight + 15; - $ValuePosition = isset($Format["ValuePosition"]) ? $Format["ValuePosition"] : PIE_VALUE_OUTSIDE; - $ValueSuffix = isset($Format["ValueSuffix"]) ? $Format["ValueSuffix"] : ""; - $ValueR = isset($Format["ValueR"]) ? $Format["ValueR"] : 255; - $ValueG = isset($Format["ValueG"]) ? $Format["ValueG"] : 255; - $ValueB = isset($Format["ValueB"]) ? $Format["ValueB"] : 255; - $ValueAlpha = isset($Format["ValueAlpha"]) ? $Format["ValueAlpha"] : 100; - $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; - - /* Error correction for overlaying rounded corners */ - if ( $SkewFactor < .5 ) { $SkewFactor = .5; } - - /* Data Processing */ - $Data = $this->pDataObject->getData(); - $Palette = $this->pDataObject->getPalette(); - - /* Do we have an abscissa serie defined? */ - if ( $Data["Abscissa"] == "" ) { return(PIE_NO_ABSCISSA); } - - /* Try to find the data serie */ - $DataSerie = ""; - foreach ($Data["Series"] as $SerieName => $SerieData) - { if ( $SerieName != $Data["Abscissa"]) { $DataSerie = $SerieName; } } - - /* Do we have data to compute? */ - if ( $DataSerie == "" ) { return(PIE_NO_DATASERIE); } - - /* Remove unused data */ - list($Data,$Palette) = $this->clean0Values($Data,$Palette,$DataSerie,$Data["Abscissa"]); - - /* Compute the pie sum */ - $SerieSum = $this->pDataObject->getSum($DataSerie); - - /* Do we have data to draw? */ - if ( $SerieSum == 0 ) { return(PIE_SUMISNULL); } - - /* Dump the real number of data to draw */ - $Values = ""; - foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) - { if ($Value != 0) { $Values[] = $Value; } } - - /* Compute the wasted angular space between series */ - if (count($Values)==1) { $WastedAngular = 0; } else { $WastedAngular = count($Values) * $DataGapAngle; } - - /* Compute the scale */ - $ScaleFactor = (360 - $WastedAngular) / $SerieSum; - - $RestoreShadow = $this->pChartObject->Shadow; - if ( $this->pChartObject->Shadow ) { $this->pChartObject->Shadow = FALSE; } - - /* Draw the polygon ring elements */ - $Offset = 360; $ID = count($Values)-1; - $Values = array_reverse($Values); - $Slice = 0; $Slices = ""; $SliceColors = ""; $Visible = ""; $SliceAngle = ""; - foreach($Values as $Key => $Value) - { - if ( !isset($Palette[$ID]["R"]) ) { $Color = $this->pChartObject->getRandomColor(); $Palette[$ID] = $Color; $this->pDataObject->savePalette($ID,$Color); } - $Settings = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]); - - $SliceColors[$Slice] = $Settings; - - $StartAngle = $Offset; - $EndAngle = $Offset-($Value*$ScaleFactor); if ( $EndAngle < 0 ) { $EndAngle = 0; } - - if ( $StartAngle > 180 ) { $Visible[$Slice]["Start"] = TRUE; } else { $Visible[$Slice]["Start"] = TRUE; } - if ( $EndAngle < 180 ) { $Visible[$Slice]["End"] = FALSE; } else { $Visible[$Slice]["End"] = TRUE; } - - $Step = (360 / (2 * PI * $OuterRadius))/2; - $OutX1 = VOID; $OutY1 = VOID; - for($i=$Offset;$i>=$EndAngle;$i=$i-$Step) - { - $Xc = cos(($i-90)*PI/180) * ($OuterRadius+$DataGapRadius-2) + $X; - $Yc = sin(($i-90)*PI/180) * ($OuterRadius+$DataGapRadius-2)*$SkewFactor + $Y; - $Slices[$Slice]["AA"][] = array($Xc,$Yc); - - $Xc = cos(($i-90)*PI/180) * ($OuterRadius+$DataGapRadius-1) + $X; - $Yc = sin(($i-90)*PI/180) * ($OuterRadius+$DataGapRadius-1)*$SkewFactor + $Y; - $Slices[$Slice]["AA"][] = array($Xc,$Yc); - - $Xc = cos(($i-90)*PI/180) * ($OuterRadius+$DataGapRadius) + $X; - $Yc = sin(($i-90)*PI/180) * ($OuterRadius+$DataGapRadius)*$SkewFactor + $Y; - $this->pChartObject->drawAntialiasPixel($Xc,$Yc,$Settings); - - if ( $OutX1 == VOID ) { $OutX1 = $Xc; $OutY1 = $Yc; } - - if ( $i<90 ) { $Yc++; } - if ( $i>90 && $i<180 ) { $Xc++; } - if ( $i>180 && $i<270 ) { $Xc++; } - if ( $i>=270 ) { $Xc++; $Yc++; } - - $Slices[$Slice]["BottomPoly"][] = floor($Xc); $Slices[$Slice]["BottomPoly"][] = floor($Yc); - $Slices[$Slice]["TopPoly"][] = floor($Xc); $Slices[$Slice]["TopPoly"][] = floor($Yc)-$SliceHeight; - $Slices[$Slice]["Angle"][] = $i; - } - $OutX2 = $Xc; $OutY2 = $Yc; - - $Slices[$Slice]["Angle"][] = VOID; - $Lasti = $i; - - $Step = (360 / (2 * PI * $InnerRadius))/2; - $InX1 = VOID; $InY1 = VOID; - for($i=$EndAngle;$i<=$Offset;$i=$i+$Step) - { - $Xc = cos(($i-90)*PI/180) * ($InnerRadius+$DataGapRadius-1) + $X; - $Yc = sin(($i-90)*PI/180) * ($InnerRadius+$DataGapRadius-1)*$SkewFactor + $Y; - $Slices[$Slice]["AA"][] = array($Xc,$Yc); - - $Xc = cos(($i-90)*PI/180) * ($InnerRadius+$DataGapRadius) + $X; - $Yc = sin(($i-90)*PI/180) * ($InnerRadius+$DataGapRadius)*$SkewFactor + $Y; - $Slices[$Slice]["AA"][] = array($Xc,$Yc); - - if ( $InX1 == VOID ) { $InX1 = $Xc; $InY1 = $Yc; } - - if ( $i<90 ) { $Yc++; } - if ( $i>90 && $i<180 ) { $Xc++; } - if ( $i>180 && $i<270 ) { $Xc++; } - if ( $i>=270 ) { $Xc++; $Yc++; } - - $Slices[$Slice]["BottomPoly"][] = floor($Xc); $Slices[$Slice]["BottomPoly"][] = floor($Yc); - $Slices[$Slice]["TopPoly"][] = floor($Xc); $Slices[$Slice]["TopPoly"][] = floor($Yc)-$SliceHeight; - $Slices[$Slice]["Angle"][] = $i; - } - $InX2 = $Xc; $InY2 = $Yc; - - $Slices[$Slice]["InX1"] = $InX1; $Slices[$Slice]["InY1"] = $InY1; - $Slices[$Slice]["InX2"] = $InX2; $Slices[$Slice]["InY2"] = $InY2; - $Slices[$Slice]["OutX1"] = $OutX1; $Slices[$Slice]["OutY1"] = $OutY1; - $Slices[$Slice]["OutX2"] = $OutX2; $Slices[$Slice]["OutY2"] = $OutY2; - - $Offset = $Lasti - $DataGapAngle; $ID--; $Slice++; - } - - /* Draw the bottom pie splice */ - foreach($Slices as $SliceID => $Plots) - { - $Settings = $SliceColors[$SliceID]; $Settings["NoBorder"] = TRUE; - $this->pChartObject->drawPolygon($Plots["BottomPoly"],$Settings); - - foreach($Plots["AA"] as $Key => $Pos) - $this->pChartObject->drawAntialiasPixel($Pos[0],$Pos[1],$Settings); - - $this->pChartObject->drawLine($Plots["InX1"],$Plots["InY1"],$Plots["OutX2"],$Plots["OutY2"],$Settings); - $this->pChartObject->drawLine($Plots["InX2"],$Plots["InY2"],$Plots["OutX1"],$Plots["OutY1"],$Settings); - } - - $Slices = array_reverse($Slices); - $SliceColors = array_reverse($SliceColors); - - /* Draw the vertical edges (semi-visible) */ - foreach($Slices as $SliceID => $Plots) - { - $Settings = $SliceColors[$SliceID]; $Settings["NoBorder"] = TRUE; - $Settings["R"] = $Settings["R"]+$Cf; $Settings["G"] = $Settings["G"]+$Cf; $Settings["B"] = $Settings["B"]+$Cf; - - $StartAngle = $Plots["Angle"][0]; - foreach($Plots["Angle"] as $Key =>$Angle) { if ($Angle == VOID) { $EndAngle = $Plots["Angle"][$Key-1]; } } - - if ( $StartAngle >= 270 || $StartAngle <= 90 ) - $this->pChartObject->drawLine($Plots["OutX1"],$Plots["OutY1"],$Plots["OutX1"],$Plots["OutY1"]-$SliceHeight,$Settings); - if ( $StartAngle >= 270 || $StartAngle <= 90 ) - $this->pChartObject->drawLine($Plots["OutX2"],$Plots["OutY2"],$Plots["OutX2"],$Plots["OutY2"]-$SliceHeight,$Settings); - - $this->pChartObject->drawLine($Plots["InX1"],$Plots["InY1"],$Plots["InX1"],$Plots["InY1"]-$SliceHeight,$Settings); - $this->pChartObject->drawLine($Plots["InX2"],$Plots["InY2"],$Plots["InX2"],$Plots["InY2"]-$SliceHeight,$Settings); - } - - /* Draw the inner vertical slices */ - foreach($Slices as $SliceID => $Plots) - { - $Settings = $SliceColors[$SliceID]; $Settings["NoBorder"] = TRUE; - $Settings["R"] = $Settings["R"]+$Cf; $Settings["G"] = $Settings["G"]+$Cf; $Settings["B"] = $Settings["B"]+$Cf; - - $Outer = TRUE; $Inner = FALSE; - $InnerPlotsA = ""; $InnerPlotsB = ""; - foreach($Plots["Angle"] as $ID => $Angle) - { - if ( $Angle == VOID ) - { $Outer = FALSE; $Inner = TRUE; } - elseif( $Inner ) - { - if (( $Angle < 90 || $Angle > 270 ) && isset($Plots["BottomPoly"][$ID*2]) ) - { - $Xo = $Plots["BottomPoly"][$ID*2]; - $Yo = $Plots["BottomPoly"][$ID*2+1]; - - $InnerPlotsA[] = $Xo; $InnerPlotsA[] = $Yo; - $InnerPlotsB[] = $Xo; $InnerPlotsB[] = $Yo-$SliceHeight; - } - } - } - - if ( $InnerPlotsA != "" ) - { $InnerPlots = array_merge($InnerPlotsA,$this->arrayReverse($InnerPlotsB)); $this->pChartObject->drawPolygon($InnerPlots,$Settings); } - } - - /* Draw the splice top and left poly */ - foreach($Slices as $SliceID => $Plots) - { - $Settings = $SliceColors[$SliceID]; $Settings["NoBorder"] = TRUE; - $Settings["R"] = $Settings["R"]+$Cf*1.5; $Settings["G"] = $Settings["G"]+$Cf*1.5; $Settings["B"] = $Settings["B"]+$Cf*1.5; - - $StartAngle = $Plots["Angle"][0]; - foreach($Plots["Angle"] as $Key =>$Angle) { if ($Angle == VOID) { $EndAngle = $Plots["Angle"][$Key-1]; } } - - if ( $StartAngle < 180 ) - { - $Points = ""; - $Points[] = $Plots["InX2"]; - $Points[] = $Plots["InY2"]; - $Points[] = $Plots["InX2"]; - $Points[] = $Plots["InY2"]-$SliceHeight; - $Points[] = $Plots["OutX1"]; - $Points[] = $Plots["OutY1"]-$SliceHeight; - $Points[] = $Plots["OutX1"]; - $Points[] = $Plots["OutY1"]; - - $this->pChartObject->drawPolygon($Points,$Settings); - } - - if ( $EndAngle > 180 ) - { - $Points = ""; - $Points[] = $Plots["InX1"]; - $Points[] = $Plots["InY1"]; - $Points[] = $Plots["InX1"]; - $Points[] = $Plots["InY1"]-$SliceHeight; - $Points[] = $Plots["OutX2"]; - $Points[] = $Plots["OutY2"]-$SliceHeight; - $Points[] = $Plots["OutX2"]; - $Points[] = $Plots["OutY2"]; - - $this->pChartObject->drawPolygon($Points,$Settings); - } - } - - - /* Draw the vertical edges (visible) */ - foreach($Slices as $SliceID => $Plots) - { - $Settings = $SliceColors[$SliceID]; $Settings["NoBorder"] = TRUE; - $Settings["R"] = $Settings["R"]+$Cf; $Settings["G"] = $Settings["G"]+$Cf; $Settings["B"] = $Settings["B"]+$Cf; - - $StartAngle = $Plots["Angle"][0]; - foreach($Plots["Angle"] as $Key =>$Angle) { if ($Angle == VOID) { $EndAngle = $Plots["Angle"][$Key-1]; } } - - if ( $StartAngle <= 270 && $StartAngle >= 90 ) - $this->pChartObject->drawLine($Plots["OutX1"],$Plots["OutY1"],$Plots["OutX1"],$Plots["OutY1"]-$SliceHeight,$Settings); - if ( $EndAngle <= 270 && $EndAngle >= 90 ) - $this->pChartObject->drawLine($Plots["OutX2"],$Plots["OutY2"],$Plots["OutX2"],$Plots["OutY2"]-$SliceHeight,$Settings); - } - - - /* Draw the outer vertical slices */ - foreach($Slices as $SliceID => $Plots) - { - $Settings = $SliceColors[$SliceID]; $Settings["NoBorder"] = TRUE; - $Settings["R"] = $Settings["R"]+$Cf; $Settings["G"] = $Settings["G"]+$Cf; $Settings["B"] = $Settings["B"]+$Cf; - - $Outer = TRUE; $Inner = FALSE; - $OuterPlotsA = ""; $OuterPlotsB = ""; $InnerPlotsA = ""; $InnerPlotsB = ""; - foreach($Plots["Angle"] as $ID => $Angle) - { - if ( $Angle == VOID ) - { $Outer = FALSE; $Inner = TRUE; } - elseif( $Outer ) - { - if ( ( $Angle > 90 && $Angle < 270 ) && isset($Plots["BottomPoly"][$ID*2]) ) - { - $Xo = $Plots["BottomPoly"][$ID*2]; - $Yo = $Plots["BottomPoly"][$ID*2+1]; - - $OuterPlotsA[] = $Xo; $OuterPlotsA[] = $Yo; - $OuterPlotsB[] = $Xo; $OuterPlotsB[] = $Yo-$SliceHeight; - } - } - } - if ( $OuterPlotsA != "" ) - { $OuterPlots = array_merge($OuterPlotsA,$this->arrayReverse($OuterPlotsB)); $this->pChartObject->drawPolygon($OuterPlots,$Settings); } - } - - $Slices = array_reverse($Slices); - $SliceColors = array_reverse($SliceColors); - - - /* Draw the top pie splice */ - foreach($Slices as $SliceID => $Plots) - { - $Settings = $SliceColors[$SliceID]; $Settings["NoBorder"] = TRUE; - $Settings["R"] = $Settings["R"]+$Cf*2; $Settings["G"] = $Settings["G"]+$Cf*2; $Settings["B"] = $Settings["B"]+$Cf*2; - - $this->pChartObject->drawPolygon($Plots["TopPoly"],$Settings); - - if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("POLY",$this->arraySerialize($Plots["TopPoly"]),$this->pChartObject->toHTMLColor($Settings["R"],$Settings["G"],$Settings["B"]),$Data["Series"][$Data["Abscissa"]]["Data"][$SliceID],$Data["Series"][$DataSerie]["Data"][count($Slices)-$SliceID-1]); } - - foreach($Plots["AA"] as $Key => $Pos) - $this->pChartObject->drawAntialiasPixel($Pos[0],$Pos[1]-$SliceHeight,$Settings); - - $this->pChartObject->drawLine($Plots["InX1"],$Plots["InY1"]-$SliceHeight,$Plots["OutX2"],$Plots["OutY2"]-$SliceHeight,$Settings); - $this->pChartObject->drawLine($Plots["InX2"],$Plots["InY2"]-$SliceHeight,$Plots["OutX1"],$Plots["OutY1"]-$SliceHeight,$Settings); - } - - if ( $DrawLabels ) - { - $Offset = 360; - foreach($Values as $Key => $Value) - { - $StartAngle = $Offset; - $EndAngle = $Offset-($Value*$ScaleFactor); if ( $EndAngle < 0 ) { $EndAngle = 0; } - - if ( $LabelColor == PIE_LABEL_COLOR_AUTO ) - { $Settings = array("FillR"=>$Palette[$ID]["R"],"FillG"=>$Palette[$ID]["G"],"FillB"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]);} - else - { $Settings = array("FillR"=>$LabelR,"FillG"=>$LabelG,"FillB"=>$LabelB,"Alpha"=>$LabelAlpha); } - - $Angle = ($EndAngle - $Offset)/2 + $Offset; - $Xc = cos(($Angle-90)*PI/180) * ($OuterRadius+$DataGapRadius) + $X; - $Yc = sin(($Angle-90)*PI/180) * ($OuterRadius+$DataGapRadius)*$SkewFactor + $Y; - - if ( $WriteValues == PIE_VALUE_PERCENTAGE ) - $Label = $Display = round(( 100 / $SerieSum ) * $Value,$Precision)."%"; - elseif ( $WriteValues == PIE_VALUE_NATURAL ) - $Label = $Data["Series"][$Data["Abscissa"]]["Data"][$Key]; - else - $Label = ""; - - if ( $LabelStacked ) - $this->writePieLabel($Xc,$Yc-$SliceHeight,$Label,$Angle,$Settings,TRUE,$X,$Y,$OuterRadius); - else - $this->writePieLabel($Xc,$Yc-$SliceHeight,$Label,$Angle,$Settings,FALSE); - - $Offset = $EndAngle - $DataGapAngle; $ID--; $Slice++; - } - } - if ( $DrawLabels && $LabelStacked ) { $this->writeShiftedLabels(); } - - $this->pChartObject->Shadow = $RestoreShadow; - - return(PIE_RENDERED); - } - - /* Serialize an array */ - function arraySerialize($Data) - { - $Result = ""; - foreach($Data as $Key => $Value) - { if ($Result == "") { $Result = floor($Value); } else { $Result = $Result.",".floor($Value); } } - - return($Result); - } - - /* Reverse an array */ - function arrayReverse($Plots) - { - $Result = ""; - - for($i=count($Plots)-1;$i>=0;$i=$i-2) - { $Result[] = $Plots[$i-1]; $Result[] = $Plots[$i]; } - - return($Result); - } - - /* Remove unused series & values */ - function clean0Values($Data,$Palette,$DataSerie,$AbscissaSerie) - { - $NewPalette = ""; $NewData = ""; $NewAbscissa = ""; - - /* Remove unused series */ - foreach($Data["Series"] as $SerieName => $SerieSettings) - { if ( $SerieName != $DataSerie && $SerieName != $AbscissaSerie ) { unset($Data["Series"][$SerieName]); } } - - /* Remove NULL values */ - foreach($Data["Series"][$DataSerie]["Data"] as $Key => $Value) - { - if ($Value != 0 ) - { - $NewData[] = $Value; - $NewAbscissa[] = $Data["Series"][$AbscissaSerie]["Data"][$Key]; - if ( isset($Palette[$Key]) ) { $NewPalette[] = $Palette[$Key]; } - } - } - $Data["Series"][$DataSerie]["Data"] = $NewData; - $Data["Series"][$AbscissaSerie]["Data"] = $NewAbscissa; - - return(array($Data,$NewPalette)); - } - } +pChartObject = $Object; + + /* Cache the pData object reference */ + $this->pDataObject = $pDataObject; + } + + /* Draw a pie chart */ + function draw2DPie($X,$Y,$Format="") + { + $Radius = isset($Format["Radius"]) ? $Format["Radius"] : 60; + $Precision = isset($Format["Precision"]) ? $Format["Precision"] : 0; + $DataGapAngle = isset($Format["DataGapAngle"]) ? $Format["DataGapAngle"] : 0; + $DataGapRadius = isset($Format["DataGapRadius"]) ? $Format["DataGapRadius"] : 0; + $SecondPass = isset($Format["SecondPass"]) ? $Format["SecondPass"] : TRUE; + $Border = isset($Format["Border"]) ? $Format["Border"] : FALSE; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255; + $Shadow = isset($Format["Shadow"]) ? $Format["Shadow"] : FALSE; + $DrawLabels = isset($Format["DrawLabels"]) ? $Format["DrawLabels"] : FALSE; + $LabelStacked = isset($Format["LabelStacked"]) ? $Format["LabelStacked"] : FALSE; + $LabelColor = isset($Format["LabelColor"]) ? $Format["LabelColor"] : PIE_LABEL_COLOR_MANUAL; + $LabelR = isset($Format["LabelR"]) ? $Format["LabelR"] : 0; + $LabelG = isset($Format["LabelG"]) ? $Format["LabelG"] : 0; + $LabelB = isset($Format["LabelB"]) ? $Format["LabelB"] : 0; + $LabelAlpha = isset($Format["LabelAlpha"]) ? $Format["LabelAlpha"] : 100; + $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : NULL; + $ValuePosition = isset($Format["ValuePosition"]) ? $Format["ValuePosition"] : PIE_VALUE_OUTSIDE; + $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 15; + $ValueSuffix = isset($Format["ValueSuffix"]) ? $Format["ValueSuffix"] : ""; + $ValueR = isset($Format["ValueR"]) ? $Format["ValueR"] : 255; + $ValueG = isset($Format["ValueG"]) ? $Format["ValueG"] : 255; + $ValueB = isset($Format["ValueB"]) ? $Format["ValueB"] : 255; + $ValueAlpha = isset($Format["ValueAlpha"]) ? $Format["ValueAlpha"] : 100; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; + + /* Data Processing */ + $Data = $this->pDataObject->getData(); + $Palette = $this->pDataObject->getPalette(); + + /* Do we have an abscissa serie defined? */ + if ( $Data["Abscissa"] == "" ) { return(PIE_NO_ABSCISSA); } + + /* Try to find the data serie */ + $DataSerie = ""; + foreach ($Data["Series"] as $SerieName => $SerieData) + { if ( $SerieName != $Data["Abscissa"]) { $DataSerie = $SerieName; } } + + /* Do we have data to compute? */ + if ( $DataSerie == "" ) { return(PIE_NO_DATASERIE); } + + /* Remove unused data */ + list($Data,$Palette) = $this->clean0Values($Data,$Palette,$DataSerie,$Data["Abscissa"]); + + /* Compute the pie sum */ + $SerieSum = $this->pDataObject->getSum($DataSerie); + + /* Do we have data to draw? */ + if ( $SerieSum == 0 ) { return(PIE_SUMISNULL); } + + /* Dump the real number of data to draw */ + $Values = ""; + foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) + { if ($Value != 0) { $Values[] = $Value; } } + + /* Compute the wasted angular space between series */ + if (count($Values)==1) { $WastedAngular = 0; } else { $WastedAngular = count($Values) * $DataGapAngle; } + + /* Compute the scale */ + $ScaleFactor = (360 - $WastedAngular) / $SerieSum; + + $RestoreShadow = $this->pChartObject->Shadow; + if ( $this->pChartObject->Shadow ) + { + $this->pChartObject->Shadow = FALSE; + + $ShadowFormat = $Format; $ShadowFormat["Shadow"] = TRUE; + $this->draw2DPie($X+$this->pChartObject->ShadowX,$Y+$this->pChartObject->ShadowY,$ShadowFormat); + } + + /* Draw the polygon pie elements */ + $Step = 360 / (2 * PI * $Radius); + $Offset = 0; $ID = 0; + foreach($Values as $Key => $Value) + { + if ( $Shadow ) + $Settings = array("R"=>$this->pChartObject->ShadowR,"G"=>$this->pChartObject->ShadowG,"B"=>$this->pChartObject->ShadowB,"Alpha"=>$this->pChartObject->Shadowa); + else + { + if ( !isset($Palette[$ID]["R"]) ) { $Color = $this->pChartObject->getRandomColor(); $Palette[$ID] = $Color; $this->pDataObject->savePalette($ID,$Color); } + $Settings = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]); + } + + if ( !$SecondPass && !$Shadow ) + { + if ( !$Border ) + $Settings["Surrounding"] = 10; + else + { $Settings["BorderR"] = $BorderR; $Settings["BorderG"] = $BorderG; $Settings["BorderB"] = $BorderB; } + } + + $Plots = ""; + $EndAngle = $Offset+($Value*$ScaleFactor); if ( $EndAngle > 360 ) { $EndAngle = 360; } + + $Angle = ($EndAngle - $Offset)/2 + $Offset; + if ($DataGapAngle == 0) + { $X0 = $X; $Y0 = $Y; } + else + { + $X0 = cos(($Angle-90)*PI/180) * $DataGapRadius + $X; + $Y0 = sin(($Angle-90)*PI/180) * $DataGapRadius + $Y; + } + + $Plots[] = $X0; $Plots[] = $Y0; + + + for($i=$Offset;$i<=$EndAngle;$i=$i+$Step) + { + $Xc = cos(($i-90)*PI/180) * $Radius + $X; + $Yc = sin(($i-90)*PI/180) * $Radius + $Y; + + if ( $SecondPass && ( $i<90 )) { $Yc++; } + if ( $SecondPass && ( $i>180 && $i<270 )) { $Xc++; } + if ( $SecondPass && ( $i>=270 )) { $Xc++; $Yc++; } + + $Plots[] = $Xc; $Plots[] = $Yc; + } + + $this->pChartObject->drawPolygon($Plots,$Settings); + if ( $RecordImageMap && !$Shadow ) { $this->pChartObject->addToImageMap("POLY",$this->arraySerialize($Plots),$this->pChartObject->toHTMLColor($Palette[$ID]["R"],$Palette[$ID]["G"],$Palette[$ID]["B"]),$Data["Series"][$Data["Abscissa"]]["Data"][$Key],$Value); } + + if ( $DrawLabels && !$Shadow && !$SecondPass ) + { + if ( $LabelColor == PIE_LABEL_COLOR_AUTO ) + { $Settings = array("FillR"=>$Palette[$ID]["R"],"FillG"=>$Palette[$ID]["G"],"FillB"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]);} + else + { $Settings = array("FillR"=>$LabelR,"FillG"=>$LabelG,"FillB"=>$LabelB,"Alpha"=>$LabelAlpha); } + + $Angle = ($EndAngle - $Offset)/2 + $Offset; + $Xc = cos(($Angle-90)*PI/180) * $Radius + $X; + $Yc = sin(($Angle-90)*PI/180) * $Radius + $Y; + + $Label = $Data["Series"][$Data["Abscissa"]]["Data"][$Key]; + + if ( $LabelStacked ) + $this->writePieLabel($Xc,$Yc,$Label,$Angle,$Settings,TRUE,$X,$Y,$Radius); + else + $this->writePieLabel($Xc,$Yc,$Label,$Angle,$Settings,FALSE); + } + + $Offset = $i + $DataGapAngle; $ID++; + } + + /* Second pass to smooth the angles */ + if ( $SecondPass ) + { + $Step = 360 / (2 * PI * $Radius); + $Offset = 0; $ID = 0; + foreach($Values as $Key => $Value) + { + $FirstPoint = TRUE; + if ( $Shadow ) + $Settings = array("R"=>$this->pChartObject->ShadowR,"G"=>$this->pChartObject->ShadowG,"B"=>$this->pChartObject->ShadowB,"Alpha"=>$this->pChartObject->Shadowa); + else + { + if ( $Border ) + $Settings = array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB); + else + $Settings = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]); + } + + $EndAngle = $Offset+($Value*$ScaleFactor); if ( $EndAngle > 360 ) { $EndAngle = 360; } + + if ($DataGapAngle == 0) + { $X0 = $X; $Y0 = $Y; } + else + { + $Angle = ($EndAngle - $Offset)/2 + $Offset; + $X0 = cos(($Angle-90)*PI/180) * $DataGapRadius + $X; + $Y0 = sin(($Angle-90)*PI/180) * $DataGapRadius + $Y; + } + $Plots[] = $X0; $Plots[] = $Y0; + + for($i=$Offset;$i<=$EndAngle;$i=$i+$Step) + { + $Xc = cos(($i-90)*PI/180) * $Radius + $X; + $Yc = sin(($i-90)*PI/180) * $Radius + $Y; + + if ( $FirstPoint ) { $this->pChartObject->drawLine($Xc,$Yc,$X0,$Y0,$Settings); } { $FirstPoint = FALSE; } + + $this->pChartObject->drawAntialiasPixel($Xc,$Yc,$Settings); + } + $this->pChartObject->drawLine($Xc,$Yc,$X0,$Y0,$Settings); + + if ( $DrawLabels && !$Shadow ) + { + if ( $LabelColor == PIE_LABEL_COLOR_AUTO ) + { $Settings = array("FillR"=>$Palette[$ID]["R"],"FillG"=>$Palette[$ID]["G"],"FillB"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]);} + else + { $Settings = array("FillR"=>$LabelR,"FillG"=>$LabelG,"FillB"=>$LabelB,"Alpha"=>$LabelAlpha); } + + $Angle = ($EndAngle - $Offset)/2 + $Offset; + $Xc = cos(($Angle-90)*PI/180) * $Radius + $X; + $Yc = sin(($Angle-90)*PI/180) * $Radius + $Y; + + $Label = $Data["Series"][$Data["Abscissa"]]["Data"][$Key]; + + if ( $LabelStacked ) + $this->writePieLabel($Xc,$Yc,$Label,$Angle,$Settings,TRUE,$X,$Y,$Radius); + else + $this->writePieLabel($Xc,$Yc,$Label,$Angle,$Settings,FALSE); + } + + $Offset = $i + $DataGapAngle; $ID++; + } + } + + if ( $WriteValues != NULL && !$Shadow ) + { + $Step = 360 / (2 * PI * $Radius); + $Offset = 0; $ID = count($Values)-1; + $Settings = array("Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"R"=>$ValueR,"G"=>$ValueG,"B"=>$ValueB,"Alpha"=>$ValueAlpha); + foreach($Values as $Key => $Value) + { + $EndAngle = ($Value*$ScaleFactor) + $Offset; if ( (int)$EndAngle > 360 ) { $EndAngle = 0; } + $Angle = ($EndAngle - $Offset)/2 + $Offset; + + if ( $ValuePosition == PIE_VALUE_OUTSIDE ) + { + $Xc = cos(($Angle-90)*PI/180) * ($Radius+$ValuePadding) + $X; + $Yc = sin(($Angle-90)*PI/180) * ($Radius+$ValuePadding) + $Y; + } + else + { + $Xc = cos(($Angle-90)*PI/180) * ($Radius)/2 + $X; + $Yc = sin(($Angle-90)*PI/180) * ($Radius)/2 + $Y; + } + + if ( $WriteValues == PIE_VALUE_PERCENTAGE ) + $Display = round(( 100 / $SerieSum ) * $Value,$Precision)."%"; + elseif ( $WriteValues == PIE_VALUE_NATURAL ) + $Display = $Value.$ValueSuffix; + + $this->pChartObject->drawText($Xc,$Yc,$Display,$Settings); + + $Offset = $EndAngle + $DataGapAngle; $ID--; + } + } + + if ( $DrawLabels && $LabelStacked ) { $this->writeShiftedLabels(); } + + $this->pChartObject->Shadow = $RestoreShadow; + + return(PIE_RENDERED); + } + + /* Draw a 3D pie chart */ + function draw3DPie($X,$Y,$Format="") + { + /* Rendering layout */ + $Radius = isset($Format["Radius"]) ? $Format["Radius"] : 80; + $Precision = isset($Format["Precision"]) ? $Format["Precision"] : 0; + $SkewFactor = isset($Format["SkewFactor"]) ? $Format["SkewFactor"] : .5; + $SliceHeight = isset($Format["SliceHeight"]) ? $Format["SliceHeight"] : 20; + $DataGapAngle = isset($Format["DataGapAngle"]) ? $Format["DataGapAngle"] : 0; + $DataGapRadius = isset($Format["DataGapRadius"]) ? $Format["DataGapRadius"] : 0; + $SecondPass = isset($Format["SecondPass"]) ? $Format["SecondPass"] : TRUE; + $Border = isset($Format["Border"]) ? $Format["Border"] : FALSE; + $Shadow = isset($Format["Shadow"]) ? $Format["Shadow"] : FALSE; + $DrawLabels = isset($Format["DrawLabels"]) ? $Format["DrawLabels"] : FALSE; + $LabelStacked = isset($Format["LabelStacked"]) ? $Format["LabelStacked"] : FALSE; + $LabelColor = isset($Format["LabelColor"]) ? $Format["LabelColor"] : PIE_LABEL_COLOR_MANUAL; + $LabelR = isset($Format["LabelR"]) ? $Format["LabelR"] : 0; + $LabelG = isset($Format["LabelG"]) ? $Format["LabelG"] : 0; + $LabelB = isset($Format["LabelB"]) ? $Format["LabelB"] : 0; + $LabelAlpha = isset($Format["LabelAlpha"]) ? $Format["LabelAlpha"] : 100; + $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : NULL; //PIE_VALUE_PERCENTAGE + $ValuePosition = isset($Format["ValuePosition"]) ? $Format["ValuePosition"] : PIE_VALUE_INSIDE; + $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 15; + $ValueSuffix = isset($Format["ValueSuffix"]) ? $Format["ValueSuffix"] : ""; + $ValueR = isset($Format["ValueR"]) ? $Format["ValueR"] : 255; + $ValueG = isset($Format["ValueG"]) ? $Format["ValueG"] : 255; + $ValueB = isset($Format["ValueB"]) ? $Format["ValueB"] : 255; + $ValueAlpha = isset($Format["ValueAlpha"]) ? $Format["ValueAlpha"] : 100; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; + + /* Error correction for overlaying rounded corners */ + if ( $SkewFactor < .5 ) { $SkewFactor = .5; } + + /* Data Processing */ + $Data = $this->pDataObject->getData(); + $Palette = $this->pDataObject->getPalette(); + + /* Do we have an abscissa serie defined? */ + if ( $Data["Abscissa"] == "" ) { return(PIE_NO_ABSCISSA); } + + /* Try to find the data serie */ + $DataSerie = ""; + foreach ($Data["Series"] as $SerieName => $SerieData) + { if ( $SerieName != $Data["Abscissa"]) { $DataSerie = $SerieName; } } + + /* Do we have data to compute? */ + if ( $DataSerie == "" ) { return(PIE_NO_DATASERIE); } + + /* Remove unused data */ + list($Data,$Palette) = $this->clean0Values($Data,$Palette,$DataSerie,$Data["Abscissa"]); + + /* Compute the pie sum */ + $SerieSum = $this->pDataObject->getSum($DataSerie); + + /* Do we have data to draw? */ + if ( $SerieSum == 0 ) { return(PIE_SUMISNULL); } + + /* Dump the real number of data to draw */ + $Values = ""; + foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) + { if ($Value != 0) { $Values[] = $Value; } } + + /* Compute the wasted angular space between series */ + if (count($Values)==1) { $WastedAngular = 0; } else { $WastedAngular = count($Values) * $DataGapAngle; } + + /* Compute the scale */ + $ScaleFactor = (360 - $WastedAngular) / $SerieSum; + + $RestoreShadow = $this->pChartObject->Shadow; + if ( $this->pChartObject->Shadow ) { $this->pChartObject->Shadow = FALSE; } + + /* Draw the polygon pie elements */ + $Step = 360 / (2 * PI * $Radius); + $Offset = 360; $ID = count($Values)-1; + $Values = array_reverse($Values); + $Slice = 0; $Slices = ""; $SliceColors = ""; $Visible = ""; $SliceAngle = ""; + foreach($Values as $Key => $Value) + { + if ( !isset($Palette[$ID]["R"]) ) { $Color = $this->pChartObject->getRandomColor(); $Palette[$ID] = $Color; $this->pDataObject->savePalette($ID,$Color); } + $Settings = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]); + + $SliceColors[$Slice] = $Settings; + + $StartAngle = $Offset; + $EndAngle = $Offset-($Value*$ScaleFactor); if ( $EndAngle < 0 ) { $EndAngle = 0; } + + if ( $StartAngle > 180 ) { $Visible[$Slice]["Start"] = TRUE; } else { $Visible[$Slice]["Start"] = TRUE; } + if ( $EndAngle < 180 ) { $Visible[$Slice]["End"] = FALSE; } else { $Visible[$Slice]["End"] = TRUE; } + + if ($DataGapAngle == 0) + { $X0 = $X; $Y0 = $Y; } + else + { + $Angle = ($EndAngle - $Offset)/2 + $Offset; + $X0 = cos(($Angle-90)*PI/180) * $DataGapRadius + $X; + $Y0 = sin(($Angle-90)*PI/180) * $DataGapRadius*$SkewFactor + $Y; + } + $Slices[$Slice][] = $X0; $Slices[$Slice][] = $Y0; $SliceAngle[$Slice][] = 0; + + for($i=$Offset;$i>=$EndAngle;$i=$i-$Step) + { + $Xc = cos(($i-90)*PI/180) * $Radius + $X; + $Yc = sin(($i-90)*PI/180) * $Radius*$SkewFactor + $Y; + + if ( ($SecondPass || $RestoreShadow ) && ( $i<90 )) { $Yc++; } + if ( ($SecondPass || $RestoreShadow ) && ( $i>90 && $i<180 )) { $Xc++; } + if ( ($SecondPass || $RestoreShadow ) && ( $i>180 && $i<270 )) { $Xc++; } + if ( ($SecondPass || $RestoreShadow ) && ( $i>=270 )) { $Xc++; $Yc++; } + + $Slices[$Slice][] = $Xc; $Slices[$Slice][] = $Yc; $SliceAngle[$Slice][] = $i; + } + + $Offset = $i - $DataGapAngle; $ID--; $Slice++; + } + + /* Draw the bottom shadow if needed */ + if ( $RestoreShadow && ($this->pChartObject->ShadowX != 0 || $this->pChartObject->ShadowY !=0 )) + { + foreach($Slices as $SliceID => $Plots) + { + $ShadowPie = ""; + for($i=0;$ipChartObject->ShadowX; $ShadowPie[] = $Plots[$i+1]+$this->pChartObject->ShadowY; } + + $Settings = array("R"=>$this->pChartObject->ShadowR,"G"=>$this->pChartObject->ShadowG,"B"=>$this->pChartObject->ShadowB,"Alpha"=>$this->pChartObject->Shadowa,"NoBorder"=>TRUE); + $this->pChartObject->drawPolygon($ShadowPie,$Settings); + } + + $Step = 360 / (2 * PI * $Radius); + $Offset = 360; + foreach($Values as $Key => $Value) + { + $EndAngle = $Offset-($Value*$ScaleFactor); if ( $EndAngle < 0 ) { $EndAngle = 0; } + + for($i=$Offset;$i>=$EndAngle;$i=$i-$Step) + { + $Xc = cos(($i-90)*PI/180) * $Radius + $X + $this->pChartObject->ShadowX; + $Yc = sin(($i-90)*PI/180) * $Radius*$SkewFactor + $Y + $this->pChartObject->ShadowY; + + $this->pChartObject->drawAntialiasPixel($Xc,$Yc,$Settings); + } + + $Offset = $i - $DataGapAngle; $ID--; + } + } + + /* Draw the bottom pie splice */ + foreach($Slices as $SliceID => $Plots) + { + $Settings = $SliceColors[$SliceID]; $Settings["NoBorder"] = TRUE; + $this->pChartObject->drawPolygon($Plots,$Settings); + + if ( $SecondPass ) + { + $Settings = $SliceColors[$SliceID]; + if ( $Border ) + { $Settings["R"]+= 30; $Settings["G"]+= 30; $Settings["B"]+= 30;; } + + if ( isset($SliceAngle[$SliceID][1]) ) /* Empty error handling */ + { + $Angle = $SliceAngle[$SliceID][1]; + $Xc = cos(($Angle-90)*PI/180) * $Radius + $X; + $Yc = sin(($Angle-90)*PI/180) * $Radius*$SkewFactor + $Y; + $this->pChartObject->drawLine($Plots[0],$Plots[1],$Xc,$Yc,$Settings); + + $Angle = $SliceAngle[$SliceID][count($SliceAngle[$SliceID])-1]; + $Xc = cos(($Angle-90)*PI/180) * $Radius + $X; + $Yc = sin(($Angle-90)*PI/180) * $Radius*$SkewFactor + $Y; + $this->pChartObject->drawLine($Plots[0],$Plots[1],$Xc,$Yc,$Settings); + } + } + } + + /* Draw the two vertical edges */ + $Slices = array_reverse($Slices); + $SliceColors = array_reverse($SliceColors); + foreach($Slices as $SliceID => $Plots) + { + $Settings = $SliceColors[$SliceID]; + $Settings["R"]+= 10; $Settings["G"]+= 10; $Settings["B"]+= 10; $Settings["NoBorder"] = TRUE; + + if ( $Visible[$SliceID]["Start"] && isset($Plots[2])) /* Empty error handling */ + { + $this->pChartObject->drawLine($Plots[2],$Plots[3],$Plots[2],$Plots[3]- $SliceHeight,array("R"=>$Settings["R"],"G"=>$Settings["G"],"B"=>$Settings["B"])); + $Border = ""; + $Border[] = $Plots[0]; $Border[] = $Plots[1]; $Border[] = $Plots[0]; $Border[] = $Plots[1] - $SliceHeight; + $Border[] = $Plots[2]; $Border[] = $Plots[3] - $SliceHeight; $Border[] = $Plots[2]; $Border[] = $Plots[3]; + $this->pChartObject->drawPolygon($Border,$Settings); + } + } + + $Slices = array_reverse($Slices); + $SliceColors = array_reverse($SliceColors); + foreach($Slices as $SliceID => $Plots) + { + $Settings = $SliceColors[$SliceID]; + $Settings["R"]+= 10; $Settings["G"]+= 10; $Settings["B"]+= 10; $Settings["NoBorder"] = TRUE; + if ( $Visible[$SliceID]["End"] ) + { + $this->pChartObject->drawLine($Plots[count($Plots)-2],$Plots[count($Plots)-1],$Plots[count($Plots)-2],$Plots[count($Plots)-1]- $SliceHeight,array("R"=>$Settings["R"],"G"=>$Settings["G"],"B"=>$Settings["B"])); + + $Border = ""; + $Border[] = $Plots[0]; $Border[] = $Plots[1]; $Border[] = $Plots[0]; $Border[] = $Plots[1] - $SliceHeight; + $Border[] = $Plots[count($Plots)-2]; $Border[] = $Plots[count($Plots)-1] - $SliceHeight; $Border[] = $Plots[count($Plots)-2]; $Border[] = $Plots[count($Plots)-1]; + $this->pChartObject->drawPolygon($Border,$Settings); + } + } + + /* Draw the rounded edges */ + foreach($Slices as $SliceID => $Plots) + { + $Settings = $SliceColors[$SliceID]; + $Settings["R"]+= 10; $Settings["G"]+= 10; $Settings["B"]+= 10; $Settings["NoBorder"] = TRUE; + + for ($j=2;$j 90 ) + { + $Border = ""; + $Border[] = $Plots[$j]; $Border[] = $Plots[$j+1]; + $Border[] = $Plots[$j+2]; $Border[] = $Plots[$j+3]; + $Border[] = $Plots[$j+2]; $Border[] = $Plots[$j+3] - $SliceHeight; + $Border[] = $Plots[$j]; $Border[] = $Plots[$j+1] - $SliceHeight; + $this->pChartObject->drawPolygon($Border,$Settings); + } + } + + if ( $SecondPass ) + { + $Settings = $SliceColors[$SliceID]; + if ( $Border ) + { $Settings["R"]+= 30; $Settings["G"]+= 30; $Settings["B"]+= 30; } + + if ( isset($SliceAngle[$SliceID][1]) ) /* Empty error handling */ + { + $Angle = $SliceAngle[$SliceID][1]; + if ( $Angle < 270 && $Angle > 90 ) + { + $Xc = cos(($Angle-90)*PI/180) * $Radius + $X; + $Yc = sin(($Angle-90)*PI/180) * $Radius*$SkewFactor + $Y; + $this->pChartObject->drawLine($Xc,$Yc,$Xc,$Yc-$SliceHeight,$Settings); + } + } + + $Angle = $SliceAngle[$SliceID][count($SliceAngle[$SliceID])-1]; + if ( $Angle < 270 && $Angle > 90 ) + { + $Xc = cos(($Angle-90)*PI/180) * $Radius + $X; + $Yc = sin(($Angle-90)*PI/180) * $Radius*$SkewFactor + $Y; + $this->pChartObject->drawLine($Xc,$Yc,$Xc,$Yc-$SliceHeight,$Settings); + } + + if ( isset($SliceAngle[$SliceID][1]) && $SliceAngle[$SliceID][1] > 270 && $SliceAngle[$SliceID][count($SliceAngle[$SliceID])-1] < 270 ) + { + $Xc = cos((270-90)*PI/180) * $Radius + $X; + $Yc = sin((270-90)*PI/180) * $Radius*$SkewFactor + $Y; + $this->pChartObject->drawLine($Xc,$Yc,$Xc,$Yc-$SliceHeight,$Settings); + } + + if ( isset($SliceAngle[$SliceID][1]) && $SliceAngle[$SliceID][1] > 90 && $SliceAngle[$SliceID][count($SliceAngle[$SliceID])-1] < 90 ) + { + $Xc = cos((0)*PI/180) * $Radius + $X; + $Yc = sin((0)*PI/180) * $Radius*$SkewFactor + $Y; + $this->pChartObject->drawLine($Xc,$Yc,$Xc,$Yc-$SliceHeight,$Settings); + } + + } + } + + /* Draw the top splice */ + foreach($Slices as $SliceID => $Plots) + { + $Settings = $SliceColors[$SliceID]; + $Settings["R"]+= 20; $Settings["G"]+= 20; $Settings["B"]+= 20; + + $Top = ""; + for($j=0;$jpChartObject->drawPolygon($Top,$Settings); + + if ( $RecordImageMap && !$Shadow ) { $this->pChartObject->addToImageMap("POLY",$this->arraySerialize($Top),$this->pChartObject->toHTMLColor($Settings["R"],$Settings["G"],$Settings["B"]),$Data["Series"][$Data["Abscissa"]]["Data"][count($Slices)-$SliceID-1],$Values[$SliceID]); } + } + + + /* Second pass to smooth the angles */ + if ( $SecondPass ) + { + $Step = 360 / (2 * PI * $Radius); + $Offset = 360; $ID = count($Values)-1; + foreach($Values as $Key => $Value) + { + $FirstPoint = TRUE; + if ( $Shadow ) + $Settings = array("R"=>$this->pChartObject->ShadowR,"G"=>$this->pChartObject->ShadowG,"B"=>$this->pChartObject->ShadowB,"Alpha"=>$this->pChartObject->Shadowa); + else + { + if ( $Border ) + { $Settings = array("R"=>$Palette[$ID]["R"]+30,"G"=>$Palette[$ID]["G"]+30,"B"=>$Palette[$ID]["B"]+30,"Alpha"=>$Palette[$ID]["Alpha"]); } + else + $Settings = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]); + } + + $EndAngle = $Offset-($Value*$ScaleFactor); if ( $EndAngle < 0 ) { $EndAngle = 0; } + + if ($DataGapAngle == 0) + { $X0 = $X; $Y0 = $Y- $SliceHeight; } + else + { + $Angle = ($EndAngle - $Offset)/2 + $Offset; + $X0 = cos(($Angle-90)*PI/180) * $DataGapRadius + $X; + $Y0 = sin(($Angle-90)*PI/180) * $DataGapRadius*$SkewFactor + $Y - $SliceHeight; + } + $Plots[] = $X0; $Plots[] = $Y0; + + for($i=$Offset;$i>=$EndAngle;$i=$i-$Step) + { + $Xc = cos(($i-90)*PI/180) * $Radius + $X; + $Yc = sin(($i-90)*PI/180) * $Radius*$SkewFactor + $Y - $SliceHeight; + + if ( $FirstPoint ) { $this->pChartObject->drawLine($Xc,$Yc,$X0,$Y0,$Settings); } { $FirstPoint = FALSE; } + + $this->pChartObject->drawAntialiasPixel($Xc,$Yc,$Settings); + if ($i < 270 && $i > 90 ) { $this->pChartObject->drawAntialiasPixel($Xc,$Yc+$SliceHeight,$Settings); } + } + $this->pChartObject->drawLine($Xc,$Yc,$X0,$Y0,$Settings); + + $Offset = $i - $DataGapAngle; $ID--; + } + } + + if ( $WriteValues != NULL ) + { + $Step = 360 / (2 * PI * $Radius); + $Offset = 360; $ID = count($Values)-1; + $Settings = array("Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"R"=>$ValueR,"G"=>$ValueG,"B"=>$ValueB,"Alpha"=>$ValueAlpha); + foreach($Values as $Key => $Value) + { + $EndAngle = $Offset-($Value*$ScaleFactor); if ( $EndAngle < 0 ) { $EndAngle = 0; } + + $Angle = ($EndAngle - $Offset)/2 + $Offset; + + if ( $ValuePosition == PIE_VALUE_OUTSIDE ) + { + $Xc = cos(($Angle-90)*PI/180) * ($Radius+$ValuePadding) + $X; + $Yc = sin(($Angle-90)*PI/180) * (($Radius*$SkewFactor)+$ValuePadding) + $Y - $SliceHeight; + } + else + { + $Xc = cos(($Angle-90)*PI/180) * ($Radius)/2 + $X; + $Yc = sin(($Angle-90)*PI/180) * ($Radius*$SkewFactor)/2 + $Y - $SliceHeight; + } + + if ( $WriteValues == PIE_VALUE_PERCENTAGE ) + $Display = round(( 100 / $SerieSum ) * $Value,$Precision)."%"; + elseif ( $WriteValues == PIE_VALUE_NATURAL ) + $Display = $Value.$ValueSuffix; + + $this->pChartObject->drawText($Xc,$Yc,$Display,$Settings); + + $Offset = $EndAngle - $DataGapAngle; $ID--; + } + } + + if ( $DrawLabels ) + { + $Step = 360 / (2 * PI * $Radius); + $Offset = 360; $ID = count($Values)-1; + foreach($Values as $Key => $Value) + { + if ( $LabelColor == PIE_LABEL_COLOR_AUTO ) + { $Settings = array("FillR"=>$Palette[$ID]["R"],"FillG"=>$Palette[$ID]["G"],"FillB"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]);} + else + { $Settings = array("FillR"=>$LabelR,"FillG"=>$LabelG,"FillB"=>$LabelB,"Alpha"=>$LabelAlpha); } + + $EndAngle = $Offset-($Value*$ScaleFactor); if ( $EndAngle < 0 ) { $EndAngle = 0; } + + $Angle = ($EndAngle - $Offset)/2 + $Offset; + $Xc = cos(($Angle-90)*PI/180) * $Radius + $X; + $Yc = sin(($Angle-90)*PI/180) * $Radius*$SkewFactor + $Y - $SliceHeight; + + if ( isset($Data["Series"][$Data["Abscissa"]]["Data"][$ID]) ) + { + $Label = $Data["Series"][$Data["Abscissa"]]["Data"][$ID]; + + if ( $LabelStacked ) + $this->writePieLabel($Xc,$Yc,$Label,$Angle,$Settings,TRUE,$X,$Y,$Radius,TRUE); + else + $this->writePieLabel($Xc,$Yc,$Label,$Angle,$Settings,FALSE); + } + + $Offset = $EndAngle - $DataGapAngle; $ID--; + } + } + + if ( $DrawLabels && $LabelStacked ) { $this->writeShiftedLabels(); } + + $this->pChartObject->Shadow = $RestoreShadow; + + return(PIE_RENDERED); + } + + /* Draw the legend of pie chart */ + function drawPieLegend($X,$Y,$Format="") + { + $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->pChartObject->FontName; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->pChartObject->FontSize; + $FontR = isset($Format["FontR"]) ? $Format["FontR"] : $this->pChartObject->FontColorR; + $FontG = isset($Format["FontG"]) ? $Format["FontG"] : $this->pChartObject->FontColorG; + $FontB = isset($Format["FontB"]) ? $Format["FontB"] : $this->pChartObject->FontColorB; + $BoxSize = isset($Format["BoxSize"]) ? $Format["BoxSize"] : 5; + $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5; + $R = isset($Format["R"]) ? $Format["R"] : 200; + $G = isset($Format["G"]) ? $Format["G"] : 200; + $B = isset($Format["B"]) ? $Format["B"] : 200; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; + $Style = isset($Format["Style"]) ? $Format["Style"] : LEGEND_ROUND; + $Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL; + + if ( $Surrounding != NULL ) { $BorderR = $R + $Surrounding; $BorderG = $G + $Surrounding; $BorderB = $B + $Surrounding; } + + $YStep = max($this->pChartObject->FontSize,$BoxSize) + 5; + $XStep = $BoxSize + 5; + + /* Data Processing */ + $Data = $this->pDataObject->getData(); + $Palette = $this->pDataObject->getPalette(); + + /* Do we have an abscissa serie defined? */ + if ( $Data["Abscissa"] == "" ) { return(PIE_NO_ABSCISSA); } + + $Boundaries = ""; $Boundaries["L"] = $X; $Boundaries["T"] = $Y; $Boundaries["R"] = 0; $Boundaries["B"] = 0; $vY = $Y; $vX = $X; + foreach($Data["Series"][$Data["Abscissa"]]["Data"] as $Key => $Value) + { + $BoxArray = $this->pChartObject->getTextBox($vX+$BoxSize+4,$vY+$BoxSize/2,$FontName,$FontSize,0,$Value); + + if ( $Mode == LEGEND_VERTICAL ) + { + if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$BoxSize/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$BoxSize/2; } + if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; } + if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$BoxSize/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$BoxSize/2; } + $vY=$vY+$YStep; + } + elseif ( $Mode == LEGEND_HORIZONTAL ) + { + if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$BoxSize/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$BoxSize/2; } + if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; } + if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$BoxSize/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$BoxSize/2; } + $vX=$Boundaries["R"]+$XStep; + } + } + $vY=$vY-$YStep; $vX=$vX-$XStep; + + $TopOffset = $Y - $Boundaries["T"]; + if ( $Boundaries["B"]-($vY+$BoxSize) < $TopOffset ) { $Boundaries["B"] = $vY+$BoxSize+$TopOffset; } + + if ( $Style == LEGEND_ROUND ) + $this->pChartObject->drawRoundedFilledRectangle($Boundaries["L"]-$Margin,$Boundaries["T"]-$Margin,$Boundaries["R"]+$Margin,$Boundaries["B"]+$Margin,$Margin,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB)); + elseif ( $Style == LEGEND_BOX ) + $this->pChartObject->drawFilledRectangle($Boundaries["L"]-$Margin,$Boundaries["T"]-$Margin,$Boundaries["R"]+$Margin,$Boundaries["B"]+$Margin,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB)); + + $RestoreShadow = $this->pChartObject->Shadow; $this->pChartObject->Shadow = FALSE; + foreach($Data["Series"][$Data["Abscissa"]]["Data"] as $Key => $Value) + { + $R = $Palette[$Key]["R"]; $G = $Palette[$Key]["G"]; $B = $Palette[$Key]["B"]; + + $this->pChartObject->drawFilledRectangle($X+1,$Y+1,$X+$BoxSize+1,$Y+$BoxSize+1,array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20)); + $this->pChartObject->drawFilledRectangle($X,$Y,$X+$BoxSize,$Y+$BoxSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Surrounding"=>20)); + if ( $Mode == LEGEND_VERTICAL ) + { + $this->pChartObject->drawText($X+$BoxSize+4,$Y+$BoxSize/2,$Value,array("R"=>$FontR,"G"=>$FontG,"B"=>$FontB,"Align"=>TEXT_ALIGN_MIDDLELEFT,"FontName"=>$FontName,"FontSize"=>$FontSize)); + $Y=$Y+$YStep; + } + elseif ( $Mode == LEGEND_HORIZONTAL ) + { + $BoxArray = $this->pChartObject->drawText($X+$BoxSize+4,$Y+$BoxSize/2,$Value,array("R"=>$FontR,"G"=>$FontG,"B"=>$FontB,"Align"=>TEXT_ALIGN_MIDDLELEFT,"FontName"=>$FontName,"FontSize"=>$FontSize)); + $X=$BoxArray[1]["X"]+2+$XStep; + } + } + + $this->Shadow = $RestoreShadow; + } + + /* Set the color of the specified slice */ + function setSliceColor($SliceID,$Format="") + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + + $this->pDataObject->Palette[$SliceID]["R"] = $R; + $this->pDataObject->Palette[$SliceID]["G"] = $G; + $this->pDataObject->Palette[$SliceID]["B"] = $B; + $this->pDataObject->Palette[$SliceID]["Alpha"] = $Alpha; + } + + /* Internally used compute the label positions */ + function writePieLabel($X,$Y,$Label,$Angle,$Settings,$Stacked,$Xc=0,$Yc=0,$Radius=0,$Reversed=FALSE) + { + $LabelOffset = 30; + $FontName = $this->pChartObject->FontName; + $FontSize = $this->pChartObject->FontSize; + + if ( !$Stacked ) + { + $Settings["Angle"] = 360-$Angle; + $Settings["Length"] = 25; + $Settings["Size"] = 8; + + $this->pChartObject->drawArrowLabel($X,$Y," ".$Label." ",$Settings); + } + else + { + $X2 = cos(deg2rad($Angle-90))*20+$X; + $Y2 = sin(deg2rad($Angle-90))*20+$Y; + + $TxtPos = $this->pChartObject->getTextBox($X,$Y,$FontName,$FontSize,0,$Label); + $Height = $TxtPos[0]["Y"] - $TxtPos[2]["Y"]; + $YTop = $Y2 - $Height/2 - 2; + $YBottom = $Y2 + $Height/2 + 2; + + if ( $this->LabelPos != "" ) + { + $Done = FALSE; + foreach($this->LabelPos as $Key => $Settings) + { + if ( !$Done ) + { + if ( $Angle <= 90 && (($YTop >= $Settings["YTop"] && $YTop <= $Settings["YBottom"]) || ($YBottom >= $Settings["YTop"] && $YBottom <= $Settings["YBottom"]))) + { $this->shift(0,180,-($Height+2),$Reversed); $Done = TRUE; } + if ( $Angle > 90 && $Angle <= 180 && (($YTop >= $Settings["YTop"] && $YTop <= $Settings["YBottom"]) || ($YBottom >= $Settings["YTop"] && $YBottom <= $Settings["YBottom"]))) + { $this->shift(0,180,-($Height+2),$Reversed); $Done = TRUE; } + if ( $Angle > 180 && $Angle <= 270 && (($YTop >= $Settings["YTop"] && $YTop <= $Settings["YBottom"]) || ($YBottom >= $Settings["YTop"] && $YBottom <= $Settings["YBottom"]))) + { $this->shift(180,360,($Height+2),$Reversed); $Done = TRUE; } + if ( $Angle > 270 && $Angle <= 360 && (($YTop >= $Settings["YTop"] && $YTop <= $Settings["YBottom"]) || ($YBottom >= $Settings["YTop"] && $YBottom <= $Settings["YBottom"]))) + { $this->shift(180,360,($Height+2),$Reversed); $Done = TRUE; } + } + } + } + + $LabelSettings = array("YTop"=>$YTop,"YBottom"=>$YBottom,"Label"=>$Label,"Angle"=>$Angle,"X1"=>$X,"Y1"=>$Y,"X2"=>$X2,"Y2"=>$Y2); + if ( $Angle <= 180 ) { $LabelSettings["X3"] = $Xc+$Radius+$LabelOffset; } + if ( $Angle > 180 ) { $LabelSettings["X3"] = $Xc-$Radius-$LabelOffset; } + $this->LabelPos[] = $LabelSettings; + } + } + + /* Internally used to shift label positions */ + function shift($StartAngle,$EndAngle,$Offset,$Reversed) + { + if ( $Reversed ) { $Offset = -$Offset; } + foreach($this->LabelPos as $Key => $Settings) + { + if ( $Settings["Angle"] > $StartAngle && $Settings["Angle"] <= $EndAngle ) { $this->LabelPos[$Key]["YTop"] = $Settings["YTop"] + $Offset; $this->LabelPos[$Key]["YBottom"] = $Settings["YBottom"] + $Offset; $this->LabelPos[$Key]["Y2"] = $Settings["Y2"] + $Offset; } + } + } + + /* Internally used to write the re-computed labels */ + function writeShiftedLabels() + { + if ( $this->LabelPos == "" ) { return(0); } + foreach($this->LabelPos as $Key => $Settings) + { + $X1 = $Settings["X1"]; $Y1 = $Settings["Y1"]; + $X2 = $Settings["X2"]; $Y2 = $Settings["Y2"]; + $X3 = $Settings["X3"]; + $Angle = $Settings["Angle"]; + $Label = $Settings["Label"]; + + $this->pChartObject->drawArrow($X2,$Y2,$X1,$Y1,array("Size"=>8)); + if ( $Angle <= 180 ) + { + $this->pChartObject->drawLine($X2,$Y2,$X3,$Y2); + $this->pChartObject->drawText($X3+2,$Y2,$Label,array("Align"=>TEXT_ALIGN_MIDDLELEFT)); + } + else + { + $this->pChartObject->drawLine($X2,$Y2,$X3,$Y2); + $this->pChartObject->drawText($X3-2,$Y2,$Label,array("Align"=>TEXT_ALIGN_MIDDLERIGHT)); + } + } + } + + /* Draw a ring chart */ + function draw2DRing($X,$Y,$Format="") + { + $OuterRadius = isset($Format["Radius"]) ? $Format["Radius"] : 60; + $Precision = isset($Format["Precision"]) ? $Format["Precision"] : 0; + $InnerRadius = isset($Format["Radius"]) ? $Format["Radius"] : 30; + $Border = isset($Format["Border"]) ? $Format["Border"] : FALSE; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 100; + $Shadow = isset($Format["Shadow"]) ? $Format["Shadow"] : FALSE; + $DrawLabels = isset($Format["DrawLabels"]) ? $Format["DrawLabels"] : FALSE; + $LabelStacked = isset($Format["LabelStacked"]) ? $Format["LabelStacked"] : FALSE; + $LabelColor = isset($Format["LabelColor"]) ? $Format["LabelColor"] : PIE_LABEL_COLOR_MANUAL; + $LabelR = isset($Format["LabelR"]) ? $Format["LabelR"] : 0; + $LabelG = isset($Format["LabelG"]) ? $Format["LabelG"] : 0; + $LabelB = isset($Format["LabelB"]) ? $Format["LabelB"] : 0; + $LabelAlpha = isset($Format["LabelAlpha"]) ? $Format["LabelAlpha"] : 100; + $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : NULL; //PIE_VALUE_PERCENTAGE + $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 5; + $ValuePosition = isset($Format["ValuePosition"]) ? $Format["ValuePosition"] : PIE_VALUE_OUTSIDE; + $ValueSuffix = isset($Format["ValueSuffix"]) ? $Format["ValueSuffix"] : ""; + $ValueR = isset($Format["ValueR"]) ? $Format["ValueR"] : 255; + $ValueG = isset($Format["ValueG"]) ? $Format["ValueG"] : 255; + $ValueB = isset($Format["ValueB"]) ? $Format["ValueB"] : 255; + $ValueAlpha = isset($Format["ValueAlpha"]) ? $Format["ValueAlpha"] : 100; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; + + /* Data Processing */ + $Data = $this->pDataObject->getData(); + $Palette = $this->pDataObject->getPalette(); + + /* Do we have an abscissa serie defined? */ + if ( $Data["Abscissa"] == "" ) { return(PIE_NO_ABSCISSA); } + + /* Try to find the data serie */ + $DataSerie = ""; + foreach ($Data["Series"] as $SerieName => $SerieData) + { if ( $SerieName != $Data["Abscissa"]) { $DataSerie = $SerieName; } } + + /* Do we have data to compute? */ + if ( $DataSerie == "" ) { return(PIE_NO_DATASERIE); } + + /* Remove unused data */ + list($Data,$Palette) = $this->clean0Values($Data,$Palette,$DataSerie,$Data["Abscissa"]); + + /* Compute the pie sum */ + $SerieSum = $this->pDataObject->getSum($DataSerie); + + /* Do we have data to draw? */ + if ( $SerieSum == 0 ) { return(PIE_SUMISNULL); } + + /* Dump the real number of data to draw */ + $Values = ""; + foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) + { if ($Value != 0) { $Values[] = $Value; } } + + /* Compute the wasted angular space between series */ + if (count($Values)==1) { $WastedAngular = 0; } else { $WastedAngular = 0; } // count($Values) + + /* Compute the scale */ + $ScaleFactor = (360 - $WastedAngular) / $SerieSum; + + $RestoreShadow = $this->pChartObject->Shadow; + if ( $this->pChartObject->Shadow ) + { + $this->pChartObject->Shadow = FALSE; + + $ShadowFormat = $Format; $ShadowFormat["Shadow"] = TRUE; + $this->draw2DRing($X+$this->pChartObject->ShadowX,$Y+$this->pChartObject->ShadowY,$ShadowFormat); + } + + /* Draw the polygon pie elements */ + $Step = 360 / (2 * PI * $OuterRadius); + $Offset = 0; $ID = 0; + foreach($Values as $Key => $Value) + { + if ( $Shadow ) + { + $Settings = array("R"=>$this->pChartObject->ShadowR,"G"=>$this->pChartObject->ShadowG,"B"=>$this->pChartObject->ShadowB,"Alpha"=>$this->pChartObject->Shadowa); + $BorderColor = $Settings; + } + else + { + if ( !isset($Palette[$ID]["R"]) ) { $Color = $this->pChartObject->getRandomColor(); $Palette[$ID] = $Color; $this->pDataObject->savePalette($ID,$Color); } + $Settings = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]); + + if ( $Border ) + $BorderColor = array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha); + else + $BorderColor = $Settings; + } + + $Plots = ""; $Boundaries = ""; $AAPixels = ""; + $EndAngle = $Offset+($Value*$ScaleFactor); if ( $EndAngle > 360 ) { $EndAngle = 360; } + for($i=$Offset;$i<=$EndAngle;$i=$i+$Step) + { + $Xc = cos(($i-90)*PI/180) * $OuterRadius + $X; + $Yc = sin(($i-90)*PI/180) * $OuterRadius + $Y; + + if ( !isset($Boundaries[0]["X1"]) ) { $Boundaries[0]["X1"] = $Xc; $Boundaries[0]["Y1"] = $Yc; } + $AAPixels[] = array($Xc,$Yc); + + if ( $i<90 ) { $Yc++; } + if ( $i>180 && $i<270 ) { $Xc++; } + if ( $i>=270 ) { $Xc++; $Yc++; } + + $Plots[] = $Xc; $Plots[] = $Yc; + } + $Boundaries[1]["X1"] = $Xc; $Boundaries[1]["Y1"] = $Yc; + $Lasti = $EndAngle; + + for($i=$EndAngle;$i>=$Offset;$i=$i-$Step) + { + $Xc = cos(($i-90)*PI/180) * ($InnerRadius-1) + $X; + $Yc = sin(($i-90)*PI/180) * ($InnerRadius-1) + $Y; + + if ( !isset($Boundaries[1]["X2"]) ) { $Boundaries[1]["X2"] = $Xc; $Boundaries[1]["Y2"] = $Yc; } + $AAPixels[] = array($Xc,$Yc); + + $Xc = cos(($i-90)*PI/180) * $InnerRadius + $X; + $Yc = sin(($i-90)*PI/180) * $InnerRadius + $Y; + + if ( $i<90 ) { $Yc++; } + if ( $i>180 && $i<270 ) { $Xc++; } + if ( $i>=270 ) { $Xc++; $Yc++; } + + $Plots[] = $Xc; $Plots[] = $Yc; + } + $Boundaries[0]["X2"] = $Xc; $Boundaries[0]["Y2"] = $Yc; + + /* Draw the polygon */ + $this->pChartObject->drawPolygon($Plots,$Settings); + if ( $RecordImageMap && !$Shadow ) { $this->pChartObject->addToImageMap("POLY",$this->arraySerialize($Plots),$this->pChartObject->toHTMLColor($Palette[$ID]["R"],$Palette[$ID]["G"],$Palette[$ID]["B"]),$Data["Series"][$Data["Abscissa"]]["Data"][$Key],$Value); } + + /* Smooth the edges using AA */ + foreach($AAPixels as $iKey => $Pos ) { $this->pChartObject->drawAntialiasPixel($Pos[0],$Pos[1],$BorderColor); } + $this->pChartObject->drawLine($Boundaries[0]["X1"],$Boundaries[0]["Y1"],$Boundaries[0]["X2"],$Boundaries[0]["Y2"],$BorderColor); + $this->pChartObject->drawLine($Boundaries[1]["X1"],$Boundaries[1]["Y1"],$Boundaries[1]["X2"],$Boundaries[1]["Y2"],$BorderColor); + + if ( $DrawLabels && !$Shadow ) + { + if ( $LabelColor == PIE_LABEL_COLOR_AUTO ) + { $Settings = array("FillR"=>$Palette[$ID]["R"],"FillG"=>$Palette[$ID]["G"],"FillB"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]);} + else + { $Settings = array("FillR"=>$LabelR,"FillG"=>$LabelG,"FillB"=>$LabelB,"Alpha"=>$LabelAlpha); } + + $Angle = ($EndAngle - $Offset)/2 + $Offset; + $Xc = cos(($Angle-90)*PI/180) * $OuterRadius + $X; + $Yc = sin(($Angle-90)*PI/180) * $OuterRadius + $Y; + + $Label = $Data["Series"][$Data["Abscissa"]]["Data"][$Key]; + + if ( $LabelStacked ) + $this->writePieLabel($Xc,$Yc,$Label,$Angle,$Settings,TRUE,$X,$Y,$OuterRadius); + else + $this->writePieLabel($Xc,$Yc,$Label,$Angle,$Settings,FALSE); + } + + $Offset = $Lasti; $ID++; + } + + if ( $DrawLabels && $LabelStacked ) { $this->writeShiftedLabels(); } + + if ( $WriteValues && !$Shadow ) + { + $Step = 360 / (2 * PI * $OuterRadius); + $Offset = 0; + foreach($Values as $Key => $Value) + { + $EndAngle = $Offset+($Value*$ScaleFactor); + if ( $EndAngle > 360 ) { $EndAngle = 360; } + + $Angle = $Offset+($Value*$ScaleFactor)/2; + if ( $ValuePosition == PIE_VALUE_OUTSIDE ) + { + $Xc = cos(($Angle-90)*PI/180) * ($OuterRadius+$ValuePadding) + $X; + $Yc = sin(($Angle-90)*PI/180) * ($OuterRadius+$ValuePadding) + $Y; + if ( $Angle >=0 && $Angle <= 90 ) { $Align = TEXT_ALIGN_BOTTOMLEFT; } + if ( $Angle > 90 && $Angle <= 180 ) { $Align = TEXT_ALIGN_TOPLEFT; } + if ( $Angle > 180 && $Angle <= 270 ) { $Align = TEXT_ALIGN_TOPRIGHT; } + if ( $Angle > 270 ) { $Align = TEXT_ALIGN_BOTTOMRIGHT; } + } + else + { + $Xc = cos(($Angle-90)*PI/180) * (($OuterRadius-$InnerRadius)/2+$InnerRadius) + $X; + $Yc = sin(($Angle-90)*PI/180) * (($OuterRadius-$InnerRadius)/2+$InnerRadius) + $Y; + $Align = TEXT_ALIGN_MIDDLEMIDDLE; + } + + if ( $WriteValues == PIE_VALUE_PERCENTAGE ) + $Display = round(( 100 / $SerieSum ) * $Value,$Precision)."%"; + elseif ( $WriteValues == PIE_VALUE_NATURAL ) + $Display = $Value.$ValueSuffix; + else + $Label = ""; + + $this->pChartObject->drawText($Xc,$Yc,$Display,array("Align"=>$Align,"R"=>$ValueR,"G"=>$ValueG,"B"=>$ValueB)); + $Offset = $EndAngle; + } + } + + $this->pChartObject->Shadow = $RestoreShadow; + + return(PIE_RENDERED); + } + + /* Draw a 3D ring chart */ + function draw3DRing($X,$Y,$Format="") + { + $OuterRadius = isset($Format["OuterRadius"]) ? $Format["OuterRadius"] : 100; + $Precision = isset($Format["Precision"]) ? $Format["Precision"] : 0; + $InnerRadius = isset($Format["InnerRadius"]) ? $Format["InnerRadius"] : 30; + $SkewFactor = isset($Format["SkewFactor"]) ? $Format["SkewFactor"] : .6; + $SliceHeight = isset($Format["SliceHeight"]) ? $Format["SliceHeight"] : 10; + $DataGapAngle = isset($Format["DataGapAngle"]) ? $Format["DataGapAngle"] : 10; + $DataGapRadius = isset($Format["DataGapRadius"]) ? $Format["DataGapRadius"] : 10; + $Border = isset($Format["Border"]) ? $Format["Border"] : FALSE; + $Shadow = isset($Format["Shadow"]) ? $Format["Shadow"] : FALSE; + $DrawLabels = isset($Format["DrawLabels"]) ? $Format["DrawLabels"] : FALSE; + $LabelStacked = isset($Format["LabelStacked"]) ? $Format["LabelStacked"] : FALSE; + $LabelColor = isset($Format["LabelColor"]) ? $Format["LabelColor"] : PIE_LABEL_COLOR_MANUAL; + $LabelR = isset($Format["LabelR"]) ? $Format["LabelR"] : 0; + $LabelG = isset($Format["LabelG"]) ? $Format["LabelG"] : 0; + $LabelB = isset($Format["LabelB"]) ? $Format["LabelB"] : 0; + $LabelAlpha = isset($Format["LabelAlpha"]) ? $Format["LabelAlpha"] : 100; + $Cf = isset($Format["Cf"]) ? $Format["Cf"] : 20; + $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : PIE_VALUE_NATURAL; + $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : $SliceHeight + 15; + $ValuePosition = isset($Format["ValuePosition"]) ? $Format["ValuePosition"] : PIE_VALUE_OUTSIDE; + $ValueSuffix = isset($Format["ValueSuffix"]) ? $Format["ValueSuffix"] : ""; + $ValueR = isset($Format["ValueR"]) ? $Format["ValueR"] : 255; + $ValueG = isset($Format["ValueG"]) ? $Format["ValueG"] : 255; + $ValueB = isset($Format["ValueB"]) ? $Format["ValueB"] : 255; + $ValueAlpha = isset($Format["ValueAlpha"]) ? $Format["ValueAlpha"] : 100; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; + + /* Error correction for overlaying rounded corners */ + if ( $SkewFactor < .5 ) { $SkewFactor = .5; } + + /* Data Processing */ + $Data = $this->pDataObject->getData(); + $Palette = $this->pDataObject->getPalette(); + + /* Do we have an abscissa serie defined? */ + if ( $Data["Abscissa"] == "" ) { return(PIE_NO_ABSCISSA); } + + /* Try to find the data serie */ + $DataSerie = ""; + foreach ($Data["Series"] as $SerieName => $SerieData) + { if ( $SerieName != $Data["Abscissa"]) { $DataSerie = $SerieName; } } + + /* Do we have data to compute? */ + if ( $DataSerie == "" ) { return(PIE_NO_DATASERIE); } + + /* Remove unused data */ + list($Data,$Palette) = $this->clean0Values($Data,$Palette,$DataSerie,$Data["Abscissa"]); + + /* Compute the pie sum */ + $SerieSum = $this->pDataObject->getSum($DataSerie); + + /* Do we have data to draw? */ + if ( $SerieSum == 0 ) { return(PIE_SUMISNULL); } + + /* Dump the real number of data to draw */ + $Values = ""; + foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) + { if ($Value != 0) { $Values[] = $Value; } } + + /* Compute the wasted angular space between series */ + if (count($Values)==1) { $WastedAngular = 0; } else { $WastedAngular = count($Values) * $DataGapAngle; } + + /* Compute the scale */ + $ScaleFactor = (360 - $WastedAngular) / $SerieSum; + + $RestoreShadow = $this->pChartObject->Shadow; + if ( $this->pChartObject->Shadow ) { $this->pChartObject->Shadow = FALSE; } + + /* Draw the polygon ring elements */ + $Offset = 360; $ID = count($Values)-1; + $Values = array_reverse($Values); + $Slice = 0; $Slices = ""; $SliceColors = ""; $Visible = ""; $SliceAngle = ""; + foreach($Values as $Key => $Value) + { + if ( !isset($Palette[$ID]["R"]) ) { $Color = $this->pChartObject->getRandomColor(); $Palette[$ID] = $Color; $this->pDataObject->savePalette($ID,$Color); } + $Settings = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]); + + $SliceColors[$Slice] = $Settings; + + $StartAngle = $Offset; + $EndAngle = $Offset-($Value*$ScaleFactor); if ( $EndAngle < 0 ) { $EndAngle = 0; } + + if ( $StartAngle > 180 ) { $Visible[$Slice]["Start"] = TRUE; } else { $Visible[$Slice]["Start"] = TRUE; } + if ( $EndAngle < 180 ) { $Visible[$Slice]["End"] = FALSE; } else { $Visible[$Slice]["End"] = TRUE; } + + $Step = (360 / (2 * PI * $OuterRadius))/2; + $OutX1 = VOID; $OutY1 = VOID; + for($i=$Offset;$i>=$EndAngle;$i=$i-$Step) + { + $Xc = cos(($i-90)*PI/180) * ($OuterRadius+$DataGapRadius-2) + $X; + $Yc = sin(($i-90)*PI/180) * ($OuterRadius+$DataGapRadius-2)*$SkewFactor + $Y; + $Slices[$Slice]["AA"][] = array($Xc,$Yc); + + $Xc = cos(($i-90)*PI/180) * ($OuterRadius+$DataGapRadius-1) + $X; + $Yc = sin(($i-90)*PI/180) * ($OuterRadius+$DataGapRadius-1)*$SkewFactor + $Y; + $Slices[$Slice]["AA"][] = array($Xc,$Yc); + + $Xc = cos(($i-90)*PI/180) * ($OuterRadius+$DataGapRadius) + $X; + $Yc = sin(($i-90)*PI/180) * ($OuterRadius+$DataGapRadius)*$SkewFactor + $Y; + $this->pChartObject->drawAntialiasPixel($Xc,$Yc,$Settings); + + if ( $OutX1 == VOID ) { $OutX1 = $Xc; $OutY1 = $Yc; } + + if ( $i<90 ) { $Yc++; } + if ( $i>90 && $i<180 ) { $Xc++; } + if ( $i>180 && $i<270 ) { $Xc++; } + if ( $i>=270 ) { $Xc++; $Yc++; } + + $Slices[$Slice]["BottomPoly"][] = floor($Xc); $Slices[$Slice]["BottomPoly"][] = floor($Yc); + $Slices[$Slice]["TopPoly"][] = floor($Xc); $Slices[$Slice]["TopPoly"][] = floor($Yc)-$SliceHeight; + $Slices[$Slice]["Angle"][] = $i; + } + $OutX2 = $Xc; $OutY2 = $Yc; + + $Slices[$Slice]["Angle"][] = VOID; + $Lasti = $i; + + $Step = (360 / (2 * PI * $InnerRadius))/2; + $InX1 = VOID; $InY1 = VOID; + for($i=$EndAngle;$i<=$Offset;$i=$i+$Step) + { + $Xc = cos(($i-90)*PI/180) * ($InnerRadius+$DataGapRadius-1) + $X; + $Yc = sin(($i-90)*PI/180) * ($InnerRadius+$DataGapRadius-1)*$SkewFactor + $Y; + $Slices[$Slice]["AA"][] = array($Xc,$Yc); + + $Xc = cos(($i-90)*PI/180) * ($InnerRadius+$DataGapRadius) + $X; + $Yc = sin(($i-90)*PI/180) * ($InnerRadius+$DataGapRadius)*$SkewFactor + $Y; + $Slices[$Slice]["AA"][] = array($Xc,$Yc); + + if ( $InX1 == VOID ) { $InX1 = $Xc; $InY1 = $Yc; } + + if ( $i<90 ) { $Yc++; } + if ( $i>90 && $i<180 ) { $Xc++; } + if ( $i>180 && $i<270 ) { $Xc++; } + if ( $i>=270 ) { $Xc++; $Yc++; } + + $Slices[$Slice]["BottomPoly"][] = floor($Xc); $Slices[$Slice]["BottomPoly"][] = floor($Yc); + $Slices[$Slice]["TopPoly"][] = floor($Xc); $Slices[$Slice]["TopPoly"][] = floor($Yc)-$SliceHeight; + $Slices[$Slice]["Angle"][] = $i; + } + $InX2 = $Xc; $InY2 = $Yc; + + $Slices[$Slice]["InX1"] = $InX1; $Slices[$Slice]["InY1"] = $InY1; + $Slices[$Slice]["InX2"] = $InX2; $Slices[$Slice]["InY2"] = $InY2; + $Slices[$Slice]["OutX1"] = $OutX1; $Slices[$Slice]["OutY1"] = $OutY1; + $Slices[$Slice]["OutX2"] = $OutX2; $Slices[$Slice]["OutY2"] = $OutY2; + + $Offset = $Lasti - $DataGapAngle; $ID--; $Slice++; + } + + /* Draw the bottom pie splice */ + foreach($Slices as $SliceID => $Plots) + { + $Settings = $SliceColors[$SliceID]; $Settings["NoBorder"] = TRUE; + $this->pChartObject->drawPolygon($Plots["BottomPoly"],$Settings); + + foreach($Plots["AA"] as $Key => $Pos) + $this->pChartObject->drawAntialiasPixel($Pos[0],$Pos[1],$Settings); + + $this->pChartObject->drawLine($Plots["InX1"],$Plots["InY1"],$Plots["OutX2"],$Plots["OutY2"],$Settings); + $this->pChartObject->drawLine($Plots["InX2"],$Plots["InY2"],$Plots["OutX1"],$Plots["OutY1"],$Settings); + } + + $Slices = array_reverse($Slices); + $SliceColors = array_reverse($SliceColors); + + /* Draw the vertical edges (semi-visible) */ + foreach($Slices as $SliceID => $Plots) + { + $Settings = $SliceColors[$SliceID]; $Settings["NoBorder"] = TRUE; + $Settings["R"] = $Settings["R"]+$Cf; $Settings["G"] = $Settings["G"]+$Cf; $Settings["B"] = $Settings["B"]+$Cf; + + $StartAngle = $Plots["Angle"][0]; + foreach($Plots["Angle"] as $Key =>$Angle) { if ($Angle == VOID) { $EndAngle = $Plots["Angle"][$Key-1]; } } + + if ( $StartAngle >= 270 || $StartAngle <= 90 ) + $this->pChartObject->drawLine($Plots["OutX1"],$Plots["OutY1"],$Plots["OutX1"],$Plots["OutY1"]-$SliceHeight,$Settings); + if ( $StartAngle >= 270 || $StartAngle <= 90 ) + $this->pChartObject->drawLine($Plots["OutX2"],$Plots["OutY2"],$Plots["OutX2"],$Plots["OutY2"]-$SliceHeight,$Settings); + + $this->pChartObject->drawLine($Plots["InX1"],$Plots["InY1"],$Plots["InX1"],$Plots["InY1"]-$SliceHeight,$Settings); + $this->pChartObject->drawLine($Plots["InX2"],$Plots["InY2"],$Plots["InX2"],$Plots["InY2"]-$SliceHeight,$Settings); + } + + /* Draw the inner vertical slices */ + foreach($Slices as $SliceID => $Plots) + { + $Settings = $SliceColors[$SliceID]; $Settings["NoBorder"] = TRUE; + $Settings["R"] = $Settings["R"]+$Cf; $Settings["G"] = $Settings["G"]+$Cf; $Settings["B"] = $Settings["B"]+$Cf; + + $Outer = TRUE; $Inner = FALSE; + $InnerPlotsA = ""; $InnerPlotsB = ""; + foreach($Plots["Angle"] as $ID => $Angle) + { + if ( $Angle == VOID ) + { $Outer = FALSE; $Inner = TRUE; } + elseif( $Inner ) + { + if (( $Angle < 90 || $Angle > 270 ) && isset($Plots["BottomPoly"][$ID*2]) ) + { + $Xo = $Plots["BottomPoly"][$ID*2]; + $Yo = $Plots["BottomPoly"][$ID*2+1]; + + $InnerPlotsA[] = $Xo; $InnerPlotsA[] = $Yo; + $InnerPlotsB[] = $Xo; $InnerPlotsB[] = $Yo-$SliceHeight; + } + } + } + + if ( $InnerPlotsA != "" ) + { $InnerPlots = array_merge($InnerPlotsA,$this->arrayReverse($InnerPlotsB)); $this->pChartObject->drawPolygon($InnerPlots,$Settings); } + } + + /* Draw the splice top and left poly */ + foreach($Slices as $SliceID => $Plots) + { + $Settings = $SliceColors[$SliceID]; $Settings["NoBorder"] = TRUE; + $Settings["R"] = $Settings["R"]+$Cf*1.5; $Settings["G"] = $Settings["G"]+$Cf*1.5; $Settings["B"] = $Settings["B"]+$Cf*1.5; + + $StartAngle = $Plots["Angle"][0]; + foreach($Plots["Angle"] as $Key =>$Angle) { if ($Angle == VOID) { $EndAngle = $Plots["Angle"][$Key-1]; } } + + if ( $StartAngle < 180 ) + { + $Points = ""; + $Points[] = $Plots["InX2"]; + $Points[] = $Plots["InY2"]; + $Points[] = $Plots["InX2"]; + $Points[] = $Plots["InY2"]-$SliceHeight; + $Points[] = $Plots["OutX1"]; + $Points[] = $Plots["OutY1"]-$SliceHeight; + $Points[] = $Plots["OutX1"]; + $Points[] = $Plots["OutY1"]; + + $this->pChartObject->drawPolygon($Points,$Settings); + } + + if ( $EndAngle > 180 ) + { + $Points = ""; + $Points[] = $Plots["InX1"]; + $Points[] = $Plots["InY1"]; + $Points[] = $Plots["InX1"]; + $Points[] = $Plots["InY1"]-$SliceHeight; + $Points[] = $Plots["OutX2"]; + $Points[] = $Plots["OutY2"]-$SliceHeight; + $Points[] = $Plots["OutX2"]; + $Points[] = $Plots["OutY2"]; + + $this->pChartObject->drawPolygon($Points,$Settings); + } + } + + + /* Draw the vertical edges (visible) */ + foreach($Slices as $SliceID => $Plots) + { + $Settings = $SliceColors[$SliceID]; $Settings["NoBorder"] = TRUE; + $Settings["R"] = $Settings["R"]+$Cf; $Settings["G"] = $Settings["G"]+$Cf; $Settings["B"] = $Settings["B"]+$Cf; + + $StartAngle = $Plots["Angle"][0]; + foreach($Plots["Angle"] as $Key =>$Angle) { if ($Angle == VOID) { $EndAngle = $Plots["Angle"][$Key-1]; } } + + if ( $StartAngle <= 270 && $StartAngle >= 90 ) + $this->pChartObject->drawLine($Plots["OutX1"],$Plots["OutY1"],$Plots["OutX1"],$Plots["OutY1"]-$SliceHeight,$Settings); + if ( $EndAngle <= 270 && $EndAngle >= 90 ) + $this->pChartObject->drawLine($Plots["OutX2"],$Plots["OutY2"],$Plots["OutX2"],$Plots["OutY2"]-$SliceHeight,$Settings); + } + + + /* Draw the outer vertical slices */ + foreach($Slices as $SliceID => $Plots) + { + $Settings = $SliceColors[$SliceID]; $Settings["NoBorder"] = TRUE; + $Settings["R"] = $Settings["R"]+$Cf; $Settings["G"] = $Settings["G"]+$Cf; $Settings["B"] = $Settings["B"]+$Cf; + + $Outer = TRUE; $Inner = FALSE; + $OuterPlotsA = ""; $OuterPlotsB = ""; $InnerPlotsA = ""; $InnerPlotsB = ""; + foreach($Plots["Angle"] as $ID => $Angle) + { + if ( $Angle == VOID ) + { $Outer = FALSE; $Inner = TRUE; } + elseif( $Outer ) + { + if ( ( $Angle > 90 && $Angle < 270 ) && isset($Plots["BottomPoly"][$ID*2]) ) + { + $Xo = $Plots["BottomPoly"][$ID*2]; + $Yo = $Plots["BottomPoly"][$ID*2+1]; + + $OuterPlotsA[] = $Xo; $OuterPlotsA[] = $Yo; + $OuterPlotsB[] = $Xo; $OuterPlotsB[] = $Yo-$SliceHeight; + } + } + } + if ( $OuterPlotsA != "" ) + { $OuterPlots = array_merge($OuterPlotsA,$this->arrayReverse($OuterPlotsB)); $this->pChartObject->drawPolygon($OuterPlots,$Settings); } + } + + $Slices = array_reverse($Slices); + $SliceColors = array_reverse($SliceColors); + + + /* Draw the top pie splice */ + foreach($Slices as $SliceID => $Plots) + { + $Settings = $SliceColors[$SliceID]; $Settings["NoBorder"] = TRUE; + $Settings["R"] = $Settings["R"]+$Cf*2; $Settings["G"] = $Settings["G"]+$Cf*2; $Settings["B"] = $Settings["B"]+$Cf*2; + + $this->pChartObject->drawPolygon($Plots["TopPoly"],$Settings); + + if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("POLY",$this->arraySerialize($Plots["TopPoly"]),$this->pChartObject->toHTMLColor($Settings["R"],$Settings["G"],$Settings["B"]),$Data["Series"][$Data["Abscissa"]]["Data"][$SliceID],$Data["Series"][$DataSerie]["Data"][count($Slices)-$SliceID-1]); } + + foreach($Plots["AA"] as $Key => $Pos) + $this->pChartObject->drawAntialiasPixel($Pos[0],$Pos[1]-$SliceHeight,$Settings); + + $this->pChartObject->drawLine($Plots["InX1"],$Plots["InY1"]-$SliceHeight,$Plots["OutX2"],$Plots["OutY2"]-$SliceHeight,$Settings); + $this->pChartObject->drawLine($Plots["InX2"],$Plots["InY2"]-$SliceHeight,$Plots["OutX1"],$Plots["OutY1"]-$SliceHeight,$Settings); + } + + if ( $DrawLabels ) + { + $Offset = 360; + foreach($Values as $Key => $Value) + { + $StartAngle = $Offset; + $EndAngle = $Offset-($Value*$ScaleFactor); if ( $EndAngle < 0 ) { $EndAngle = 0; } + + if ( $LabelColor == PIE_LABEL_COLOR_AUTO ) + { $Settings = array("FillR"=>$Palette[$ID]["R"],"FillG"=>$Palette[$ID]["G"],"FillB"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"]);} + else + { $Settings = array("FillR"=>$LabelR,"FillG"=>$LabelG,"FillB"=>$LabelB,"Alpha"=>$LabelAlpha); } + + $Angle = ($EndAngle - $Offset)/2 + $Offset; + $Xc = cos(($Angle-90)*PI/180) * ($OuterRadius+$DataGapRadius) + $X; + $Yc = sin(($Angle-90)*PI/180) * ($OuterRadius+$DataGapRadius)*$SkewFactor + $Y; + + if ( $WriteValues == PIE_VALUE_PERCENTAGE ) + $Label = $Display = round(( 100 / $SerieSum ) * $Value,$Precision)."%"; + elseif ( $WriteValues == PIE_VALUE_NATURAL ) + $Label = $Data["Series"][$Data["Abscissa"]]["Data"][$Key]; + else + $Label = ""; + + if ( $LabelStacked ) + $this->writePieLabel($Xc,$Yc-$SliceHeight,$Label,$Angle,$Settings,TRUE,$X,$Y,$OuterRadius); + else + $this->writePieLabel($Xc,$Yc-$SliceHeight,$Label,$Angle,$Settings,FALSE); + + $Offset = $EndAngle - $DataGapAngle; $ID--; $Slice++; + } + } + if ( $DrawLabels && $LabelStacked ) { $this->writeShiftedLabels(); } + + $this->pChartObject->Shadow = $RestoreShadow; + + return(PIE_RENDERED); + } + + /* Serialize an array */ + function arraySerialize($Data) + { + $Result = ""; + foreach($Data as $Key => $Value) + { if ($Result == "") { $Result = floor($Value); } else { $Result = $Result.",".floor($Value); } } + + return($Result); + } + + /* Reverse an array */ + function arrayReverse($Plots) + { + $Result = ""; + + for($i=count($Plots)-1;$i>=0;$i=$i-2) + { $Result[] = $Plots[$i-1]; $Result[] = $Plots[$i]; } + + return($Result); + } + + /* Remove unused series & values */ + function clean0Values($Data,$Palette,$DataSerie,$AbscissaSerie) + { + $NewPalette = ""; $NewData = ""; $NewAbscissa = ""; + + /* Remove unused series */ + foreach($Data["Series"] as $SerieName => $SerieSettings) + { if ( $SerieName != $DataSerie && $SerieName != $AbscissaSerie ) { unset($Data["Series"][$SerieName]); } } + + /* Remove NULL values */ + foreach($Data["Series"][$DataSerie]["Data"] as $Key => $Value) + { + if ($Value != 0 ) + { + $NewData[] = $Value; + $NewAbscissa[] = $Data["Series"][$AbscissaSerie]["Data"][$Key]; + if ( isset($Palette[$Key]) ) { $NewPalette[] = $Palette[$Key]; } + } + } + $Data["Series"][$DataSerie]["Data"] = $NewData; + $Data["Series"][$AbscissaSerie]["Data"] = $NewAbscissa; + + return(array($Data,$NewPalette)); + } + } ?> \ No newline at end of file diff --git a/www/libs/pChart2.1.3/class/pRadar.class.php b/www/libs/pChart2.1.4/class/pRadar.class.php similarity index 97% rename from www/libs/pChart2.1.3/class/pRadar.class.php rename to www/libs/pChart2.1.4/class/pRadar.class.php index af15bbf5..fd1aa278 100644 --- a/www/libs/pChart2.1.3/class/pRadar.class.php +++ b/www/libs/pChart2.1.4/class/pRadar.class.php @@ -1,681 +1,681 @@ -pChartObject = $Object; - - $FixedMax = isset($Format["FixedMax"]) ? $Format["FixedMax"] : VOID; - $AxisR = isset($Format["AxisR"]) ? $Format["AxisR"] : 60; - $AxisG = isset($Format["AxisG"]) ? $Format["AxisG"] : 60; - $AxisB = isset($Format["AxisB"]) ? $Format["AxisB"] : 60; - $AxisAlpha = isset($Format["AxisAlpha"]) ? $Format["AxisAlpha"] : 50; - $AxisRotation = isset($Format["AxisRotation"]) ? $Format["AxisRotation"] : 0; - $DrawTicks = isset($Format["DrawTicks"]) ? $Format["DrawTicks"] : TRUE; - $TicksLength = isset($Format["TicksLength"]) ? $Format["TicksLength"] : 2; - $DrawAxisValues = isset($Format["DrawAxisValues"]) ? $Format["DrawAxisValues"] : TRUE; - $AxisBoxRounded = isset($Format["AxisBoxRounded"]) ? $Format["AxisBoxRounded"] : TRUE; - $AxisFontName = isset($Format["AxisFontName"]) ? $Format["AxisFontName"] : $this->pChartObject->FontName; - $AxisFontSize = isset($Format["AxisFontSize"]) ? $Format["AxisFontSize"] : $this->pChartObject->FontSize; - $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : FALSE; - $WriteValuesInBubble = isset($Format["WriteValuesInBubble"]) ? $Format["WriteValuesInBubble"] : TRUE; - $ValueFontName = isset($Format["ValueFontName"]) ? $Format["ValueFontName"] : $this->pChartObject->FontName; - $ValueFontSize = isset($Format["ValueFontSize"]) ? $Format["ValueFontSize"] : $this->pChartObject->FontSize; - $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 4; - $OuterBubbleRadius = isset($Format["OuterBubbleRadius"]) ? $Format["OuterBubbleRadius"] : 2; - $OuterBubbleR = isset($Format["OuterBubbleR"]) ? $Format["OuterBubbleR"] : VOID; - $OuterBubbleG = isset($Format["OuterBubbleG"]) ? $Format["OuterBubbleG"] : VOID; - $OuterBubbleB = isset($Format["OuterBubbleB"]) ? $Format["OuterBubbleB"] : VOID; - $OuterBubbleAlpha = isset($Format["OuterBubbleAlpha"]) ? $Format["OuterBubbleAlpha"] : 100; - $InnerBubbleR = isset($Format["InnerBubbleR"]) ? $Format["InnerBubbleR"] : 255; - $InnerBubbleG = isset($Format["InnerBubbleG"]) ? $Format["InnerBubbleG"] : 255; - $InnerBubbleB = isset($Format["InnerBubbleB"]) ? $Format["InnerBubbleB"] : 255; - $InnerBubbleAlpha = isset($Format["InnerBubbleAlpha"]) ? $Format["InnerBubbleAlpha"] : 100; - $DrawBackground = isset($Format["DrawBackground"]) ? $Format["DrawBackground"] : TRUE; - $BackgroundR = isset($Format["BackgroundR"]) ? $Format["BackgroundR"] : 255; - $BackgroundG = isset($Format["BackgroundG"]) ? $Format["BackgroundG"] : 255; - $BackgroundB = isset($Format["BackgroundB"]) ? $Format["BackgroundB"] : 255; - $BackgroundAlpha = isset($Format["BackgroundAlpha"]) ? $Format["BackgroundAlpha"] : 50; - $BackgroundGradient= isset($Format["BackgroundGradient"]) ? $Format["BackgroundGradient"] : NULL; - $Layout = isset($Format["Layout"]) ? $Format["Layout"] : RADAR_LAYOUT_STAR; - $SegmentHeight = isset($Format["SegmentHeight"]) ? $Format["SegmentHeight"] : SEGMENT_HEIGHT_AUTO; - $Segments = isset($Format["Segments"]) ? $Format["Segments"] : 4; - $WriteLabels = isset($Format["WriteLabels"]) ? $Format["WriteLabels"] : TRUE; - $SkipLabels = isset($Format["SkipLabels"]) ? $Format["SkipLabels"] : 1; - $LabelMiddle = isset($Format["LabelMiddle"]) ? $Format["LabelMiddle"] : FALSE; - $LabelsBackground = isset($Format["LabelsBackground"]) ? $Format["LabelsBackground"] : TRUE; - $LabelsBGR = isset($Format["LabelsBGR"]) ? $Format["LabelsBGR"] : 255; - $LabelsBGG = isset($Format["LabelsBGR"]) ? $Format["LabelsBGG"] : 255; - $LabelsBGB = isset($Format["LabelsBGR"]) ? $Format["LabelsBGB"] : 255; - $LabelsBGAlpha = isset($Format["LabelsBGAlpha"]) ? $Format["LabelsBGAlpha"] : 50; - $LabelPos = isset($Format["LabelPos"]) ? $Format["LabelPos"] : RADAR_LABELS_ROTATED; - $LabelPadding = isset($Format["LabelPadding"]) ? $Format["LabelPadding"] : 4; - $DrawPoints = isset($Format["DrawPoints"]) ? $Format["DrawPoints"] : TRUE; - $PointRadius = isset($Format["PointRadius"]) ? $Format["PointRadius"] : 4; - $PointSurrounding = isset($Format["PointRadius"]) ? $Format["PointRadius"] : -30; - $DrawLines = isset($Format["DrawLines"]) ? $Format["DrawLines"] : TRUE; - $LineLoopStart = isset($Format["LineLoopStart"]) ? $Format["LineLoopStart"] : TRUE; - $DrawPoly = isset($Format["DrawPoly"]) ? $Format["DrawPoly"] : FALSE; - $PolyAlpha = isset($Format["PolyAlpha"]) ? $Format["PolyAlpha"] : 40; - $FontSize = $Object->FontSize; - $X1 = $Object->GraphAreaX1; - $Y1 = $Object->GraphAreaY1; - $X2 = $Object->GraphAreaX2; - $Y2 = $Object->GraphAreaY2; - $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; - - /* Cancel default tick length if ticks not enabled */ - if ( $DrawTicks == FALSE ) { $TicksLength = 0; } - - /* Data Processing */ - $Data = $Values->getData(); - $Palette = $Values->getPalette(); - - /* Catch the number of required axis */ - $LabelSerie = $Data["Abscissa"]; - if ( $LabelSerie != "" ) - { $Points = count($Data["Series"][$LabelSerie]["Data"]); } - else - { - $Points = 0; - foreach($Data["Series"] as $SerieName => $DataArray) - { if ( count($DataArray["Data"]) > $Points ) { $Points = count($DataArray["Data"]); } } - } - - /* Draw the axis */ - $CenterX = ($X2-$X1)/2 + $X1; - $CenterY = ($Y2-$Y1)/2 + $Y1; - - $EdgeHeight = min(($X2-$X1)/2,($Y2-$Y1)/2); - if ( $WriteLabels ) - $EdgeHeight = $EdgeHeight - $FontSize - $LabelPadding - $TicksLength; - - /* Determine the scale if set to automatic */ - if ( $SegmentHeight == SEGMENT_HEIGHT_AUTO) - { - if ( $FixedMax != VOID ) - $Max = $FixedMax; - else - { - $Max = 0; - foreach($Data["Series"] as $SerieName => $DataArray) - { - if ( $SerieName != $LabelSerie ) - { - if ( max($DataArray["Data"]) > $Max ) { $Max = max($DataArray["Data"]); } - } - } - } - $MaxSegments = $EdgeHeight/20; - $Scale = $Object->computeScale(0,$Max,$MaxSegments,array(1,2,5)); - - $Segments = $Scale["Rows"]; - $SegmentHeight = $Scale["RowHeight"]; - } - - if ( $LabelMiddle && $SkipLabels == 1 ) - { $Axisoffset = (360/$Points)/2; } - elseif ( $LabelMiddle && $SkipLabels != 1 ) - { $Axisoffset = (360/($Points/$SkipLabels))/2; } - elseif ( !$LabelMiddle ) - { $Axisoffset = 0; } - - /* Background processing */ - if ( $DrawBackground ) - { - $RestoreShadow = $Object->Shadow; - $Object->Shadow = FALSE; - - if ($BackgroundGradient == NULL) - { - if ( $Layout == RADAR_LAYOUT_STAR ) - { - $Color = array("R"=>$BackgroundR,"G"=>$BackgroundG,"B"=>$BackgroundB,"Alpha"=>$BackgroundAlpha); - $PointArray = ""; - for($i=0;$i<=360;$i=$i+(360/$Points)) - { - $PointArray[] = cos(deg2rad($i+$AxisRotation)) * $EdgeHeight + $CenterX; - $PointArray[] = sin(deg2rad($i+$AxisRotation)) * $EdgeHeight + $CenterY; - } - $Object->drawPolygon($PointArray,$Color); - } - elseif ( $Layout == RADAR_LAYOUT_CIRCLE ) - { - $Color = array("R"=>$BackgroundR,"G"=>$BackgroundG,"B"=>$BackgroundB,"Alpha"=>$BackgroundAlpha); - $Object->drawFilledCircle($CenterX,$CenterY,$EdgeHeight,$Color); - } - } - else - { - $GradientROffset = ($BackgroundGradient["EndR"] - $BackgroundGradient["StartR"]) / $Segments; - $GradientGOffset = ($BackgroundGradient["EndG"] - $BackgroundGradient["StartG"]) / $Segments; - $GradientBOffset = ($BackgroundGradient["EndB"] - $BackgroundGradient["StartB"]) / $Segments; - $GradientAlphaOffset = ($BackgroundGradient["EndAlpha"] - $BackgroundGradient["StartAlpha"]) / $Segments; - - if ( $Layout == RADAR_LAYOUT_STAR ) - { - for($j=$Segments;$j>=1;$j--) - { - $Color = array("R"=>$BackgroundGradient["StartR"]+$GradientROffset*$j,"G"=>$BackgroundGradient["StartG"]+$GradientGOffset*$j,"B"=>$BackgroundGradient["StartB"]+$GradientBOffset*$j,"Alpha"=>$BackgroundGradient["StartAlpha"]+$GradientAlphaOffset*$j); - $PointArray = ""; - - for($i=0;$i<=360;$i=$i+(360/$Points)) - { - $PointArray[] = cos(deg2rad($i+$AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterX; - $PointArray[] = sin(deg2rad($i+$AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterY; - } - $Object->drawPolygon($PointArray,$Color); - } - } - elseif ( $Layout == RADAR_LAYOUT_CIRCLE ) - { - for($j=$Segments;$j>=1;$j--) - { - $Color = array("R"=>$BackgroundGradient["StartR"]+$GradientROffset*$j,"G"=>$BackgroundGradient["StartG"]+$GradientGOffset*$j,"B"=>$BackgroundGradient["StartB"]+$GradientBOffset*$j,"Alpha"=>$BackgroundGradient["StartAlpha"]+$GradientAlphaOffset*$j); - $Object->drawFilledCircle($CenterX,$CenterY,($EdgeHeight/$Segments)*$j,$Color); - } - } - } - $Object->Shadow = $RestoreShadow; - } - - /* Axis to axis lines */ - $Color = array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha); - $ColorDotted = array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha*.8, "Ticks"=>2); - if ( $Layout == RADAR_LAYOUT_STAR ) - { - for($j=1;$j<=$Segments;$j++) - { - for($i=0;$i<360;$i=$i+(360/$Points)) - { - $EdgeX1 = cos(deg2rad($i+$AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterX; - $EdgeY1 = sin(deg2rad($i+$AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterY; - $EdgeX2 = cos(deg2rad($i+$AxisRotation+(360/$Points))) * ($EdgeHeight/$Segments)*$j + $CenterX; - $EdgeY2 = sin(deg2rad($i+$AxisRotation+(360/$Points))) * ($EdgeHeight/$Segments)*$j + $CenterY; - - $Object->drawLine($EdgeX1,$EdgeY1,$EdgeX2,$EdgeY2,$Color); - } - } - } - elseif ( $Layout == RADAR_LAYOUT_CIRCLE ) - { - for($j=1;$j<=$Segments;$j++) - { - $Radius = ($EdgeHeight/$Segments)*$j; - $Object->drawCircle($CenterX,$CenterY,$Radius,$Radius,$Color); - } - } - - if ( $DrawAxisValues ) - { - if ( $LabelsBackground ) - $Options = array("DrawBox"=>TRUE, "Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"BoxR"=>$LabelsBGR,"BoxG"=>$LabelsBGG,"BoxB"=>$LabelsBGB,"BoxAlpha"=>$LabelsBGAlpha); - else - $Options = array("Align"=>TEXT_ALIGN_MIDDLEMIDDLE); - - if ( $AxisBoxRounded ) { $Options["BoxRounded"] = TRUE; } - - $Options["FontName"] = $AxisFontName; - $Options["FontSize"] = $AxisFontSize; - - $Angle = 360 / ($Points*2); - for($j=1;$j<=$Segments;$j++) - { - $Label = $j * $SegmentHeight; - - if ( $Layout == RADAR_LAYOUT_CIRCLE ) - { - $EdgeX1 = cos(deg2rad($Angle+$AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterX; - $EdgeY1 = sin(deg2rad($Angle+$AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterY; - } - elseif ( $Layout == RADAR_LAYOUT_STAR ) - { - $EdgeX1 = cos(deg2rad($AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterX; - $EdgeY1 = sin(deg2rad($AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterY; - $EdgeX2 = cos(deg2rad((360 / $Points) + $AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterX; - $EdgeY2 = sin(deg2rad((360 / $Points) + $AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterY; - - $EdgeX1 = ($EdgeX2 - $EdgeX1)/2 + $EdgeX1; - $EdgeY1 = ($EdgeY2 - $EdgeY1)/2 + $EdgeY1; - } - - $Object->drawText($EdgeX1,$EdgeY1,$Label,$Options); - } - } - - /* Axis lines */ - $ID = 0; - for($i=0;$i<360;$i=$i+(360/$Points)) - { - $EdgeX = cos(deg2rad($i+$AxisRotation)) * ($EdgeHeight+$TicksLength) + $CenterX; - $EdgeY = sin(deg2rad($i+$AxisRotation)) * ($EdgeHeight+$TicksLength) + $CenterY; - - if ($ID % $SkipLabels == 0) - { $Object->drawLine($CenterX,$CenterY,$EdgeX,$EdgeY,$Color); } - else - { $Object->drawLine($CenterX,$CenterY,$EdgeX,$EdgeY,$ColorDotted); } - - if ( $WriteLabels ) - { - $LabelX = cos(deg2rad($i+$AxisRotation+$Axisoffset)) * ($EdgeHeight+$LabelPadding+$TicksLength) + $CenterX; - $LabelY = sin(deg2rad($i+$AxisRotation+$Axisoffset)) * ($EdgeHeight+$LabelPadding+$TicksLength) + $CenterY; - - if ( $LabelSerie != "" ) - { $Label = isset($Data["Series"][$LabelSerie]["Data"][$ID]) ? $Data["Series"][$LabelSerie]["Data"][$ID] : ""; } - else - $Label = $ID; - - if ($ID % $SkipLabels == 0) - { - if ( $LabelPos == RADAR_LABELS_ROTATED ) - $Object->drawText($LabelX,$LabelY,$Label,array("Angle"=>(360-($i+$AxisRotation+$Axisoffset))-90,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); - else - { - if ( (floor($LabelX) == floor($CenterX)) && (floor($LabelY) < floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); } - if ( (floor($LabelX) > floor($CenterX)) && (floor($LabelY) < floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_BOTTOMLEFT)); } - if ( (floor($LabelX) > floor($CenterX)) && (floor($LabelY) == floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_MIDDLELEFT)); } - if ( (floor($LabelX) > floor($CenterX)) && (floor($LabelY) > floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_TOPLEFT)); } - if ( (floor($LabelX) < floor($CenterX)) && (floor($LabelY) < floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_BOTTOMRIGHT)); } - if ( (floor($LabelX) < floor($CenterX)) && (floor($LabelY) == floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_MIDDLERIGHT)); } - if ( (floor($LabelX) < floor($CenterX)) && (floor($LabelY) > floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_TOPRIGHT)); } - if ( (floor($LabelX) == floor($CenterX)) && (floor($LabelY) > floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_TOPMIDDLE)); } - } - } - } - $ID++; - } - - /* Compute the plots position */ - $ID = 0; $Plot = ""; - foreach($Data["Series"] as $SerieName => $DataS) - { - if ( $SerieName != $LabelSerie ) - { - $Color = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"],"Surrounding"=>$PointSurrounding); - foreach($DataS["Data"] as $Key => $Value) - { - $Angle = (360/$Points) * $Key; - $Length = ($EdgeHeight/($Segments*$SegmentHeight))*$Value; - - $X = cos(deg2rad($Angle+$AxisRotation)) * $Length + $CenterX; - $Y = sin(deg2rad($Angle+$AxisRotation)) * $Length + $CenterY; - - $Plot[$ID][] = array($X,$Y,$Value); - - if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".floor($PointRadius),$this->pChartObject->toHTMLColor($Palette[$ID]["R"],$Palette[$ID]["G"],$Palette[$ID]["B"]),$DataS["Description"],$Data["Series"][$LabelSerie]["Data"][$Key]." = ".$Value); } - } - $ID++; - } - } - - /* Draw all that stuff! */ - foreach($Plot as $ID => $Points) - { - $Color = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"],"Surrounding"=>$PointSurrounding); - - /* Draw the polygons */ - if ( $DrawPoly ) - { - if ($PolyAlpha != NULL) - $Color = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$PolyAlpha,"Surrounding"=>$PointSurrounding); - - $PointsArray = ""; - for($i=0; $idrawPolygon($PointsArray,$Color); - } - - $Color = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"],"Surrounding"=>$PointSurrounding); - - /* Bubble and labels settings */ - $TextSettings = array("Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontName"=>$ValueFontName,"FontSize"=>$ValueFontSize,"R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"]); - $InnerColor = array("R"=>$InnerBubbleR,"G"=>$InnerBubbleG,"B"=>$InnerBubbleB,"Alpha"=>$InnerBubbleAlpha); - if ( $OuterBubbleR != VOID ) - $OuterColor = array("R"=>$OuterBubbleR,"G"=>$OuterBubbleG,"B"=>$OuterBubbleB,"Alpha"=>$OuterBubbleAlpha); - else - $OuterColor = array("R"=>$Palette[$ID]["R"]+20,"G"=>$Palette[$ID]["G"]+20,"B"=>$Palette[$ID]["B"]+20,"Alpha"=>$Palette[$ID]["Alpha"]); - - /* Loop to the starting points if asked */ - if ( $LineLoopStart && $DrawLines ) - $Object->drawLine($Points[count($Points)-1][0],$Points[count($Points)-1][1],$Points[0][0],$Points[0][1],$Color); - - /* Draw the lines & points */ - for($i=0; $idrawLine($Points[$i][0],$Points[$i][1],$Points[$i+1][0],$Points[$i+1][1],$Color); - - if ( $DrawPoints ) - $Object->drawFilledCircle($Points[$i][0],$Points[$i][1],$PointRadius,$Color); - - if ( $WriteValuesInBubble && $WriteValues ) - { - $TxtPos = $this->pChartObject->getTextBox($Points[$i][0],$Points[$i][1],$ValueFontName,$ValueFontSize,0,$Points[$i][2]); - $Radius = floor(($TxtPos[1]["X"] - $TxtPos[0]["X"] + $ValuePadding*2)/2); - - $this->pChartObject->drawFilledCircle($Points[$i][0],$Points[$i][1],$Radius+$OuterBubbleRadius,$OuterColor); - $this->pChartObject->drawFilledCircle($Points[$i][0],$Points[$i][1],$Radius,$InnerColor); - } - - if ( $WriteValues ) - $this->pChartObject->drawText($Points[$i][0]-1,$Points[$i][1]-1,$Points[$i][2],$TextSettings); - } - } - } - - - - /* Draw a radar chart */ - function drawPolar($Object,$Values,$Format="") - { - $this->pChartObject = $Object; - - $FixedMax = isset($Format["FixedMax"]) ? $Format["FixedMax"] : VOID; - $AxisR = isset($Format["AxisR"]) ? $Format["AxisR"] : 60; - $AxisG = isset($Format["AxisG"]) ? $Format["AxisG"] : 60; - $AxisB = isset($Format["AxisB"]) ? $Format["AxisB"] : 60; - $AxisAlpha = isset($Format["AxisAlpha"]) ? $Format["AxisAlpha"] : 50; - $AxisRotation = isset($Format["AxisRotation"]) ? $Format["AxisRotation"] : -90; - $DrawTicks = isset($Format["DrawTicks"]) ? $Format["DrawTicks"] : TRUE; - $TicksLength = isset($Format["TicksLength"]) ? $Format["TicksLength"] : 2; - $DrawAxisValues = isset($Format["DrawAxisValues"]) ? $Format["DrawAxisValues"] : TRUE; - $AxisBoxRounded = isset($Format["AxisBoxRounded"]) ? $Format["AxisBoxRounded"] : TRUE; - $AxisFontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->pChartObject->FontName; - $AxisFontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->pChartObject->FontSize; - $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : FALSE; - $WriteValuesInBubble = isset($Format["WriteValuesInBubble"]) ? $Format["WriteValuesInBubble"] : TRUE; - $ValueFontName = isset($Format["ValueFontName"]) ? $Format["ValueFontName"] : $this->pChartObject->FontName; - $ValueFontSize = isset($Format["ValueFontSize"]) ? $Format["ValueFontSize"] : $this->pChartObject->FontSize; - $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 4; - $OuterBubbleRadius = isset($Format["OuterBubbleRadius"]) ? $Format["OuterBubbleRadius"] : 2; - $OuterBubbleR = isset($Format["OuterBubbleR"]) ? $Format["OuterBubbleR"] : VOID; - $OuterBubbleG = isset($Format["OuterBubbleG"]) ? $Format["OuterBubbleG"] : VOID; - $OuterBubbleB = isset($Format["OuterBubbleB"]) ? $Format["OuterBubbleB"] : VOID; - $OuterBubbleAlpha = isset($Format["OuterBubbleAlpha"]) ? $Format["OuterBubbleAlpha"] : 100; - $InnerBubbleR = isset($Format["InnerBubbleR"]) ? $Format["InnerBubbleR"] : 255; - $InnerBubbleG = isset($Format["InnerBubbleG"]) ? $Format["InnerBubbleG"] : 255; - $InnerBubbleB = isset($Format["InnerBubbleB"]) ? $Format["InnerBubbleB"] : 255; - $InnerBubbleAlpha = isset($Format["InnerBubbleAlpha"]) ? $Format["InnerBubbleAlpha"] : 100; - $DrawBackground = isset($Format["DrawBackground"]) ? $Format["DrawBackground"] : TRUE; - $BackgroundR = isset($Format["BackgroundR"]) ? $Format["BackgroundR"] : 255; - $BackgroundG = isset($Format["BackgroundG"]) ? $Format["BackgroundG"] : 255; - $BackgroundB = isset($Format["BackgroundB"]) ? $Format["BackgroundB"] : 255; - $BackgroundAlpha = isset($Format["BackgroundAlpha"]) ? $Format["BackgroundAlpha"] : 50; - $BackgroundGradient= isset($Format["BackgroundGradient"]) ? $Format["BackgroundGradient"] : NULL; - $AxisSteps = isset($Format["AxisSteps"]) ? $Format["AxisSteps"] : 20; - $SegmentHeight = isset($Format["SegmentHeight"]) ? $Format["SegmentHeight"] : SEGMENT_HEIGHT_AUTO; - $Segments = isset($Format["Segments"]) ? $Format["Segments"] : 4; - $WriteLabels = isset($Format["WriteLabels"]) ? $Format["WriteLabels"] : TRUE; - $LabelsBackground = isset($Format["LabelsBackground"]) ? $Format["LabelsBackground"] : TRUE; - $LabelsBGR = isset($Format["LabelsBGR"]) ? $Format["LabelsBGR"] : 255; - $LabelsBGG = isset($Format["LabelsBGR"]) ? $Format["LabelsBGG"] : 255; - $LabelsBGB = isset($Format["LabelsBGR"]) ? $Format["LabelsBGB"] : 255; - $LabelsBGAlpha = isset($Format["LabelsBGAlpha"]) ? $Format["LabelsBGAlpha"] : 50; - $LabelPos = isset($Format["LabelPos"]) ? $Format["LabelPos"] : RADAR_LABELS_ROTATED; - $LabelPadding = isset($Format["LabelPadding"]) ? $Format["LabelPadding"] : 4; - $DrawPoints = isset($Format["DrawPoints"]) ? $Format["DrawPoints"] : TRUE; - $PointRadius = isset($Format["PointRadius"]) ? $Format["PointRadius"] : 4; - $PointSurrounding = isset($Format["PointRadius"]) ? $Format["PointRadius"] : -30; - $DrawLines = isset($Format["DrawLines"]) ? $Format["DrawLines"] : TRUE; - $LineLoopStart = isset($Format["LineLoopStart"]) ? $Format["LineLoopStart"] : FALSE; - $DrawPoly = isset($Format["DrawPoly"]) ? $Format["DrawPoly"] : FALSE; - $PolyAlpha = isset($Format["PolyAlpha"]) ? $Format["PolyAlpha"] : NULL; - $FontSize = $Object->FontSize; - $X1 = $Object->GraphAreaX1; - $Y1 = $Object->GraphAreaY1; - $X2 = $Object->GraphAreaX2; - $Y2 = $Object->GraphAreaY2; - $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; - - if ( $AxisBoxRounded ) { $DrawAxisValues = TRUE; } - - /* Cancel default tick length if ticks not enabled */ - if ( $DrawTicks == FALSE ) { $TicksLength = 0; } - - /* Data Processing */ - $Data = $Values->getData(); - $Palette = $Values->getPalette(); - - /* Catch the number of required axis */ - $LabelSerie = $Data["Abscissa"]; - if ( $LabelSerie != "" ) - { $Points = count($Data["Series"][$LabelSerie]["Data"]); } - else - { - $Points = 0; - foreach($Data["Series"] as $SerieName => $DataArray) - { if ( count($DataArray["Data"]) > $Points ) { $Points = count($DataArray["Data"]); } } - } - - /* Draw the axis */ - $CenterX = ($X2-$X1)/2 + $X1; - $CenterY = ($Y2-$Y1)/2 + $Y1; - - $EdgeHeight = min(($X2-$X1)/2,($Y2-$Y1)/2); - if ( $WriteLabels ) - $EdgeHeight = $EdgeHeight - $FontSize - $LabelPadding - $TicksLength; - - /* Determine the scale if set to automatic */ - if ( $SegmentHeight == SEGMENT_HEIGHT_AUTO) - { - if ( $FixedMax != VOID ) - $Max = $FixedMax; - else - { - $Max = 0; - foreach($Data["Series"] as $SerieName => $DataArray) - { - if ( $SerieName != $LabelSerie ) - { - if ( max($DataArray["Data"]) > $Max ) { $Max = max($DataArray["Data"]); } - } - } - } - $MaxSegments = $EdgeHeight/20; - $Scale = $Object->computeScale(0,$Max,$MaxSegments,array(1,2,5)); - - $Segments = $Scale["Rows"]; - $SegmentHeight = $Scale["RowHeight"]; - } - - - /* Background processing */ - if ( $DrawBackground ) - { - $RestoreShadow = $Object->Shadow; - $Object->Shadow = FALSE; - - if ($BackgroundGradient == NULL) - { - $Color = array("R"=>$BackgroundR,"G"=>$BackgroundG,"B"=>$BackgroundB,"Alpha"=>$BackgroundAlpha); - $Object->drawFilledCircle($CenterX,$CenterY,$EdgeHeight,$Color); - } - else - { - $GradientROffset = ($BackgroundGradient["EndR"] - $BackgroundGradient["StartR"]) / $Segments; - $GradientGOffset = ($BackgroundGradient["EndG"] - $BackgroundGradient["StartG"]) / $Segments; - $GradientBOffset = ($BackgroundGradient["EndB"] - $BackgroundGradient["StartB"]) / $Segments; - $GradientAlphaOffset = ($BackgroundGradient["EndAlpha"] - $BackgroundGradient["StartAlpha"]) / $Segments; - - for($j=$Segments;$j>=1;$j--) - { - $Color = array("R"=>$BackgroundGradient["StartR"]+$GradientROffset*$j,"G"=>$BackgroundGradient["StartG"]+$GradientGOffset*$j,"B"=>$BackgroundGradient["StartB"]+$GradientBOffset*$j,"Alpha"=>$BackgroundGradient["StartAlpha"]+$GradientAlphaOffset*$j); - $Object->drawFilledCircle($CenterX,$CenterY,($EdgeHeight/$Segments)*$j,$Color); - } - } - $Object->Shadow = $RestoreShadow; - } - - /* Axis to axis lines */ - $Color = array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha); - for($j=1;$j<=$Segments;$j++) - { - $Radius = ($EdgeHeight/$Segments)*$j; - $Object->drawCircle($CenterX,$CenterY,$Radius,$Radius,$Color); - } - - if ( $DrawAxisValues ) - { - if ( $LabelsBackground ) - $Options = array("DrawBox"=>TRUE, "Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"BoxR"=>$LabelsBGR,"BoxG"=>$LabelsBGG,"BoxB"=>$LabelsBGB,"BoxAlpha"=>$LabelsBGAlpha); - else - $Options = array("Align"=>TEXT_ALIGN_MIDDLEMIDDLE); - - if ( $AxisBoxRounded ) { $Options["BoxRounded"] = TRUE; } - - $Options["FontName"] = $AxisFontName; - $Options["FontSize"] = $AxisFontSize; - - $Angle = 360 / ($Points*2); - for($j=1;$j<=$Segments;$j++) - { - $EdgeX1 = cos(deg2rad($Angle+$AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterX; - $EdgeY1 = sin(deg2rad($Angle+$AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterY; - $Label = $j*$SegmentHeight; - - $Object->drawText($EdgeX1,$EdgeY1,$Label,$Options); - } - } - - /* Axis lines */ - $ID = 0; - for($i=0;$i<=359;$i=$i+$AxisSteps) - { - $EdgeX = cos(deg2rad($i+$AxisRotation)) * ($EdgeHeight+$TicksLength) + $CenterX; - $EdgeY = sin(deg2rad($i+$AxisRotation)) * ($EdgeHeight+$TicksLength) + $CenterY; - - $Object->drawLine($CenterX,$CenterY,$EdgeX,$EdgeY,$Color); - - if ( $WriteLabels ) - { - $LabelX = cos(deg2rad($i+$AxisRotation)) * ($EdgeHeight+$LabelPadding+$TicksLength) + $CenterX; - $LabelY = sin(deg2rad($i+$AxisRotation)) * ($EdgeHeight+$LabelPadding+$TicksLength) + $CenterY; - $Label = $i."°"; - - if ( $LabelPos == RADAR_LABELS_ROTATED ) - $Object->drawText($LabelX,$LabelY,$Label,array("Angle"=>(360-$i),"Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); - else - { - if ( (floor($LabelX) == floor($CenterX)) && (floor($LabelY) < floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); } - if ( (floor($LabelX) > floor($CenterX)) && (floor($LabelY) < floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_BOTTOMLEFT)); } - if ( (floor($LabelX) > floor($CenterX)) && (floor($LabelY) == floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_MIDDLELEFT)); } - if ( (floor($LabelX) > floor($CenterX)) && (floor($LabelY) > floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_TOPLEFT)); } - if ( (floor($LabelX) < floor($CenterX)) && (floor($LabelY) < floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_BOTTOMRIGHT)); } - if ( (floor($LabelX) < floor($CenterX)) && (floor($LabelY) == floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_MIDDLERIGHT)); } - if ( (floor($LabelX) < floor($CenterX)) && (floor($LabelY) > floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_TOPRIGHT)); } - if ( (floor($LabelX) == floor($CenterX)) && (floor($LabelY) > floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_TOPMIDDLE)); } - } - } - $ID++; - } - - /* Compute the plots position */ - $ID = 0; $Plot = ""; - foreach($Data["Series"] as $SerieName => $DataSet) - { - if ( $SerieName != $LabelSerie ) - { - $Color = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"],"Surrounding"=>$PointSurrounding); - foreach($DataSet["Data"] as $Key => $Value) - { - $Angle = $Data["Series"][$LabelSerie]["Data"][$Key]; - $Length = ($EdgeHeight/($Segments*$SegmentHeight))*$Value; - - $X = cos(deg2rad($Angle+$AxisRotation)) * $Length + $CenterX; - $Y = sin(deg2rad($Angle+$AxisRotation)) * $Length + $CenterY; - - if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".floor($PointRadius),$this->pChartObject->toHTMLColor($Palette[$ID]["R"],$Palette[$ID]["G"],$Palette[$ID]["B"]),$DataSet["Description"],$Data["Series"][$LabelSerie]["Data"][$Key]."° = ".$Value); } - - $Plot[$ID][] = array($X,$Y,$Value); - } - $ID++; - } - } - - /* Draw all that stuff! */ - foreach($Plot as $ID => $Points) - { - $Color = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"],"Surrounding"=>$PointSurrounding); - - /* Draw the polygons */ - if ( $DrawPoly ) - { - if ($PolyAlpha != NULL) - $Color = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$PolyAlpha,"Surrounding"=>$PointSurrounding); - - $PointsArray = ""; - for($i=0; $idrawPolygon($PointsArray,$Color); - } - - $Color = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"],"Surrounding"=>$PointSurrounding); - - /* Bubble and labels settings */ - $TextSettings = array("Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontName"=>$ValueFontName,"FontSize"=>$ValueFontSize,"R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"]); - $InnerColor = array("R"=>$InnerBubbleR,"G"=>$InnerBubbleG,"B"=>$InnerBubbleB,"Alpha"=>$InnerBubbleAlpha); - if ( $OuterBubbleR != VOID ) - $OuterColor = array("R"=>$OuterBubbleR,"G"=>$OuterBubbleG,"B"=>$OuterBubbleB,"Alpha"=>$OuterBubbleAlpha); - else - $OuterColor = array("R"=>$Palette[$ID]["R"]+20,"G"=>$Palette[$ID]["G"]+20,"B"=>$Palette[$ID]["B"]+20,"Alpha"=>$Palette[$ID]["Alpha"]); - - /* Loop to the starting points if asked */ - if ( $LineLoopStart && $DrawLines ) - $Object->drawLine($Points[count($Points)-1][0],$Points[count($Points)-1][1],$Points[0][0],$Points[0][1],$Color); - - /* Draw the lines & points */ - for($i=0; $idrawLine($Points[$i][0],$Points[$i][1],$Points[$i+1][0],$Points[$i+1][1],$Color); - - if ( $DrawPoints ) - $Object->drawFilledCircle($Points[$i][0],$Points[$i][1],$PointRadius,$Color); - - if ( $WriteValuesInBubble && $WriteValues ) - { - $TxtPos = $this->pChartObject->getTextBox($Points[$i][0],$Points[$i][1],$ValueFontName,$ValueFontSize,0,$Points[$i][2]); - $Radius = floor(($TxtPos[1]["X"] - $TxtPos[0]["X"] + $ValuePadding*2)/2); - - $this->pChartObject->drawFilledCircle($Points[$i][0],$Points[$i][1],$Radius+$OuterBubbleRadius,$OuterColor); - $this->pChartObject->drawFilledCircle($Points[$i][0],$Points[$i][1],$Radius,$InnerColor); - } - - if ( $WriteValues ) - $this->pChartObject->drawText($Points[$i][0]-1,$Points[$i][1]-1,$Points[$i][2],$TextSettings); - } - } - } - } +pChartObject = $Object; + + $FixedMax = isset($Format["FixedMax"]) ? $Format["FixedMax"] : VOID; + $AxisR = isset($Format["AxisR"]) ? $Format["AxisR"] : 60; + $AxisG = isset($Format["AxisG"]) ? $Format["AxisG"] : 60; + $AxisB = isset($Format["AxisB"]) ? $Format["AxisB"] : 60; + $AxisAlpha = isset($Format["AxisAlpha"]) ? $Format["AxisAlpha"] : 50; + $AxisRotation = isset($Format["AxisRotation"]) ? $Format["AxisRotation"] : 0; + $DrawTicks = isset($Format["DrawTicks"]) ? $Format["DrawTicks"] : TRUE; + $TicksLength = isset($Format["TicksLength"]) ? $Format["TicksLength"] : 2; + $DrawAxisValues = isset($Format["DrawAxisValues"]) ? $Format["DrawAxisValues"] : TRUE; + $AxisBoxRounded = isset($Format["AxisBoxRounded"]) ? $Format["AxisBoxRounded"] : TRUE; + $AxisFontName = isset($Format["AxisFontName"]) ? $Format["AxisFontName"] : $this->pChartObject->FontName; + $AxisFontSize = isset($Format["AxisFontSize"]) ? $Format["AxisFontSize"] : $this->pChartObject->FontSize; + $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : FALSE; + $WriteValuesInBubble = isset($Format["WriteValuesInBubble"]) ? $Format["WriteValuesInBubble"] : TRUE; + $ValueFontName = isset($Format["ValueFontName"]) ? $Format["ValueFontName"] : $this->pChartObject->FontName; + $ValueFontSize = isset($Format["ValueFontSize"]) ? $Format["ValueFontSize"] : $this->pChartObject->FontSize; + $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 4; + $OuterBubbleRadius = isset($Format["OuterBubbleRadius"]) ? $Format["OuterBubbleRadius"] : 2; + $OuterBubbleR = isset($Format["OuterBubbleR"]) ? $Format["OuterBubbleR"] : VOID; + $OuterBubbleG = isset($Format["OuterBubbleG"]) ? $Format["OuterBubbleG"] : VOID; + $OuterBubbleB = isset($Format["OuterBubbleB"]) ? $Format["OuterBubbleB"] : VOID; + $OuterBubbleAlpha = isset($Format["OuterBubbleAlpha"]) ? $Format["OuterBubbleAlpha"] : 100; + $InnerBubbleR = isset($Format["InnerBubbleR"]) ? $Format["InnerBubbleR"] : 255; + $InnerBubbleG = isset($Format["InnerBubbleG"]) ? $Format["InnerBubbleG"] : 255; + $InnerBubbleB = isset($Format["InnerBubbleB"]) ? $Format["InnerBubbleB"] : 255; + $InnerBubbleAlpha = isset($Format["InnerBubbleAlpha"]) ? $Format["InnerBubbleAlpha"] : 100; + $DrawBackground = isset($Format["DrawBackground"]) ? $Format["DrawBackground"] : TRUE; + $BackgroundR = isset($Format["BackgroundR"]) ? $Format["BackgroundR"] : 255; + $BackgroundG = isset($Format["BackgroundG"]) ? $Format["BackgroundG"] : 255; + $BackgroundB = isset($Format["BackgroundB"]) ? $Format["BackgroundB"] : 255; + $BackgroundAlpha = isset($Format["BackgroundAlpha"]) ? $Format["BackgroundAlpha"] : 50; + $BackgroundGradient= isset($Format["BackgroundGradient"]) ? $Format["BackgroundGradient"] : NULL; + $Layout = isset($Format["Layout"]) ? $Format["Layout"] : RADAR_LAYOUT_STAR; + $SegmentHeight = isset($Format["SegmentHeight"]) ? $Format["SegmentHeight"] : SEGMENT_HEIGHT_AUTO; + $Segments = isset($Format["Segments"]) ? $Format["Segments"] : 4; + $WriteLabels = isset($Format["WriteLabels"]) ? $Format["WriteLabels"] : TRUE; + $SkipLabels = isset($Format["SkipLabels"]) ? $Format["SkipLabels"] : 1; + $LabelMiddle = isset($Format["LabelMiddle"]) ? $Format["LabelMiddle"] : FALSE; + $LabelsBackground = isset($Format["LabelsBackground"]) ? $Format["LabelsBackground"] : TRUE; + $LabelsBGR = isset($Format["LabelsBGR"]) ? $Format["LabelsBGR"] : 255; + $LabelsBGG = isset($Format["LabelsBGR"]) ? $Format["LabelsBGG"] : 255; + $LabelsBGB = isset($Format["LabelsBGR"]) ? $Format["LabelsBGB"] : 255; + $LabelsBGAlpha = isset($Format["LabelsBGAlpha"]) ? $Format["LabelsBGAlpha"] : 50; + $LabelPos = isset($Format["LabelPos"]) ? $Format["LabelPos"] : RADAR_LABELS_ROTATED; + $LabelPadding = isset($Format["LabelPadding"]) ? $Format["LabelPadding"] : 4; + $DrawPoints = isset($Format["DrawPoints"]) ? $Format["DrawPoints"] : TRUE; + $PointRadius = isset($Format["PointRadius"]) ? $Format["PointRadius"] : 4; + $PointSurrounding = isset($Format["PointRadius"]) ? $Format["PointRadius"] : -30; + $DrawLines = isset($Format["DrawLines"]) ? $Format["DrawLines"] : TRUE; + $LineLoopStart = isset($Format["LineLoopStart"]) ? $Format["LineLoopStart"] : TRUE; + $DrawPoly = isset($Format["DrawPoly"]) ? $Format["DrawPoly"] : FALSE; + $PolyAlpha = isset($Format["PolyAlpha"]) ? $Format["PolyAlpha"] : 40; + $FontSize = $Object->FontSize; + $X1 = $Object->GraphAreaX1; + $Y1 = $Object->GraphAreaY1; + $X2 = $Object->GraphAreaX2; + $Y2 = $Object->GraphAreaY2; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; + + /* Cancel default tick length if ticks not enabled */ + if ( $DrawTicks == FALSE ) { $TicksLength = 0; } + + /* Data Processing */ + $Data = $Values->getData(); + $Palette = $Values->getPalette(); + + /* Catch the number of required axis */ + $LabelSerie = $Data["Abscissa"]; + if ( $LabelSerie != "" ) + { $Points = count($Data["Series"][$LabelSerie]["Data"]); } + else + { + $Points = 0; + foreach($Data["Series"] as $SerieName => $DataArray) + { if ( count($DataArray["Data"]) > $Points ) { $Points = count($DataArray["Data"]); } } + } + + /* Draw the axis */ + $CenterX = ($X2-$X1)/2 + $X1; + $CenterY = ($Y2-$Y1)/2 + $Y1; + + $EdgeHeight = min(($X2-$X1)/2,($Y2-$Y1)/2); + if ( $WriteLabels ) + $EdgeHeight = $EdgeHeight - $FontSize - $LabelPadding - $TicksLength; + + /* Determine the scale if set to automatic */ + if ( $SegmentHeight == SEGMENT_HEIGHT_AUTO) + { + if ( $FixedMax != VOID ) + $Max = $FixedMax; + else + { + $Max = 0; + foreach($Data["Series"] as $SerieName => $DataArray) + { + if ( $SerieName != $LabelSerie ) + { + if ( max($DataArray["Data"]) > $Max ) { $Max = max($DataArray["Data"]); } + } + } + } + $MaxSegments = $EdgeHeight/20; + $Scale = $Object->computeScale(0,$Max,$MaxSegments,array(1,2,5)); + + $Segments = $Scale["Rows"]; + $SegmentHeight = $Scale["RowHeight"]; + } + + if ( $LabelMiddle && $SkipLabels == 1 ) + { $Axisoffset = (360/$Points)/2; } + elseif ( $LabelMiddle && $SkipLabels != 1 ) + { $Axisoffset = (360/($Points/$SkipLabels))/2; } + elseif ( !$LabelMiddle ) + { $Axisoffset = 0; } + + /* Background processing */ + if ( $DrawBackground ) + { + $RestoreShadow = $Object->Shadow; + $Object->Shadow = FALSE; + + if ($BackgroundGradient == NULL) + { + if ( $Layout == RADAR_LAYOUT_STAR ) + { + $Color = array("R"=>$BackgroundR,"G"=>$BackgroundG,"B"=>$BackgroundB,"Alpha"=>$BackgroundAlpha); + $PointArray = ""; + for($i=0;$i<=360;$i=$i+(360/$Points)) + { + $PointArray[] = cos(deg2rad($i+$AxisRotation)) * $EdgeHeight + $CenterX; + $PointArray[] = sin(deg2rad($i+$AxisRotation)) * $EdgeHeight + $CenterY; + } + $Object->drawPolygon($PointArray,$Color); + } + elseif ( $Layout == RADAR_LAYOUT_CIRCLE ) + { + $Color = array("R"=>$BackgroundR,"G"=>$BackgroundG,"B"=>$BackgroundB,"Alpha"=>$BackgroundAlpha); + $Object->drawFilledCircle($CenterX,$CenterY,$EdgeHeight,$Color); + } + } + else + { + $GradientROffset = ($BackgroundGradient["EndR"] - $BackgroundGradient["StartR"]) / $Segments; + $GradientGOffset = ($BackgroundGradient["EndG"] - $BackgroundGradient["StartG"]) / $Segments; + $GradientBOffset = ($BackgroundGradient["EndB"] - $BackgroundGradient["StartB"]) / $Segments; + $GradientAlphaOffset = ($BackgroundGradient["EndAlpha"] - $BackgroundGradient["StartAlpha"]) / $Segments; + + if ( $Layout == RADAR_LAYOUT_STAR ) + { + for($j=$Segments;$j>=1;$j--) + { + $Color = array("R"=>$BackgroundGradient["StartR"]+$GradientROffset*$j,"G"=>$BackgroundGradient["StartG"]+$GradientGOffset*$j,"B"=>$BackgroundGradient["StartB"]+$GradientBOffset*$j,"Alpha"=>$BackgroundGradient["StartAlpha"]+$GradientAlphaOffset*$j); + $PointArray = ""; + + for($i=0;$i<=360;$i=$i+(360/$Points)) + { + $PointArray[] = cos(deg2rad($i+$AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterX; + $PointArray[] = sin(deg2rad($i+$AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterY; + } + $Object->drawPolygon($PointArray,$Color); + } + } + elseif ( $Layout == RADAR_LAYOUT_CIRCLE ) + { + for($j=$Segments;$j>=1;$j--) + { + $Color = array("R"=>$BackgroundGradient["StartR"]+$GradientROffset*$j,"G"=>$BackgroundGradient["StartG"]+$GradientGOffset*$j,"B"=>$BackgroundGradient["StartB"]+$GradientBOffset*$j,"Alpha"=>$BackgroundGradient["StartAlpha"]+$GradientAlphaOffset*$j); + $Object->drawFilledCircle($CenterX,$CenterY,($EdgeHeight/$Segments)*$j,$Color); + } + } + } + $Object->Shadow = $RestoreShadow; + } + + /* Axis to axis lines */ + $Color = array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha); + $ColorDotted = array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha*.8, "Ticks"=>2); + if ( $Layout == RADAR_LAYOUT_STAR ) + { + for($j=1;$j<=$Segments;$j++) + { + for($i=0;$i<360;$i=$i+(360/$Points)) + { + $EdgeX1 = cos(deg2rad($i+$AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterX; + $EdgeY1 = sin(deg2rad($i+$AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterY; + $EdgeX2 = cos(deg2rad($i+$AxisRotation+(360/$Points))) * ($EdgeHeight/$Segments)*$j + $CenterX; + $EdgeY2 = sin(deg2rad($i+$AxisRotation+(360/$Points))) * ($EdgeHeight/$Segments)*$j + $CenterY; + + $Object->drawLine($EdgeX1,$EdgeY1,$EdgeX2,$EdgeY2,$Color); + } + } + } + elseif ( $Layout == RADAR_LAYOUT_CIRCLE ) + { + for($j=1;$j<=$Segments;$j++) + { + $Radius = ($EdgeHeight/$Segments)*$j; + $Object->drawCircle($CenterX,$CenterY,$Radius,$Radius,$Color); + } + } + + if ( $DrawAxisValues ) + { + if ( $LabelsBackground ) + $Options = array("DrawBox"=>TRUE, "Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"BoxR"=>$LabelsBGR,"BoxG"=>$LabelsBGG,"BoxB"=>$LabelsBGB,"BoxAlpha"=>$LabelsBGAlpha); + else + $Options = array("Align"=>TEXT_ALIGN_MIDDLEMIDDLE); + + if ( $AxisBoxRounded ) { $Options["BoxRounded"] = TRUE; } + + $Options["FontName"] = $AxisFontName; + $Options["FontSize"] = $AxisFontSize; + + $Angle = 360 / ($Points*2); + for($j=1;$j<=$Segments;$j++) + { + $Label = $j * $SegmentHeight; + + if ( $Layout == RADAR_LAYOUT_CIRCLE ) + { + $EdgeX1 = cos(deg2rad($Angle+$AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterX; + $EdgeY1 = sin(deg2rad($Angle+$AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterY; + } + elseif ( $Layout == RADAR_LAYOUT_STAR ) + { + $EdgeX1 = cos(deg2rad($AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterX; + $EdgeY1 = sin(deg2rad($AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterY; + $EdgeX2 = cos(deg2rad((360 / $Points) + $AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterX; + $EdgeY2 = sin(deg2rad((360 / $Points) + $AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterY; + + $EdgeX1 = ($EdgeX2 - $EdgeX1)/2 + $EdgeX1; + $EdgeY1 = ($EdgeY2 - $EdgeY1)/2 + $EdgeY1; + } + + $Object->drawText($EdgeX1,$EdgeY1,$Label,$Options); + } + } + + /* Axis lines */ + $ID = 0; + for($i=0;$i<360;$i=$i+(360/$Points)) + { + $EdgeX = cos(deg2rad($i+$AxisRotation)) * ($EdgeHeight+$TicksLength) + $CenterX; + $EdgeY = sin(deg2rad($i+$AxisRotation)) * ($EdgeHeight+$TicksLength) + $CenterY; + + if ($ID % $SkipLabels == 0) + { $Object->drawLine($CenterX,$CenterY,$EdgeX,$EdgeY,$Color); } + else + { $Object->drawLine($CenterX,$CenterY,$EdgeX,$EdgeY,$ColorDotted); } + + if ( $WriteLabels ) + { + $LabelX = cos(deg2rad($i+$AxisRotation+$Axisoffset)) * ($EdgeHeight+$LabelPadding+$TicksLength) + $CenterX; + $LabelY = sin(deg2rad($i+$AxisRotation+$Axisoffset)) * ($EdgeHeight+$LabelPadding+$TicksLength) + $CenterY; + + if ( $LabelSerie != "" ) + { $Label = isset($Data["Series"][$LabelSerie]["Data"][$ID]) ? $Data["Series"][$LabelSerie]["Data"][$ID] : ""; } + else + $Label = $ID; + + if ($ID % $SkipLabels == 0) + { + if ( $LabelPos == RADAR_LABELS_ROTATED ) + $Object->drawText($LabelX,$LabelY,$Label,array("Angle"=>(360-($i+$AxisRotation+$Axisoffset))-90,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); + else + { + if ( (floor($LabelX) == floor($CenterX)) && (floor($LabelY) < floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); } + if ( (floor($LabelX) > floor($CenterX)) && (floor($LabelY) < floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_BOTTOMLEFT)); } + if ( (floor($LabelX) > floor($CenterX)) && (floor($LabelY) == floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_MIDDLELEFT)); } + if ( (floor($LabelX) > floor($CenterX)) && (floor($LabelY) > floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_TOPLEFT)); } + if ( (floor($LabelX) < floor($CenterX)) && (floor($LabelY) < floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_BOTTOMRIGHT)); } + if ( (floor($LabelX) < floor($CenterX)) && (floor($LabelY) == floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_MIDDLERIGHT)); } + if ( (floor($LabelX) < floor($CenterX)) && (floor($LabelY) > floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_TOPRIGHT)); } + if ( (floor($LabelX) == floor($CenterX)) && (floor($LabelY) > floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_TOPMIDDLE)); } + } + } + } + $ID++; + } + + /* Compute the plots position */ + $ID = 0; $Plot = ""; + foreach($Data["Series"] as $SerieName => $DataS) + { + if ( $SerieName != $LabelSerie ) + { + $Color = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"],"Surrounding"=>$PointSurrounding); + foreach($DataS["Data"] as $Key => $Value) + { + $Angle = (360/$Points) * $Key; + $Length = ($EdgeHeight/($Segments*$SegmentHeight))*$Value; + + $X = cos(deg2rad($Angle+$AxisRotation)) * $Length + $CenterX; + $Y = sin(deg2rad($Angle+$AxisRotation)) * $Length + $CenterY; + + $Plot[$ID][] = array($X,$Y,$Value); + + if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".floor($PointRadius),$this->pChartObject->toHTMLColor($Palette[$ID]["R"],$Palette[$ID]["G"],$Palette[$ID]["B"]),$DataS["Description"],$Data["Series"][$LabelSerie]["Data"][$Key]." = ".$Value); } + } + $ID++; + } + } + + /* Draw all that stuff! */ + foreach($Plot as $ID => $Points) + { + $Color = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"],"Surrounding"=>$PointSurrounding); + + /* Draw the polygons */ + if ( $DrawPoly ) + { + if ($PolyAlpha != NULL) + $Color = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$PolyAlpha,"Surrounding"=>$PointSurrounding); + + $PointsArray = ""; + for($i=0; $idrawPolygon($PointsArray,$Color); + } + + $Color = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"],"Surrounding"=>$PointSurrounding); + + /* Bubble and labels settings */ + $TextSettings = array("Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontName"=>$ValueFontName,"FontSize"=>$ValueFontSize,"R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"]); + $InnerColor = array("R"=>$InnerBubbleR,"G"=>$InnerBubbleG,"B"=>$InnerBubbleB,"Alpha"=>$InnerBubbleAlpha); + if ( $OuterBubbleR != VOID ) + $OuterColor = array("R"=>$OuterBubbleR,"G"=>$OuterBubbleG,"B"=>$OuterBubbleB,"Alpha"=>$OuterBubbleAlpha); + else + $OuterColor = array("R"=>$Palette[$ID]["R"]+20,"G"=>$Palette[$ID]["G"]+20,"B"=>$Palette[$ID]["B"]+20,"Alpha"=>$Palette[$ID]["Alpha"]); + + /* Loop to the starting points if asked */ + if ( $LineLoopStart && $DrawLines ) + $Object->drawLine($Points[count($Points)-1][0],$Points[count($Points)-1][1],$Points[0][0],$Points[0][1],$Color); + + /* Draw the lines & points */ + for($i=0; $idrawLine($Points[$i][0],$Points[$i][1],$Points[$i+1][0],$Points[$i+1][1],$Color); + + if ( $DrawPoints ) + $Object->drawFilledCircle($Points[$i][0],$Points[$i][1],$PointRadius,$Color); + + if ( $WriteValuesInBubble && $WriteValues ) + { + $TxtPos = $this->pChartObject->getTextBox($Points[$i][0],$Points[$i][1],$ValueFontName,$ValueFontSize,0,$Points[$i][2]); + $Radius = floor(($TxtPos[1]["X"] - $TxtPos[0]["X"] + $ValuePadding*2)/2); + + $this->pChartObject->drawFilledCircle($Points[$i][0],$Points[$i][1],$Radius+$OuterBubbleRadius,$OuterColor); + $this->pChartObject->drawFilledCircle($Points[$i][0],$Points[$i][1],$Radius,$InnerColor); + } + + if ( $WriteValues ) + $this->pChartObject->drawText($Points[$i][0]-1,$Points[$i][1]-1,$Points[$i][2],$TextSettings); + } + } + } + + + + /* Draw a radar chart */ + function drawPolar($Object,$Values,$Format="") + { + $this->pChartObject = $Object; + + $FixedMax = isset($Format["FixedMax"]) ? $Format["FixedMax"] : VOID; + $AxisR = isset($Format["AxisR"]) ? $Format["AxisR"] : 60; + $AxisG = isset($Format["AxisG"]) ? $Format["AxisG"] : 60; + $AxisB = isset($Format["AxisB"]) ? $Format["AxisB"] : 60; + $AxisAlpha = isset($Format["AxisAlpha"]) ? $Format["AxisAlpha"] : 50; + $AxisRotation = isset($Format["AxisRotation"]) ? $Format["AxisRotation"] : -90; + $DrawTicks = isset($Format["DrawTicks"]) ? $Format["DrawTicks"] : TRUE; + $TicksLength = isset($Format["TicksLength"]) ? $Format["TicksLength"] : 2; + $DrawAxisValues = isset($Format["DrawAxisValues"]) ? $Format["DrawAxisValues"] : TRUE; + $AxisBoxRounded = isset($Format["AxisBoxRounded"]) ? $Format["AxisBoxRounded"] : TRUE; + $AxisFontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->pChartObject->FontName; + $AxisFontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->pChartObject->FontSize; + $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : FALSE; + $WriteValuesInBubble = isset($Format["WriteValuesInBubble"]) ? $Format["WriteValuesInBubble"] : TRUE; + $ValueFontName = isset($Format["ValueFontName"]) ? $Format["ValueFontName"] : $this->pChartObject->FontName; + $ValueFontSize = isset($Format["ValueFontSize"]) ? $Format["ValueFontSize"] : $this->pChartObject->FontSize; + $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 4; + $OuterBubbleRadius = isset($Format["OuterBubbleRadius"]) ? $Format["OuterBubbleRadius"] : 2; + $OuterBubbleR = isset($Format["OuterBubbleR"]) ? $Format["OuterBubbleR"] : VOID; + $OuterBubbleG = isset($Format["OuterBubbleG"]) ? $Format["OuterBubbleG"] : VOID; + $OuterBubbleB = isset($Format["OuterBubbleB"]) ? $Format["OuterBubbleB"] : VOID; + $OuterBubbleAlpha = isset($Format["OuterBubbleAlpha"]) ? $Format["OuterBubbleAlpha"] : 100; + $InnerBubbleR = isset($Format["InnerBubbleR"]) ? $Format["InnerBubbleR"] : 255; + $InnerBubbleG = isset($Format["InnerBubbleG"]) ? $Format["InnerBubbleG"] : 255; + $InnerBubbleB = isset($Format["InnerBubbleB"]) ? $Format["InnerBubbleB"] : 255; + $InnerBubbleAlpha = isset($Format["InnerBubbleAlpha"]) ? $Format["InnerBubbleAlpha"] : 100; + $DrawBackground = isset($Format["DrawBackground"]) ? $Format["DrawBackground"] : TRUE; + $BackgroundR = isset($Format["BackgroundR"]) ? $Format["BackgroundR"] : 255; + $BackgroundG = isset($Format["BackgroundG"]) ? $Format["BackgroundG"] : 255; + $BackgroundB = isset($Format["BackgroundB"]) ? $Format["BackgroundB"] : 255; + $BackgroundAlpha = isset($Format["BackgroundAlpha"]) ? $Format["BackgroundAlpha"] : 50; + $BackgroundGradient= isset($Format["BackgroundGradient"]) ? $Format["BackgroundGradient"] : NULL; + $AxisSteps = isset($Format["AxisSteps"]) ? $Format["AxisSteps"] : 20; + $SegmentHeight = isset($Format["SegmentHeight"]) ? $Format["SegmentHeight"] : SEGMENT_HEIGHT_AUTO; + $Segments = isset($Format["Segments"]) ? $Format["Segments"] : 4; + $WriteLabels = isset($Format["WriteLabels"]) ? $Format["WriteLabels"] : TRUE; + $LabelsBackground = isset($Format["LabelsBackground"]) ? $Format["LabelsBackground"] : TRUE; + $LabelsBGR = isset($Format["LabelsBGR"]) ? $Format["LabelsBGR"] : 255; + $LabelsBGG = isset($Format["LabelsBGR"]) ? $Format["LabelsBGG"] : 255; + $LabelsBGB = isset($Format["LabelsBGR"]) ? $Format["LabelsBGB"] : 255; + $LabelsBGAlpha = isset($Format["LabelsBGAlpha"]) ? $Format["LabelsBGAlpha"] : 50; + $LabelPos = isset($Format["LabelPos"]) ? $Format["LabelPos"] : RADAR_LABELS_ROTATED; + $LabelPadding = isset($Format["LabelPadding"]) ? $Format["LabelPadding"] : 4; + $DrawPoints = isset($Format["DrawPoints"]) ? $Format["DrawPoints"] : TRUE; + $PointRadius = isset($Format["PointRadius"]) ? $Format["PointRadius"] : 4; + $PointSurrounding = isset($Format["PointRadius"]) ? $Format["PointRadius"] : -30; + $DrawLines = isset($Format["DrawLines"]) ? $Format["DrawLines"] : TRUE; + $LineLoopStart = isset($Format["LineLoopStart"]) ? $Format["LineLoopStart"] : FALSE; + $DrawPoly = isset($Format["DrawPoly"]) ? $Format["DrawPoly"] : FALSE; + $PolyAlpha = isset($Format["PolyAlpha"]) ? $Format["PolyAlpha"] : NULL; + $FontSize = $Object->FontSize; + $X1 = $Object->GraphAreaX1; + $Y1 = $Object->GraphAreaY1; + $X2 = $Object->GraphAreaX2; + $Y2 = $Object->GraphAreaY2; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; + + if ( $AxisBoxRounded ) { $DrawAxisValues = TRUE; } + + /* Cancel default tick length if ticks not enabled */ + if ( $DrawTicks == FALSE ) { $TicksLength = 0; } + + /* Data Processing */ + $Data = $Values->getData(); + $Palette = $Values->getPalette(); + + /* Catch the number of required axis */ + $LabelSerie = $Data["Abscissa"]; + if ( $LabelSerie != "" ) + { $Points = count($Data["Series"][$LabelSerie]["Data"]); } + else + { + $Points = 0; + foreach($Data["Series"] as $SerieName => $DataArray) + { if ( count($DataArray["Data"]) > $Points ) { $Points = count($DataArray["Data"]); } } + } + + /* Draw the axis */ + $CenterX = ($X2-$X1)/2 + $X1; + $CenterY = ($Y2-$Y1)/2 + $Y1; + + $EdgeHeight = min(($X2-$X1)/2,($Y2-$Y1)/2); + if ( $WriteLabels ) + $EdgeHeight = $EdgeHeight - $FontSize - $LabelPadding - $TicksLength; + + /* Determine the scale if set to automatic */ + if ( $SegmentHeight == SEGMENT_HEIGHT_AUTO) + { + if ( $FixedMax != VOID ) + $Max = $FixedMax; + else + { + $Max = 0; + foreach($Data["Series"] as $SerieName => $DataArray) + { + if ( $SerieName != $LabelSerie ) + { + if ( max($DataArray["Data"]) > $Max ) { $Max = max($DataArray["Data"]); } + } + } + } + $MaxSegments = $EdgeHeight/20; + $Scale = $Object->computeScale(0,$Max,$MaxSegments,array(1,2,5)); + + $Segments = $Scale["Rows"]; + $SegmentHeight = $Scale["RowHeight"]; + } + + + /* Background processing */ + if ( $DrawBackground ) + { + $RestoreShadow = $Object->Shadow; + $Object->Shadow = FALSE; + + if ($BackgroundGradient == NULL) + { + $Color = array("R"=>$BackgroundR,"G"=>$BackgroundG,"B"=>$BackgroundB,"Alpha"=>$BackgroundAlpha); + $Object->drawFilledCircle($CenterX,$CenterY,$EdgeHeight,$Color); + } + else + { + $GradientROffset = ($BackgroundGradient["EndR"] - $BackgroundGradient["StartR"]) / $Segments; + $GradientGOffset = ($BackgroundGradient["EndG"] - $BackgroundGradient["StartG"]) / $Segments; + $GradientBOffset = ($BackgroundGradient["EndB"] - $BackgroundGradient["StartB"]) / $Segments; + $GradientAlphaOffset = ($BackgroundGradient["EndAlpha"] - $BackgroundGradient["StartAlpha"]) / $Segments; + + for($j=$Segments;$j>=1;$j--) + { + $Color = array("R"=>$BackgroundGradient["StartR"]+$GradientROffset*$j,"G"=>$BackgroundGradient["StartG"]+$GradientGOffset*$j,"B"=>$BackgroundGradient["StartB"]+$GradientBOffset*$j,"Alpha"=>$BackgroundGradient["StartAlpha"]+$GradientAlphaOffset*$j); + $Object->drawFilledCircle($CenterX,$CenterY,($EdgeHeight/$Segments)*$j,$Color); + } + } + $Object->Shadow = $RestoreShadow; + } + + /* Axis to axis lines */ + $Color = array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha); + for($j=1;$j<=$Segments;$j++) + { + $Radius = ($EdgeHeight/$Segments)*$j; + $Object->drawCircle($CenterX,$CenterY,$Radius,$Radius,$Color); + } + + if ( $DrawAxisValues ) + { + if ( $LabelsBackground ) + $Options = array("DrawBox"=>TRUE, "Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"BoxR"=>$LabelsBGR,"BoxG"=>$LabelsBGG,"BoxB"=>$LabelsBGB,"BoxAlpha"=>$LabelsBGAlpha); + else + $Options = array("Align"=>TEXT_ALIGN_MIDDLEMIDDLE); + + if ( $AxisBoxRounded ) { $Options["BoxRounded"] = TRUE; } + + $Options["FontName"] = $AxisFontName; + $Options["FontSize"] = $AxisFontSize; + + $Angle = 360 / ($Points*2); + for($j=1;$j<=$Segments;$j++) + { + $EdgeX1 = cos(deg2rad($Angle+$AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterX; + $EdgeY1 = sin(deg2rad($Angle+$AxisRotation)) * ($EdgeHeight/$Segments)*$j + $CenterY; + $Label = $j*$SegmentHeight; + + $Object->drawText($EdgeX1,$EdgeY1,$Label,$Options); + } + } + + /* Axis lines */ + $ID = 0; + for($i=0;$i<=359;$i=$i+$AxisSteps) + { + $EdgeX = cos(deg2rad($i+$AxisRotation)) * ($EdgeHeight+$TicksLength) + $CenterX; + $EdgeY = sin(deg2rad($i+$AxisRotation)) * ($EdgeHeight+$TicksLength) + $CenterY; + + $Object->drawLine($CenterX,$CenterY,$EdgeX,$EdgeY,$Color); + + if ( $WriteLabels ) + { + $LabelX = cos(deg2rad($i+$AxisRotation)) * ($EdgeHeight+$LabelPadding+$TicksLength) + $CenterX; + $LabelY = sin(deg2rad($i+$AxisRotation)) * ($EdgeHeight+$LabelPadding+$TicksLength) + $CenterY; + $Label = $i."°"; + + if ( $LabelPos == RADAR_LABELS_ROTATED ) + $Object->drawText($LabelX,$LabelY,$Label,array("Angle"=>(360-$i),"Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); + else + { + if ( (floor($LabelX) == floor($CenterX)) && (floor($LabelY) < floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); } + if ( (floor($LabelX) > floor($CenterX)) && (floor($LabelY) < floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_BOTTOMLEFT)); } + if ( (floor($LabelX) > floor($CenterX)) && (floor($LabelY) == floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_MIDDLELEFT)); } + if ( (floor($LabelX) > floor($CenterX)) && (floor($LabelY) > floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_TOPLEFT)); } + if ( (floor($LabelX) < floor($CenterX)) && (floor($LabelY) < floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_BOTTOMRIGHT)); } + if ( (floor($LabelX) < floor($CenterX)) && (floor($LabelY) == floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_MIDDLERIGHT)); } + if ( (floor($LabelX) < floor($CenterX)) && (floor($LabelY) > floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_TOPRIGHT)); } + if ( (floor($LabelX) == floor($CenterX)) && (floor($LabelY) > floor($CenterY)) ) { $Object->drawText($LabelX,$LabelY,$Label,array("Align"=>TEXT_ALIGN_TOPMIDDLE)); } + } + } + $ID++; + } + + /* Compute the plots position */ + $ID = 0; $Plot = ""; + foreach($Data["Series"] as $SerieName => $DataSet) + { + if ( $SerieName != $LabelSerie ) + { + $Color = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"],"Surrounding"=>$PointSurrounding); + foreach($DataSet["Data"] as $Key => $Value) + { + $Angle = $Data["Series"][$LabelSerie]["Data"][$Key]; + $Length = ($EdgeHeight/($Segments*$SegmentHeight))*$Value; + + $X = cos(deg2rad($Angle+$AxisRotation)) * $Length + $CenterX; + $Y = sin(deg2rad($Angle+$AxisRotation)) * $Length + $CenterY; + + if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".floor($PointRadius),$this->pChartObject->toHTMLColor($Palette[$ID]["R"],$Palette[$ID]["G"],$Palette[$ID]["B"]),$DataSet["Description"],$Data["Series"][$LabelSerie]["Data"][$Key]."° = ".$Value); } + + $Plot[$ID][] = array($X,$Y,$Value); + } + $ID++; + } + } + + /* Draw all that stuff! */ + foreach($Plot as $ID => $Points) + { + $Color = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"],"Surrounding"=>$PointSurrounding); + + /* Draw the polygons */ + if ( $DrawPoly ) + { + if ($PolyAlpha != NULL) + $Color = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$PolyAlpha,"Surrounding"=>$PointSurrounding); + + $PointsArray = ""; + for($i=0; $idrawPolygon($PointsArray,$Color); + } + + $Color = array("R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"],"Alpha"=>$Palette[$ID]["Alpha"],"Surrounding"=>$PointSurrounding); + + /* Bubble and labels settings */ + $TextSettings = array("Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontName"=>$ValueFontName,"FontSize"=>$ValueFontSize,"R"=>$Palette[$ID]["R"],"G"=>$Palette[$ID]["G"],"B"=>$Palette[$ID]["B"]); + $InnerColor = array("R"=>$InnerBubbleR,"G"=>$InnerBubbleG,"B"=>$InnerBubbleB,"Alpha"=>$InnerBubbleAlpha); + if ( $OuterBubbleR != VOID ) + $OuterColor = array("R"=>$OuterBubbleR,"G"=>$OuterBubbleG,"B"=>$OuterBubbleB,"Alpha"=>$OuterBubbleAlpha); + else + $OuterColor = array("R"=>$Palette[$ID]["R"]+20,"G"=>$Palette[$ID]["G"]+20,"B"=>$Palette[$ID]["B"]+20,"Alpha"=>$Palette[$ID]["Alpha"]); + + /* Loop to the starting points if asked */ + if ( $LineLoopStart && $DrawLines ) + $Object->drawLine($Points[count($Points)-1][0],$Points[count($Points)-1][1],$Points[0][0],$Points[0][1],$Color); + + /* Draw the lines & points */ + for($i=0; $idrawLine($Points[$i][0],$Points[$i][1],$Points[$i+1][0],$Points[$i+1][1],$Color); + + if ( $DrawPoints ) + $Object->drawFilledCircle($Points[$i][0],$Points[$i][1],$PointRadius,$Color); + + if ( $WriteValuesInBubble && $WriteValues ) + { + $TxtPos = $this->pChartObject->getTextBox($Points[$i][0],$Points[$i][1],$ValueFontName,$ValueFontSize,0,$Points[$i][2]); + $Radius = floor(($TxtPos[1]["X"] - $TxtPos[0]["X"] + $ValuePadding*2)/2); + + $this->pChartObject->drawFilledCircle($Points[$i][0],$Points[$i][1],$Radius+$OuterBubbleRadius,$OuterColor); + $this->pChartObject->drawFilledCircle($Points[$i][0],$Points[$i][1],$Radius,$InnerColor); + } + + if ( $WriteValues ) + $this->pChartObject->drawText($Points[$i][0]-1,$Points[$i][1]-1,$Points[$i][2],$TextSettings); + } + } + } + } ?> \ No newline at end of file diff --git a/www/libs/pChart2.1.3/class/pScatter.class.php b/www/libs/pChart2.1.4/class/pScatter.class.php similarity index 98% rename from www/libs/pChart2.1.3/class/pScatter.class.php rename to www/libs/pChart2.1.4/class/pScatter.class.php index a83ce399..6129fc8b 100644 --- a/www/libs/pChart2.1.3/class/pScatter.class.php +++ b/www/libs/pChart2.1.4/class/pScatter.class.php @@ -1,1158 +1,1158 @@ -pChartObject = $pChartObject; - $this->pDataObject = $pDataObject; - } - - /* Prepare the scale */ - function drawScatterScale($Format="") - { - $Mode = isset($Format["Mode"]) ? $Format["Mode"] : SCALE_MODE_FLOATING; - $Floating = isset($Format["Floating"]) ? $Format["Floating"] : FALSE; - $XLabelsRotation = isset($Format["XLabelsRotation"]) ? $Format["XLabelsRotation"] : 90; - $MinDivHeight = isset($Format["MinDivHeight"]) ? $Format["MinDivHeight"] : 20; - $Factors = isset($Format["Factors"]) ? $Format["Factors"] : array(1,2,5); - $ManualScale = isset($Format["ManualScale"]) ? $Format["ManualScale"] : array("0"=>array("Min"=>-100,"Max"=>100)); - $XMargin = isset($Format["XMargin"]) ? $Format["XMargin"] : 0; - $YMargin = isset($Format["YMargin"]) ? $Format["YMargin"] : 0; - $ScaleSpacing = isset($Format["ScaleSpacing"]) ? $Format["ScaleSpacing"] : 15; - $InnerTickWidth = isset($Format["InnerTickWidth"]) ? $Format["InnerTickWidth"] : 2; - $OuterTickWidth = isset($Format["OuterTickWidth"]) ? $Format["OuterTickWidth"] : 2; - $DrawXLines = isset($Format["DrawXLines"]) ? $Format["DrawXLines"] : ALL; - $DrawYLines = isset($Format["DrawYLines"]) ? $Format["DrawYLines"] : ALL; - $GridTicks = isset($Format["GridTicks"]) ? $Format["GridTicks"] : 4; - $GridR = isset($Format["GridR"]) ? $Format["GridR"] : 255; - $GridG = isset($Format["GridG"]) ? $Format["GridG"] : 255; - $GridB = isset($Format["GridB"]) ? $Format["GridB"] : 255; - $GridAlpha = isset($Format["GridAlpha"]) ? $Format["GridAlpha"] : 40; - $AxisRo = isset($Format["AxisR"]) ? $Format["AxisR"] : 0; - $AxisGo = isset($Format["AxisG"]) ? $Format["AxisG"] : 0; - $AxisBo = isset($Format["AxisB"]) ? $Format["AxisB"] : 0; - $AxisAlpha = isset($Format["AxisAlpha"]) ? $Format["AxisAlpha"] : 100; - $TickRo = isset($Format["TickR"]) ? $Format["TickR"] : 0; - $TickGo = isset($Format["TickG"]) ? $Format["TickG"] : 0; - $TickBo = isset($Format["TickB"]) ? $Format["TickB"] : 0; - $TickAlpha = isset($Format["TickAlpha"]) ? $Format["TickAlpha"] : 100; - $DrawSubTicks = isset($Format["DrawSubTicks"]) ? $Format["DrawSubTicks"] : FALSE; - $InnerSubTickWidth = isset($Format["InnerSubTickWidth"]) ? $Format["InnerSubTickWidth"] : 0; - $OuterSubTickWidth = isset($Format["OuterSubTickWidth"]) ? $Format["OuterSubTickWidth"] : 2; - $SubTickR = isset($Format["SubTickR"]) ? $Format["SubTickR"] : 255; - $SubTickG = isset($Format["SubTickG"]) ? $Format["SubTickG"] : 0; - $SubTickB = isset($Format["SubTickB"]) ? $Format["SubTickB"] : 0; - $SubTickAlpha = isset($Format["SubTickAlpha"]) ? $Format["SubTickAlpha"] : 100; - $XReleasePercent = isset($Format["XReleasePercent"]) ? $Format["XReleasePercent"] : 1; - $DrawArrows = isset($Format["DrawArrows"]) ? $Format["DrawArrows"] : FALSE; - $ArrowSize = isset($Format["ArrowSize"]) ? $Format["ArrowSize"] : 8; - $CycleBackground = isset($Format["CycleBackground"]) ? $Format["CycleBackground"] : FALSE; - $BackgroundR1 = isset($Format["BackgroundR1"]) ? $Format["BackgroundR1"] : 255; - $BackgroundG1 = isset($Format["BackgroundG1"]) ? $Format["BackgroundG1"] : 255; - $BackgroundB1 = isset($Format["BackgroundB1"]) ? $Format["BackgroundB1"] : 255; - $BackgroundAlpha1 = isset($Format["BackgroundAlpha1"]) ? $Format["BackgroundAlpha1"] : 10; - $BackgroundR2 = isset($Format["BackgroundR2"]) ? $Format["BackgroundR2"] : 230; - $BackgroundG2 = isset($Format["BackgroundG2"]) ? $Format["BackgroundG2"] : 230; - $BackgroundB2 = isset($Format["BackgroundB2"]) ? $Format["BackgroundB2"] : 230; - $BackgroundAlpha2 = isset($Format["BackgroundAlpha2"]) ? $Format["BackgroundAlpha2"] : 10; - - /* Check if we have at least both one X and Y axis */ - $GotXAxis = FALSE; $GotYAxis = FALSE; - foreach($this->pDataObject->Data["Axis"] as $AxisID => $AxisSettings) - { - if ( $AxisSettings["Identity"] == AXIS_X ) { $GotXAxis = TRUE; } - if ( $AxisSettings["Identity"] == AXIS_Y ) { $GotYAxis = TRUE; } - } - if ( !$GotXAxis ) { return(SCATTER_MISSING_X_SERIE); } - if ( !$GotYAxis ) { return(SCATTER_MISSING_Y_SERIE); } - - /* Skip a NOTICE event in case of an empty array */ - if ( $DrawYLines == NONE ) { $DrawYLines = array("zarma"=>"31"); } - - $Data = $this->pDataObject->getData(); - - foreach($Data["Axis"] as $AxisID => $AxisSettings) - { - if ( $AxisSettings["Identity"] == AXIS_X) - { $Width = $this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1 - $XMargin*2; } - else - { $Width = $this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1 - $YMargin*2; } - - $AxisMin = ABSOLUTE_MAX; $AxisMax = OUT_OF_SIGHT; - if ( $Mode == SCALE_MODE_FLOATING ) - { - foreach($Data["Series"] as $SerieID => $SerieParameter) - { - if ( $SerieParameter["Axis"] == $AxisID && $Data["Series"][$SerieID]["isDrawable"] ) - { - $AxisMax = max($AxisMax,$Data["Series"][$SerieID]["Max"]); - $AxisMin = min($AxisMin,$Data["Series"][$SerieID]["Min"]); - } - } - $AutoMargin = (($AxisMax-$AxisMin)/100)*$XReleasePercent; - - $Data["Axis"][$AxisID]["Min"] = $AxisMin-$AutoMargin; $Data["Axis"][$AxisID]["Max"] = $AxisMax+$AutoMargin; - } - elseif ( $Mode == SCALE_MODE_MANUAL ) - { - if ( isset($ManualScale[$AxisID]["Min"]) && isset($ManualScale[$AxisID]["Max"]) ) - { - $Data["Axis"][$AxisID]["Min"] = $ManualScale[$AxisID]["Min"]; - $Data["Axis"][$AxisID]["Max"] = $ManualScale[$AxisID]["Max"]; - } - else - { echo "Manual scale boundaries not set."; exit(); } - } - - /* Full manual scale */ - if ( isset($ManualScale[$AxisID]["Rows"]) && isset($ManualScale[$AxisID]["RowHeight"]) ) - $Scale = array("Rows"=>$ManualScale[$AxisID]["Rows"],"RowHeight"=>$ManualScale[$AxisID]["RowHeight"],"XMin"=>$ManualScale[$AxisID]["Min"],"XMax"=>$ManualScale[$AxisID]["Max"]); - else - { - $MaxDivs = floor($Width/$MinDivHeight); - $Scale = $this->pChartObject->computeScale($Data["Axis"][$AxisID]["Min"],$Data["Axis"][$AxisID]["Max"],$MaxDivs,$Factors,$AxisID); - } - - $Data["Axis"][$AxisID]["Margin"] = $AxisSettings["Identity"] == AXIS_X ? $XMargin : $YMargin; - $Data["Axis"][$AxisID]["ScaleMin"] = $Scale["XMin"]; - $Data["Axis"][$AxisID]["ScaleMax"] = $Scale["XMax"]; - $Data["Axis"][$AxisID]["Rows"] = $Scale["Rows"]; - $Data["Axis"][$AxisID]["RowHeight"] = $Scale["RowHeight"]; - - if ( isset($Scale["Format"]) ) { $Data["Axis"][$AxisID]["Format"] = $Scale["Format"]; } - - if ( !isset($Data["Axis"][$AxisID]["Display"]) ) { $Data["Axis"][$AxisID]["Display"] = NULL; } - if ( !isset($Data["Axis"][$AxisID]["Format"]) ) { $Data["Axis"][$AxisID]["Format"] = NULL; } - if ( !isset($Data["Axis"][$AxisID]["Unit"]) ) { $Data["Axis"][$AxisID]["Unit"] = NULL; } - } - - /* Get the default font color */ - $FontColorRo = $this->pChartObject->FontColorR; $FontColorGo = $this->pChartObject->FontColorG; $FontColorBo = $this->pChartObject->FontColorB; - - /* Set the original boundaries */ - $AxisPos["L"] = $this->pChartObject->GraphAreaX1; $AxisPos["R"] = $this->pChartObject->GraphAreaX2; $AxisPos["T"] = $this->pChartObject->GraphAreaY1; $AxisPos["B"] = $this->pChartObject->GraphAreaY2; - - foreach($Data["Axis"] as $AxisID => $AxisSettings) - { - if ( isset($AxisSettings["Color"]) ) - { - $AxisR = $AxisSettings["Color"]["R"]; $AxisG = $AxisSettings["Color"]["G"]; $AxisB = $AxisSettings["Color"]["B"]; - $TickR = $AxisSettings["Color"]["R"]; $TickG = $AxisSettings["Color"]["G"]; $TickB = $AxisSettings["Color"]["B"]; - $this->pChartObject->setFontProperties(array("R"=>$AxisSettings["Color"]["R"],"G"=>$AxisSettings["Color"]["G"],"B"=>$AxisSettings["Color"]["B"])); - } - else - { - $AxisR = $AxisRo; $AxisG = $AxisGo; $AxisB = $AxisBo; - $TickR = $TickRo; $TickG = $TickGo; $TickB = $TickBo; - $this->pChartObject->setFontProperties(array("R"=>$FontColorRo,"G"=>$FontColorGo,"B"=>$FontColorBo)); - } - - $LastValue = "w00t"; $ID = 1; - if ( $AxisSettings["Identity"] == AXIS_X ) - { - if ( $AxisSettings["Position"] == AXIS_POSITION_BOTTOM ) - { - if ( $XLabelsRotation == 0 ) { $LabelAlign = TEXT_ALIGN_TOPMIDDLE; $LabelOffset = 2; } - if ( $XLabelsRotation > 0 && $XLabelsRotation < 190 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $LabelOffset = 5; } - if ( $XLabelsRotation == 180 ) { $LabelAlign = TEXT_ALIGN_BOTTOMMIDDLE; $LabelOffset = 5; } - if ( $XLabelsRotation > 180 && $XLabelsRotation < 360 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $LabelOffset = 2; } - - if ( $Floating ) - { $FloatingOffset = $YMargin; $this->pChartObject->drawLine($this->pChartObject->GraphAreaX1+$AxisSettings["Margin"],$AxisPos["B"],$this->pChartObject->GraphAreaX2-$AxisSettings["Margin"],$AxisPos["B"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - else - { $FloatingOffset = 0; $this->pChartObject->drawLine($this->pChartObject->GraphAreaX1,$AxisPos["B"],$this->pChartObject->GraphAreaX2,$AxisPos["B"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - - if ( $DrawArrows ) { $this->pChartObject->drawArrow($this->pChartObject->GraphAreaX2-$AxisSettings["Margin"],$AxisPos["B"],$this->pChartObject->GraphAreaX2+($ArrowSize*2),$AxisPos["B"],array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } - - $Width = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1) - $AxisSettings["Margin"]*2; - $Step = $Width / $AxisSettings["Rows"]; $SubTicksSize = $Step /2; $MaxBottom = $AxisPos["B"]; - $LastX = NULL; - for($i=0;$i<=$AxisSettings["Rows"];$i++) - { - $XPos = $this->pChartObject->GraphAreaX1 + $AxisSettings["Margin"] + $Step*$i; - $YPos = $AxisPos["B"]; - $Value = $this->pChartObject->scaleFormat($AxisSettings["ScaleMin"] + $AxisSettings["RowHeight"]*$i,$AxisSettings["Display"],$AxisSettings["Format"],$AxisSettings["Unit"]); - - if ( $i%2 == 1 ) { $BGColor = array("R"=>$BackgroundR1,"G"=>$BackgroundG1,"B"=>$BackgroundB1,"Alpha"=>$BackgroundAlpha1); } else { $BGColor = array("R"=>$BackgroundR2,"G"=>$BackgroundG2,"B"=>$BackgroundB2,"Alpha"=>$BackgroundAlpha2); } - if ( $LastX != NULL && $CycleBackground && ( $DrawXLines == ALL || in_array($AxisID,$DrawXLines) )) { $this->pChartObject->drawFilledRectangle($LastX,$this->pChartObject->GraphAreaY1+$FloatingOffset,$XPos,$this->pChartObject->GraphAreaY2-$FloatingOffset,$BGColor); } - - if ( $DrawXLines == ALL || in_array($AxisID,$DrawXLines) ) { $this->pChartObject->drawLine($XPos,$this->pChartObject->GraphAreaY1+$FloatingOffset,$XPos,$this->pChartObject->GraphAreaY2-$FloatingOffset,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } - if ( $DrawSubTicks && $i != $AxisSettings["Rows"] ) - $this->pChartObject->drawLine($XPos+$SubTicksSize,$YPos-$InnerSubTickWidth,$XPos+$SubTicksSize,$YPos+$OuterSubTickWidth,array("R"=>$SubTickR,"G"=>$SubTickG,"B"=>$SubTickB,"Alpha"=>$SubTickAlpha)); - - $this->pChartObject->drawLine($XPos,$YPos-$InnerTickWidth,$XPos,$YPos+$OuterTickWidth,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); - $Bounds = $this->pChartObject->drawText($XPos,$YPos+$OuterTickWidth+$LabelOffset,$Value,array("Angle"=>$XLabelsRotation,"Align"=>$LabelAlign)); - $TxtBottom = $YPos+2+$OuterTickWidth+2+($Bounds[0]["Y"]-$Bounds[2]["Y"]); - $MaxBottom = max($MaxBottom,$TxtBottom); - - $LastX = $XPos; - } - - if ( isset($AxisSettings["Name"]) ) - { - $YPos = $MaxBottom+2; - $XPos = $this->pChartObject->GraphAreaX1+($this->pChartObject->GraphAreaX2-$this->pChartObject->GraphAreaX1)/2; - $Bounds = $this->pChartObject->drawText($XPos,$YPos,$AxisSettings["Name"],array("Align"=>TEXT_ALIGN_TOPMIDDLE)); - $MaxBottom = $Bounds[0]["Y"]; - - $this->pDataObject->Data["GraphArea"]["Y2"] = $MaxBottom + $this->pChartObject->FontSize; - } - - $AxisPos["B"] = $MaxBottom + $ScaleSpacing; - } - elseif ( $AxisSettings["Position"] == AXIS_POSITION_TOP ) - { - if ( $XLabelsRotation == 0 ) { $LabelAlign = TEXT_ALIGN_BOTTOMMIDDLE; $LabelOffset = 2; } - if ( $XLabelsRotation > 0 && $XLabelsRotation < 190 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $LabelOffset = 2; } - if ( $XLabelsRotation == 180 ) { $LabelAlign = TEXT_ALIGN_TOPMIDDLE; $LabelOffset = 5; } - if ( $XLabelsRotation > 180 && $SLabelxRotation < 360 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $LabelOffset = 5; } - - if ( $Floating ) - { $FloatingOffset = $YMargin; $this->pChartObject->drawLine($this->pChartObject->GraphAreaX1+$AxisSettings["Margin"],$AxisPos["T"],$this->pChartObject->GraphAreaX2-$AxisSettings["Margin"],$AxisPos["T"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - else - { $FloatingOffset = 0; $this->pChartObject->drawLine($this->pChartObject->GraphAreaX1,$AxisPos["T"],$this->pChartObject->GraphAreaX2,$AxisPos["T"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - - if ( $DrawArrows ) { $this->pChartObject->drawArrow($this->pChartObject->GraphAreaX2-$AxisSettings["Margin"],$AxisPos["T"],$this->pChartObject->GraphAreaX2+($ArrowSize*2),$AxisPos["T"],array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } - - $Width = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1) - $AxisSettings["Margin"]*2; - $Step = $Width / $AxisSettings["Rows"]; $SubTicksSize = $Step /2; $MinTop = $AxisPos["T"]; - $LastX = NULL; - for($i=0;$i<=$AxisSettings["Rows"];$i++) - { - $XPos = $this->pChartObject->GraphAreaX1 + $AxisSettings["Margin"] + $Step*$i; - $YPos = $AxisPos["T"]; - $Value = $this->pChartObject->scaleFormat($AxisSettings["ScaleMin"] + $AxisSettings["RowHeight"]*$i,$AxisSettings["Display"],$AxisSettings["Format"],$AxisSettings["Unit"]); - - if ( $i%2 == 1 ) { $BGColor = array("R"=>$BackgroundR1,"G"=>$BackgroundG1,"B"=>$BackgroundB1,"Alpha"=>$BackgroundAlpha1); } else { $BGColor = array("R"=>$BackgroundR2,"G"=>$BackgroundG2,"B"=>$BackgroundB2,"Alpha"=>$BackgroundAlpha2); } - if ( $LastX != NULL && $CycleBackground && ( $DrawXLines == ALL || in_array($AxisID,$DrawXLines) )) { $this->pChartObject->drawFilledRectangle($LastX,$this->pChartObject->GraphAreaY1+$FloatingOffset,$XPos,$this->pChartObject->GraphAreaY2-$FloatingOffset,$BGColor); } - - if ( $DrawXLines == ALL || in_array($AxisID,$DrawXLines) ) { $this->pChartObject->drawLine($XPos,$this->pChartObject->GraphAreaY1+$FloatingOffset,$XPos,$this->pChartObject->GraphAreaY2-$FloatingOffset,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } - - if ( $DrawSubTicks && $i != $AxisSettings["Rows"] ) - $this->pChartObject->drawLine($XPos+$SubTicksSize,$YPos-$OuterSubTickWidth,$XPos+$SubTicksSize,$YPos+$InnerSubTickWidth,array("R"=>$SubTickR,"G"=>$SubTickG,"B"=>$SubTickB,"Alpha"=>$SubTickAlpha)); - - $this->pChartObject->drawLine($XPos,$YPos-$OuterTickWidth,$XPos,$YPos+$InnerTickWidth,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); - $Bounds = $this->pChartObject->drawText($XPos,$YPos-$OuterTickWidth-$LabelOffset,$Value,array("Angle"=>$XLabelsRotation,"Align"=>$LabelAlign)); - $TxtBox = $YPos-$OuterTickWidth-4-($Bounds[0]["Y"]-$Bounds[2]["Y"]); - $MinTop = min($MinTop,$TxtBox); - - $LastX = $XPos; - } - - if ( isset($AxisSettings["Name"]) ) - { - $YPos = $MinTop-2; - $XPos = $this->pChartObject->GraphAreaX1+($this->pChartObject->GraphAreaX2-$this->pChartObject->GraphAreaX1)/2; - $Bounds = $this->pChartObject->drawText($XPos,$YPos,$AxisSettings["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); - $MinTop = $Bounds[2]["Y"]; - - $this->pDataObject->Data["GraphArea"]["Y1"] = $MinTop; - } - - $AxisPos["T"] = $MinTop - $ScaleSpacing; - } - } - elseif ( $AxisSettings["Identity"] == AXIS_Y ) - { - if ( $AxisSettings["Position"] == AXIS_POSITION_LEFT ) - { - - if ( $Floating ) - { $FloatingOffset = $XMargin; $this->pChartObject->drawLine($AxisPos["L"],$this->pChartObject->GraphAreaY1+$AxisSettings["Margin"],$AxisPos["L"],$this->pChartObject->GraphAreaY2-$AxisSettings["Margin"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - else - { $FloatingOffset = 0; $this->pChartObject->drawLine($AxisPos["L"],$this->pChartObject->GraphAreaY1,$AxisPos["L"],$this->pChartObject->GraphAreaY2,array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - - if ( $DrawArrows ) { $this->pChartObject->drawArrow($AxisPos["L"],$this->pChartObject->GraphAreaY1+$AxisSettings["Margin"],$AxisPos["L"],$this->pChartObject->GraphAreaY1-($ArrowSize*2),array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } - - $Height = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1) - $AxisSettings["Margin"]*2; - $Step = $Height / $AxisSettings["Rows"]; $SubTicksSize = $Step /2; $MinLeft = $AxisPos["L"]; - $LastY = NULL; - for($i=0;$i<=$AxisSettings["Rows"];$i++) - { - $YPos = $this->pChartObject->GraphAreaY2 - $AxisSettings["Margin"] - $Step*$i; - $XPos = $AxisPos["L"]; - $Value = $this->pChartObject->scaleFormat($AxisSettings["ScaleMin"] + $AxisSettings["RowHeight"]*$i,$AxisSettings["Display"],$AxisSettings["Format"],$AxisSettings["Unit"]); - - if ( $i%2 == 1 ) { $BGColor = array("R"=>$BackgroundR1,"G"=>$BackgroundG1,"B"=>$BackgroundB1,"Alpha"=>$BackgroundAlpha1); } else { $BGColor = array("R"=>$BackgroundR2,"G"=>$BackgroundG2,"B"=>$BackgroundB2,"Alpha"=>$BackgroundAlpha2); } - if ( $LastY != NULL && $CycleBackground && ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) )) { $this->pChartObject->drawFilledRectangle($this->pChartObject->GraphAreaX1+$FloatingOffset,$LastY,$this->pChartObject->GraphAreaX2-$FloatingOffset,$YPos,$BGColor); } - - if ( ($YPos != $this->pChartObject->GraphAreaY1 && $YPos != $this->pChartObject->GraphAreaY2) && ($DrawYLines == ALL || in_array($AxisID,$DrawYLines) )) { $this->pChartObject->drawLine($this->pChartObject->GraphAreaX1+$FloatingOffset,$YPos,$this->pChartObject->GraphAreaX2-$FloatingOffset,$YPos,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } - - if ( $DrawSubTicks && $i != $AxisSettings["Rows"] ) - $this->pChartObject->drawLine($XPos-$OuterSubTickWidth,$YPos-$SubTicksSize,$XPos+$InnerSubTickWidth,$YPos-$SubTicksSize,array("R"=>$SubTickR,"G"=>$SubTickG,"B"=>$SubTickB,"Alpha"=>$SubTickAlpha)); - - $this->pChartObject->drawLine($XPos-$OuterTickWidth,$YPos,$XPos+$InnerTickWidth,$YPos,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); - $Bounds = $this->pChartObject->drawText($XPos-$OuterTickWidth-2,$YPos,$Value,array("Align"=>TEXT_ALIGN_MIDDLERIGHT)); - $TxtLeft = $XPos-$OuterTickWidth-2-($Bounds[1]["X"]-$Bounds[0]["X"]); - $MinLeft = min($MinLeft,$TxtLeft); - - $LastY = $YPos; - } - - if ( isset($AxisSettings["Name"]) ) - { - $XPos = $MinLeft-2; - $YPos = $this->pChartObject->GraphAreaY1+($this->pChartObject->GraphAreaY2-$this->pChartObject->GraphAreaY1)/2; - $Bounds = $this->pChartObject->drawText($XPos,$YPos,$AxisSettings["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE,"Angle"=>90)); - $MinLeft = $Bounds[2]["X"]; - - $this->pDataObject->Data["GraphArea"]["X1"] = $MinLeft; - } - - $AxisPos["L"] = $MinLeft - $ScaleSpacing; - } - elseif ( $AxisSettings["Position"] == AXIS_POSITION_RIGHT ) - { - - if ( $Floating ) - { $FloatingOffset = $XMargin; $this->pChartObject->drawLine($AxisPos["R"],$this->pChartObject->GraphAreaY1+$AxisSettings["Margin"],$AxisPos["R"],$this->pChartObject->GraphAreaY2-$AxisSettings["Margin"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - else - { $FloatingOffset = 0; $this->pChartObject->drawLine($AxisPos["R"],$this->pChartObject->GraphAreaY1,$AxisPos["R"],$this->pChartObject->GraphAreaY2,array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } - - if ( $DrawArrows ) { $this->pChartObject->drawArrow($AxisPos["R"],$this->pChartObject->GraphAreaY1+$AxisSettings["Margin"],$AxisPos["R"],$this->pChartObject->GraphAreaY1-($ArrowSize*2),array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } - - $Height = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1) - $AxisSettings["Margin"]*2; - $Step = $Height / $AxisSettings["Rows"]; $SubTicksSize = $Step /2; $MaxLeft = $AxisPos["R"]; - $LastY = NULL; - for($i=0;$i<=$AxisSettings["Rows"];$i++) - { - $YPos = $this->pChartObject->GraphAreaY2 - $AxisSettings["Margin"] - $Step*$i; - $XPos = $AxisPos["R"]; - $Value = $this->pChartObject->scaleFormat($AxisSettings["ScaleMin"] + $AxisSettings["RowHeight"]*$i,$AxisSettings["Display"],$AxisSettings["Format"],$AxisSettings["Unit"]); - - if ( $i%2 == 1 ) { $BGColor = array("R"=>$BackgroundR1,"G"=>$BackgroundG1,"B"=>$BackgroundB1,"Alpha"=>$BackgroundAlpha1); } else { $BGColor = array("R"=>$BackgroundR2,"G"=>$BackgroundG2,"B"=>$BackgroundB2,"Alpha"=>$BackgroundAlpha2); } - if ( $LastY != NULL && $CycleBackground && ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) )) { $this->pChartObject->drawFilledRectangle($this->pChartObject->GraphAreaX1+$FloatingOffset,$LastY,$this->pChartObject->GraphAreaX2-$FloatingOffset,$YPos,$BGColor); } - - if ( ($YPos != $this->pChartObject->GraphAreaY1 && $YPos != $this->pChartObject->GraphAreaY2) && ($DrawYLines == ALL || in_array($AxisID,$DrawYLines)) ) { $this->pChartObject->drawLine($this->pChartObject->GraphAreaX1+$FloatingOffset,$YPos,$this->pChartObject->GraphAreaX2-$FloatingOffset,$YPos,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } - - if ( $DrawSubTicks && $i != $AxisSettings["Rows"] ) - $this->pChartObject->drawLine($XPos-$InnerSubTickWidth,$YPos-$SubTicksSize,$XPos+$OuterSubTickWidth,$YPos-$SubTicksSize,array("R"=>$SubTickR,"G"=>$SubTickG,"B"=>$SubTickB,"Alpha"=>$SubTickAlpha)); - - $this->pChartObject->drawLine($XPos-$InnerTickWidth,$YPos,$XPos+$OuterTickWidth,$YPos,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); - $Bounds = $this->pChartObject->drawText($XPos+$OuterTickWidth+2,$YPos,$Value,array("Align"=>TEXT_ALIGN_MIDDLELEFT)); - $TxtLeft = $XPos+$OuterTickWidth+2+($Bounds[1]["X"]-$Bounds[0]["X"]); - $MaxLeft = max($MaxLeft,$TxtLeft); - - $LastY = $YPos; - } - - if ( isset($AxisSettings["Name"]) ) - { - $XPos = $MaxLeft+6; - $YPos = $this->pChartObject->GraphAreaY1+($this->pChartObject->GraphAreaY2-$this->pChartObject->GraphAreaY1)/2; - $Bounds = $this->pChartObject->drawText($XPos,$YPos,$AxisSettings["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE,"Angle"=>270)); - $MaxLeft = $Bounds[2]["X"]; - - $this->pDataObject->Data["GraphArea"]["X2"] = $MaxLeft + $this->pChartObject->FontSize; - } - - $AxisPos["R"] = $MaxLeft + $ScaleSpacing; - } - } - } - - $this->pDataObject->saveAxisConfig($Data["Axis"]); - } - - /* Draw a scatter plot chart */ - function drawScatterPlotChart($Format=NULL) - { - $PlotSize = isset($Format["PlotSize"]) ? $Format["PlotSize"] : 3; - $PlotBorder = isset($Format["PlotBorder"]) ? $Format["PlotBorder"] : FALSE; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 250; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 250; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 250; - $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 30; - $BorderSize = isset($Format["BorderSize"]) ? $Format["BorderSize"] : 1; - $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; - $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; - $ImageMapTitle = isset($Format["ImageMapTitle"]) ? $Format["ImageMapTitle"] : NULL; - $ImageMapPrecision = isset($Format["ImageMapPrecision"]) ? $Format["ImageMapPrecision"] : 2; - - $Data = $this->pDataObject->getData(); - $Palette = $this->pDataObject->getPalette(); - - $BorderColor = array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha); - - foreach($Data["ScatterSeries"] as $Key => $Series) - { - if ( $Series["isDrawable"] == TRUE ) - { - $SerieX = $Series["X"]; $SerieValuesX = $Data["Series"][$SerieX]["Data"]; $SerieXAxis = $Data["Series"][$SerieX]["Axis"]; - $SerieY = $Series["Y"]; $SerieValuesY = $Data["Series"][$SerieY]["Data"]; $SerieYAxis = $Data["Series"][$SerieY]["Axis"]; - - if ( $ImageMapTitle == NULL ) { $Description = $Data["Series"][$Series["X"]]["Description"]." / ".$Data["Series"][$Series["Y"]]["Description"]; } else { $Description = $ImageMapTitle; } - - if ( isset($Series["Picture"]) && $Series["Picture"] != "" ) - { $Picture = $Series["Picture"]; list($PicWidth,$PicHeight,$PicType) = $this->pChartObject->getPicInfo($Picture); } - else - { $Picture = NULL; } - - $PosArrayX = $this->getPosArray($SerieValuesX,$SerieXAxis); - if ( !is_array($PosArrayX) ) { $Value = $PosArrayX; $PosArrayX = ""; $PosArrayX[0] = $Value; } - $PosArrayY = $this->getPosArray($SerieValuesY,$SerieYAxis); - if ( !is_array($PosArrayY) ) { $Value = $PosArrayY; $PosArrayY = ""; $PosArrayY[0] = $Value; } - - $Color = array("R"=>$Series["Color"]["R"],"G"=>$Series["Color"]["G"],"B"=>$Series["Color"]["B"],"Alpha"=>$Series["Color"]["Alpha"]); - - foreach($PosArrayX as $Key => $Value) - { - $X = $Value; $Y = $PosArrayY[$Key]; - - if ( $X != VOID && $Y != VOID ) - { - $RealValue = round($Data["Series"][$Series["X"]]["Data"][$Key],2)." / ".round($Data["Series"][$Series["Y"]]["Data"][$Key],2); - if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".floor($PlotSize+$BorderSize),$this->pChartObject->toHTMLColor($Series["Color"]["R"],$Series["Color"]["G"],$Series["Color"]["B"]),$Description,$RealValue); } - - if( isset($Series["Shape"]) ) - { $this->pChartObject->drawShape($X,$Y,$Series["Shape"],$PlotSize,$PlotBorder,$BorderSize,$Series["Color"]["R"],$Series["Color"]["G"],$Series["Color"]["B"],$Series["Color"]["Alpha"],$BorderR,$BorderG,$BorderB,$BorderAlpha); } - elseif ( $Picture == NULL ) - { - if ( $PlotBorder ) { $this->pChartObject->drawFilledCircle($X,$Y,$PlotSize+$BorderSize,$BorderColor); } - $this->pChartObject->drawFilledCircle($X,$Y,$PlotSize,$Color); - } - else - { $this->pChartObject->drawFromPicture($PicType,$Picture,$X-$PicWidth/2,$Y-$PicHeight/2); } - } - } - } - } - } - - /* Draw a scatter line chart */ - function drawScatterLineChart($Format=NULL) - { - $Data = $this->pDataObject->getData(); - $Palette = $this->pDataObject->getPalette(); - $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; - $ImageMapTitle = isset($Format["ImageMapTitle"]) ? $Format["ImageMapTitle"] : NULL; - $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 10; - $ImageMapPrecision = isset($Format["ImageMapPrecision"]) ? $Format["ImageMapPrecision"] : 2; - - /* Parse all the series to draw */ - foreach($Data["ScatterSeries"] as $Key => $Series) - { - if ( $Series["isDrawable"] == TRUE ) - { - $SerieX = $Series["X"]; $SerieValuesX = $Data["Series"][$SerieX]["Data"]; $SerieXAxis = $Data["Series"][$SerieX]["Axis"]; - $SerieY = $Series["Y"]; $SerieValuesY = $Data["Series"][$SerieY]["Data"]; $SerieYAxis = $Data["Series"][$SerieY]["Axis"]; - $Ticks = $Series["Ticks"]; - $Weight = $Series["Weight"]; - - if ( $ImageMapTitle == NULL ) { $Description = $Data["Series"][$Series["X"]]["Description"]." / ".$Data["Series"][$Series["Y"]]["Description"]; } else { $Description = $ImageMapTitle; } - - $PosArrayX = $this->getPosArray($SerieValuesX,$SerieXAxis); - if ( !is_array($PosArrayX) ) { $Value = $PosArrayX; $PosArrayX = ""; $PosArrayX[0] = $Value; } - $PosArrayY = $this->getPosArray($SerieValuesY,$SerieYAxis); - if ( !is_array($PosArrayY) ) { $Value = $PosArrayY; $PosArrayY = ""; $PosArrayY[0] = $Value; } - - $Color = array("R"=>$Series["Color"]["R"],"G"=>$Series["Color"]["G"],"B"=>$Series["Color"]["B"],"Alpha"=>$Series["Color"]["Alpha"]); - if ( $Ticks != 0 ) { $Color["Ticks"] = $Ticks; } - if ( $Weight != 0 ) { $Color["Weight"] = $Weight; } - - $LastX = VOID; $LastY = VOID; - foreach($PosArrayX as $Key => $Value) - { - $X = $Value; $Y = $PosArrayY[$Key]; - - if ( $X != VOID && $Y != VOID ) - { - $RealValue = round($Data["Series"][$Series["X"]]["Data"][$Key],2)." / ".round($Data["Series"][$Series["Y"]]["Data"][$Key],2); - if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$ImageMapPlotSize,$this->pChartObject->toHTMLColor($Series["Color"]["R"],$Series["Color"]["G"],$Series["Color"]["B"]),$Description,$RealValue); } - } - - if ( $X != VOID && $Y != VOID && $LastX != VOID && $LastY != VOID) - $this->pChartObject->drawLine($LastX,$LastY,$X,$Y,$Color); - - $LastX = $X; $LastY = $Y; - } - } - } - } - - /* Draw a scatter spline chart */ - function drawScatterSplineChart($Format=NULL) - { - $Data = $this->pDataObject->getData(); - $Palette = $this->pDataObject->getPalette(); - $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; - $ImageMapTitle = isset($Format["ImageMapTitle"]) ? $Format["ImageMapTitle"] : NULL; - $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 10; - $ImageMapPrecision = isset($Format["ImageMapPrecision"]) ? $Format["ImageMapPrecision"] : 2; - - foreach($Data["ScatterSeries"] as $Key => $Series) - { - if ( $Series["isDrawable"] == TRUE ) - { - $SerieX = $Series["X"]; $SerieValuesX = $Data["Series"][$SerieX]["Data"]; $SerieXAxis = $Data["Series"][$SerieX]["Axis"]; - $SerieY = $Series["Y"]; $SerieValuesY = $Data["Series"][$SerieY]["Data"]; $SerieYAxis = $Data["Series"][$SerieY]["Axis"]; - $Ticks = $Series["Ticks"]; - $Weight = $Series["Weight"]; - - if ( $ImageMapTitle == NULL ) { $Description = $Data["Series"][$Series["X"]]["Description"]." / ".$Data["Series"][$Series["Y"]]["Description"]; } else { $Description = $ImageMapTitle; } - - $PosArrayX = $this->getPosArray($SerieValuesX,$SerieXAxis); - if ( !is_array($PosArrayX) ) { $Value = $PosArrayX; $PosArrayX = ""; $PosArrayX[0] = $Value; } - $PosArrayY = $this->getPosArray($SerieValuesY,$SerieYAxis); - if ( !is_array($PosArrayY) ) { $Value = $PosArrayY; $PosArrayY = ""; $PosArrayY[0] = $Value; } - - $SplineSettings = array("R"=>$Series["Color"]["R"],"G"=>$Series["Color"]["G"],"B"=>$Series["Color"]["B"],"Alpha"=>$Series["Color"]["Alpha"]); - if ( $Ticks != 0 ) { $SplineSettings["Ticks"] = $Ticks; } - if ( $Weight != 0 ) { $SplineSettings["Weight"] = $Weight; } - - $LastX = VOID; $LastY = VOID; $WayPoints = ""; $Forces = ""; - foreach($PosArrayX as $Key => $Value) - { - $X = $Value; $Y = $PosArrayY[$Key]; - $Force = $this->pChartObject->getLength($LastX,$LastY,$X,$Y)/5; - - if ( $X != VOID && $Y != VOID ) - { - $RealValue = round($Data["Series"][$Series["X"]]["Data"][$Key],2)." / ".round($Data["Series"][$Series["Y"]]["Data"][$Key],2); - if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$ImageMapPlotSize,$this->pChartObject->toHTMLColor($Series["Color"]["R"],$Series["Color"]["G"],$Series["Color"]["B"]),$Description,$RealValue); } - } - - if ( $X != VOID && $Y != VOID ) - { $WayPoints[] = array($X,$Y); $Forces[] = $Force; } - - if ( $Y == VOID || $X == VOID ) - { $SplineSettings["Forces"] = $Forces; $this->pChartObject->drawSpline($WayPoints,$SplineSettings); $WayPoints = ""; $Forces = "";} - - $LastX = $X; $LastY = $Y; - } - $SplineSettings["Forces"] = $Forces; - $this->pChartObject->drawSpline($WayPoints,$SplineSettings); - } - } - } - - /* Return the scaled plot position */ - function getPosArray($Values,$AxisID) - { - $Data = $this->pDataObject->getData(); - - if ( !is_array($Values) ) { $Values = array($Values); } - - if ( $Data["Axis"][$AxisID]["Identity"] == AXIS_X ) - { - $Height = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1) - $Data["Axis"][$AxisID]["Margin"]*2; - $ScaleHeight = $Data["Axis"][$AxisID]["ScaleMax"] - $Data["Axis"][$AxisID]["ScaleMin"]; - $Step = $Height / $ScaleHeight; - - $Result = ""; - foreach($Values as $Key => $Value) - { - if ( $Value == VOID ) - $Result[] = VOID; - else - $Result[] = $this->pChartObject->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"] + ($Step * ($Value-$Data["Axis"][$AxisID]["ScaleMin"])); - } - - if ( count($Result) == 1 ) { return($Result[0]); } else { return($Result); } - } - else - { - $Height = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1) - $Data["Axis"][$AxisID]["Margin"]*2; - $ScaleHeight = $Data["Axis"][$AxisID]["ScaleMax"] - $Data["Axis"][$AxisID]["ScaleMin"]; - $Step = $Height / $ScaleHeight; - - $Result = ""; - foreach($Values as $Key => $Value) - { - if ( $Value == VOID ) - $Result[] = VOID; - else - $Result[] = $this->pChartObject->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"] - ($Step * ($Value-$Data["Axis"][$AxisID]["ScaleMin"])); - } - - if ( count($Result) == 1 ) { return($Result[0]); } else { return($Result); } - } - } - - /* Draw the legend of the active series */ - function drawScatterLegend($X,$Y,$Format="") - { - $Family = isset($Format["Family"]) ? $Format["Family"] : LEGEND_FAMILY_BOX; - $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->pChartObject->FontName; - $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->pChartObject->FontSize; - $FontR = isset($Format["FontR"]) ? $Format["FontR"] : $this->pChartObject->FontColorR; - $FontG = isset($Format["FontG"]) ? $Format["FontG"] : $this->pChartObject->FontColorG; - $FontB = isset($Format["FontB"]) ? $Format["FontB"] : $this->pChartObject->FontColorB; - $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 5; - $BoxHeight = isset($Format["BoxHeight"]) ? $Format["BoxHeight"] : 5; - $IconAreaWidth = isset($Format["IconAreaWidth"]) ? $Format["IconAreaWidth"] : $BoxWidth; - $IconAreaHeight = isset($Format["IconAreaHeight"]) ? $Format["IconAreaHeight"] : $BoxHeight; - $XSpacing = isset($Format["XSpacing"]) ? $Format["XSpacing"] : 5; - $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5; - $R = isset($Format["R"]) ? $Format["R"] : 200; - $G = isset($Format["G"]) ? $Format["G"] : 200; - $B = isset($Format["B"]) ? $Format["B"] : 200; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255; - $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; - $Style = isset($Format["Style"]) ? $Format["Style"] : LEGEND_ROUND; - $Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL; - - if ( $Surrounding != NULL ) { $BorderR = $R + $Surrounding; $BorderG = $G + $Surrounding; $BorderB = $B + $Surrounding; } - - $Data = $this->pDataObject->getData(); - - foreach($Data["ScatterSeries"] as $Key => $Series) - { - if ( $Series["isDrawable"] == TRUE && isset($Series["Picture"])) - { - list($PicWidth,$PicHeight) = $this->pChartObject->getPicInfo($Series["Picture"]); - if ( $IconAreaWidth < $PicWidth ) { $IconAreaWidth = $PicWidth; } - if ( $IconAreaHeight < $PicHeight ) { $IconAreaHeight = $PicHeight; } - } - } - - $YStep = max($this->pChartObject->FontSize,$IconAreaHeight) + 5; - $XStep = $IconAreaWidth + 5; - $XStep = $XSpacing; - - $Boundaries = ""; $Boundaries["L"] = $X; $Boundaries["T"] = $Y; $Boundaries["R"] = 0; $Boundaries["B"] = 0; $vY = $Y; $vX = $X; - foreach($Data["ScatterSeries"] as $Key => $Series) - { - if ( $Series["isDrawable"] == TRUE ) - { - if ( $Mode == LEGEND_VERTICAL ) - { - $BoxArray = $this->pChartObject->getTextBox($vX+$IconAreaWidth+4,$vY+$IconAreaHeight/2,$FontName,$FontSize,0,$Series["Description"]); - - if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$IconAreaHeight/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$IconAreaHeight/2; } - if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; } - if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$IconAreaHeight/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$IconAreaHeight/2; } - - $Lines = preg_split("/\n/",$Series["Description"]); - $vY = $vY + max($this->pChartObject->FontSize*count($Lines),$IconAreaHeight) + 5; - } - elseif ( $Mode == LEGEND_HORIZONTAL ) - { - $Lines = preg_split("/\n/",$Series["Description"]); - $Width = ""; - foreach($Lines as $Key => $Value) - { - $BoxArray = $this->pChartObject->getTextBox($vX+$IconAreaWidth+6,$Y+$IconAreaHeight/2+(($this->pChartObject->FontSize+3)*$Key),$FontName,$FontSize,0,$Value); - - if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$IconAreaHeight/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$IconAreaHeight/2; } - if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; } - if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$IconAreaHeight/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$IconAreaHeight/2; } - - $Width[] = $BoxArray[1]["X"]; - } - - $vX=max($Width)+$XStep; - } - } - } - $vY=$vY-$YStep; $vX=$vX-$XStep; - - $TopOffset = $Y - $Boundaries["T"]; - if ( $Boundaries["B"]-($vY+$IconAreaHeight) < $TopOffset ) { $Boundaries["B"] = $vY+$IconAreaHeight+$TopOffset; } - - if ( $Style == LEGEND_ROUND ) - $this->pChartObject->drawRoundedFilledRectangle($Boundaries["L"]-$Margin,$Boundaries["T"]-$Margin,$Boundaries["R"]+$Margin,$Boundaries["B"]+$Margin,$Margin,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB)); - elseif ( $Style == LEGEND_BOX ) - $this->pChartObject->drawFilledRectangle($Boundaries["L"]-$Margin,$Boundaries["T"]-$Margin,$Boundaries["R"]+$Margin,$Boundaries["B"]+$Margin,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB)); - - $RestoreShadow = $this->pChartObject->Shadow; $this->Shadow = FALSE; - foreach($Data["ScatterSeries"] as $Key => $Series) - { - if ( $Series["isDrawable"] == TRUE ) - { - $R = $Series["Color"]["R"]; $G = $Series["Color"]["G"]; $B = $Series["Color"]["B"]; - $Ticks = $Series["Ticks"]; $Weight = $Series["Weight"]; - - if ( isset($Series["Picture"]) ) - { - $Picture = $Series["Picture"]; - list($PicWidth,$PicHeight) = $this->pChartObject->getPicInfo($Picture); - $PicX = $X+$IconAreaWidth/2; $PicY = $Y+$IconAreaHeight/2; - - $this->pChartObject->drawFromPNG($PicX-$PicWidth/2,$PicY-$PicHeight/2,$Picture); - } - else - { - if ( $Family == LEGEND_FAMILY_BOX ) - { - if ( $BoxWidth != $IconAreaWidth ) { $XOffset = floor(($IconAreaWidth-$BoxWidth)/2); } else { $XOffset = 0; } - if ( $BoxHeight != $IconAreaHeight ) { $YOffset = floor(($IconAreaHeight-$BoxHeight)/2); } else { $YOffset = 0; } - - $this->pChartObject->drawFilledRectangle($X+1+$XOffset,$Y+1+$YOffset,$X+$BoxWidth+$XOffset+1,$Y+$BoxHeight+1+$YOffset,array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20)); - $this->pChartObject->drawFilledRectangle($X+$XOffset,$Y+$YOffset,$X+$BoxWidth+$XOffset,$Y+$BoxHeight+$YOffset,array("R"=>$R,"G"=>$G,"B"=>$B,"Surrounding"=>20)); - } - elseif ( $Family == LEGEND_FAMILY_CIRCLE ) - { - $this->pChartObject->drawFilledCircle($X+1+$IconAreaWidth/2,$Y+1+$IconAreaHeight/2,min($IconAreaHeight/2,$IconAreaWidth/2),array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20)); - $this->pChartObject->drawFilledCircle($X+$IconAreaWidth/2,$Y+$IconAreaHeight/2,min($IconAreaHeight/2,$IconAreaWidth/2),array("R"=>$R,"G"=>$G,"B"=>$B,"Surrounding"=>20)); - } - elseif ( $Family == LEGEND_FAMILY_LINE ) - { - $this->pChartObject->drawLine($X+1,$Y+1+$IconAreaHeight/2,$X+1+$IconAreaWidth,$Y+1+$IconAreaHeight/2,array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20,"Ticks"=>$Ticks,"Weight"=>$Weight)); - $this->pChartObject->drawLine($X,$Y+$IconAreaHeight/2,$X+$IconAreaWidth,$Y+$IconAreaHeight/2,array("R"=>$R,"G"=>$G,"B"=>$B,"Ticks"=>$Ticks,"Weight"=>$Weight)); - } - } - - if ( $Mode == LEGEND_VERTICAL ) - { - $Lines = preg_split("/\n/",$Series["Description"]); - foreach($Lines as $Key => $Value) - $this->pChartObject->drawText($X+$IconAreaWidth+4,$Y+$IconAreaHeight/2+(($this->pChartObject->FontSize+3)*$Key),$Value,array("R"=>$FontR,"G"=>$FontG,"B"=>$FontB,"Align"=>TEXT_ALIGN_MIDDLELEFT)); - - $Y=$Y+max($this->pChartObject->FontSize*count($Lines),$IconAreaHeight) + 5; - } - elseif ( $Mode == LEGEND_HORIZONTAL ) - { - $Lines = preg_split("/\n/",$Series["Description"]); - $Width = ""; - foreach($Lines as $Key => $Value) - { - $BoxArray = $this->pChartObject->drawText($X+$IconAreaWidth+4,$Y+$IconAreaHeight/2+(($this->pChartObject->FontSize+3)*$Key),$Value,array("R"=>$FontR,"G"=>$FontG,"B"=>$FontB,"Align"=>TEXT_ALIGN_MIDDLELEFT)); - $Width[] = $BoxArray[1]["X"]; - } - $X=max($Width)+2+$XStep; - } - } - } - - $this->Shadow = $RestoreShadow; - } - - /* Get the legend box size */ - function getScatterLegendSize($Format="") - { - $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->pChartObject->FontName; - $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->pChartObject->FontSize; - $BoxSize = isset($Format["BoxSize"]) ? $Format["BoxSize"] : 5; - $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5; - $Style = isset($Format["Style"]) ? $Format["Style"] : LEGEND_ROUND; - $Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL; - - $YStep = max($this->pChartObject->FontSize,$BoxSize) + 5; - $XStep = $BoxSize + 5; - - $X=100; $Y=100; - - $Data = $this->pDataObject->getData(); - - foreach($Data["ScatterSeries"] as $Key => $Series) - { - if ( $Series["isDrawable"] == TRUE && isset($Series["Picture"])) - { - list($PicWidth,$PicHeight) = $this->pChartObject->getPicInfo($Series["Picture"]); - if ( $IconAreaWidth < $PicWidth ) { $IconAreaWidth = $PicWidth; } - if ( $IconAreaHeight < $PicHeight ) { $IconAreaHeight = $PicHeight; } - } - } - - $YStep = max($this->pChartObject->FontSize,$IconAreaHeight) + 5; - $XStep = $IconAreaWidth + 5; - $XStep = $XSpacing; - - $Boundaries = ""; $Boundaries["L"] = $X; $Boundaries["T"] = $Y; $Boundaries["R"] = 0; $Boundaries["B"] = 0; $vY = $Y; $vX = $X; - foreach($Data["ScatterSeries"] as $Key => $Series) - { - if ( $Series["isDrawable"] == TRUE ) - { - if ( $Mode == LEGEND_VERTICAL ) - { - $BoxArray = $this->pChartObject->getTextBox($vX+$IconAreaWidth+4,$vY+$IconAreaHeight/2,$FontName,$FontSize,0,$Series["Description"]); - - if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$IconAreaHeight/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$IconAreaHeight/2; } - if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; } - if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$IconAreaHeight/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$IconAreaHeight/2; } - - $Lines = preg_split("/\n/",$Series["Description"]); - $vY = $vY + max($this->pChartObject->FontSize*count($Lines),$IconAreaHeight) + 5; - } - elseif ( $Mode == LEGEND_HORIZONTAL ) - { - $Lines = preg_split("/\n/",$Series["Description"]); - $Width = ""; - foreach($Lines as $Key => $Value) - { - $BoxArray = $this->pChartObject->getTextBox($vX+$IconAreaWidth+6,$Y+$IconAreaHeight/2+(($this->pChartObject->FontSize+3)*$Key),$FontName,$FontSize,0,$Value); - - if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$IconAreaHeight/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$IconAreaHeight/2; } - if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; } - if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$IconAreaHeight/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$IconAreaHeight/2; } - - $Width[] = $BoxArray[1]["X"]; - } - - $vX=max($Width)+$XStep; - } - } - } - $vY=$vY-$YStep; $vX=$vX-$XStep; - - $TopOffset = $Y - $Boundaries["T"]; - if ( $Boundaries["B"]-($vY+$BoxSize) < $TopOffset ) { $Boundaries["B"] = $vY+$BoxSize+$TopOffset; } - - $Width = ($Boundaries["R"]+$Margin) - ($Boundaries["L"]-$Margin); - $Height = ($Boundaries["B"]+$Margin) - ($Boundaries["T"]-$Margin); - - return(array("Width"=>$Width,"Height"=>$Height)); - } - - /* Draw the line of best fit */ - function drawScatterBestFit($Format="") - { - $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : 0; - - $Data = $this->pDataObject->getData(); - - foreach($Data["ScatterSeries"] as $Key => $Series) - { - if ( $Series["isDrawable"] == TRUE ) - { - $SerieX = $Series["X"]; $SerieValuesX = $Data["Series"][$SerieX]["Data"]; $SerieXAxis = $Data["Series"][$SerieX]["Axis"]; - $SerieY = $Series["Y"]; $SerieValuesY = $Data["Series"][$SerieY]["Data"]; $SerieYAxis = $Data["Series"][$SerieY]["Axis"]; - - $Color = array("R"=>$Series["Color"]["R"],"G"=>$Series["Color"]["G"],"B"=>$Series["Color"]["B"],"Alpha"=>$Series["Color"]["Alpha"]); - $Color["Ticks"] = $Ticks; - - $PosArrayX = $Data["Series"][$Series["X"]]["Data"]; - $PosArrayY = $Data["Series"][$Series["Y"]]["Data"]; - - $Sxy = 0; $Sx = 0; $Sy = 0; $Sxx = 0; - foreach($PosArrayX as $Key => $Value) - { - $X = $Value; $Y = $PosArrayY[$Key]; - - $Sxy = $Sxy + $X*$Y; - $Sx = $Sx + $X; - $Sy = $Sy + $Y; - $Sxx = $Sxx + $X*$X; - } - - $n = count($PosArrayX); - - if ((($n*$Sxx) == ($Sx*$Sx))) - { - $X1 = $this->getPosArray($Data["Axis"][$SerieXAxis]["ScaleMin"],$SerieXAxis); - $X2 = $X1; - $Y1 = $this->pChartObject->GraphAreaY1; - $Y2 = $this->pChartObject->GraphAreaY2; - } - else - { - $M = (($n*$Sxy)-($Sx*$Sy)) / (($n*$Sxx)-($Sx*$Sx)); - $B = (($Sy)-($M*$Sx))/($n); - - $X1 = $this->getPosArray($Data["Axis"][$SerieXAxis]["ScaleMin"],$SerieXAxis); - $Y1 = $this->getPosArray($M * $Data["Axis"][$SerieXAxis]["ScaleMin"] + $B,$SerieYAxis); - $X2 = $this->getPosArray($Data["Axis"][$SerieXAxis]["ScaleMax"],$SerieXAxis); - $Y2 = $this->getPosArray($M * $Data["Axis"][$SerieXAxis]["ScaleMax"] + $B,$SerieYAxis); - - $RealM = -($Y2-$Y1)/($X2-$X1); - - if ( $Y1 < $this->pChartObject->GraphAreaY1 ) { $X1 = $X1 + ($this->pChartObject->GraphAreaY1-$Y1/$RealM); $Y1 = $this->pChartObject->GraphAreaY1; } - if ( $Y1 > $this->pChartObject->GraphAreaY2 ) { $X1 = $X1 + ($Y1-$this->pChartObject->GraphAreaY2)/$RealM; $Y1 = $this->pChartObject->GraphAreaY2; } - if ( $Y2 < $this->pChartObject->GraphAreaY1 ) { $X2 = $X2 - ($this->pChartObject->GraphAreaY1-$Y2)/$RealM; $Y2 = $this->pChartObject->GraphAreaY1; } - if ( $Y2 > $this->pChartObject->GraphAreaY2 ) { $X2 = $X2 - ($Y2-$this->pChartObject->GraphAreaY2)/$RealM; $Y2 = $this->pChartObject->GraphAreaY2; } - } - - $this->pChartObject->drawLine($X1,$Y1,$X2,$Y2,$Color); - } - } - } - - function writeScatterLabel($ScatterSerieID,$Points,$Format="") - { - $OverrideTitle = isset($Format["OverrideTitle"]) ? $Format["OverrideTitle"] : NULL; - $DrawPoint = isset($Format["DrawPoint"]) ? $Format["DrawPoint"] : LABEL_POINT_BOX; - $Decimals = isset($Format["Decimals"]) ? $Format["Decimals"] : NULL; - - $Data = $this->pDataObject->getData(); - $Palette = $this->pDataObject->getPalette(); - - if ( !is_array($Points) ) { $Point = $Points; $Points = ""; $Points[0] = $Point; } - - if ( !isset($Data["ScatterSeries"][$ScatterSerieID]) ) - return(0); - - $Series = $Data["ScatterSeries"][$ScatterSerieID]; - $SerieX = $Series["X"]; $SerieValuesX = $Data["Series"][$SerieX]["Data"]; $SerieXAxis = $Data["Series"][$SerieX]["Axis"]; - $SerieY = $Series["Y"]; $SerieValuesY = $Data["Series"][$SerieY]["Data"]; $SerieYAxis = $Data["Series"][$SerieY]["Axis"]; - - $PosArrayX = $this->getPosArray($SerieValuesX,$SerieXAxis); - if ( !is_array($PosArrayX) ) { $Value = $PosArrayX; $PosArrayX = ""; $PosArrayX[0] = $Value; } - $PosArrayY = $this->getPosArray($SerieValuesY,$SerieYAxis); - if ( !is_array($PosArrayY) ) { $Value = $PosArrayY; $PosArrayY = ""; $PosArrayY[0] = $Value; } - - foreach($Points as $Key => $Point) - { - if ( isset($PosArrayX[$Point]) && isset($PosArrayY[$Point]) ) - { - $X = floor($PosArrayX[$Point]); - $Y = floor($PosArrayY[$Point]); - - if ( $DrawPoint == LABEL_POINT_CIRCLE ) - $this->pChartObject->drawFilledCircle($X,$Y,3,array("R"=>255,"G"=>255,"B"=>255,"BorderR"=>0,"BorderG"=>0,"BorderB"=>0)); - elseif ( $DrawPoint == LABEL_POINT_BOX ) - $this->pChartObject->drawFilledRectangle($X-2,$Y-2,$X+2,$Y+2,array("R"=>255,"G"=>255,"B"=>255,"BorderR"=>0,"BorderG"=>0,"BorderB"=>0)); - - $Serie = ""; - $Serie["R"] = $Series["Color"]["R"]; - $Serie["G"] = $Series["Color"]["G"]; - $Serie["B"] = $Series["Color"]["B"]; - $Serie["Alpha"] = $Series["Color"]["Alpha"]; - - $XAxisMode = $Data["Axis"][$SerieXAxis]["Display"]; - $XAxisFormat = $Data["Axis"][$SerieXAxis]["Format"]; - $XAxisUnit = $Data["Axis"][$SerieXAxis]["Unit"]; - if ( $Decimals == NULL ) { $XValue = $SerieValuesX[$Point]; } else { $XValue = round($SerieValuesX[$Point],$Decimals); } - $XValue = $this->pChartObject->scaleFormat($XValue,$XAxisMode,$XAxisFormat,$XAxisUnit); - - $YAxisMode = $Data["Axis"][$SerieYAxis]["Display"]; - $YAxisFormat = $Data["Axis"][$SerieYAxis]["Format"]; - $YAxisUnit = $Data["Axis"][$SerieYAxis]["Unit"]; - if ( $Decimals == NULL ) { $YValue = $SerieValuesY[$Point]; } else { $YValue = round($SerieValuesY[$Point],$Decimals); } - $YValue = $this->pChartObject->scaleFormat($YValue,$YAxisMode,$YAxisFormat,$YAxisUnit); - - $Caption = $XValue." / ".$YValue; - - if ( isset($Series["Description"]) ) - $Description = $Series["Description"]; - else - $Description = "No description"; - - $Series = ""; - $Series[] = array("Format"=>$Serie,"Caption"=>$Caption); - - $this->pChartObject->drawLabelBox($X,$Y-3,$Description,$Series,$Format); - } - } - } - - /* Draw a Scatter threshold */ - function drawScatterThreshold($Value,$Format="") - { - $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0; - $R = isset($Format["R"]) ? $Format["R"] : 255; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 50; - $Weight = isset($Format["Weight"]) ? $Format["Weight"] : NULL; - $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : 3; - $Wide = isset($Format["Wide"]) ? $Format["Wide"] : FALSE; - $WideFactor = isset($Format["WideFactor"]) ? $Format["WideFactor"] : 5; - $WriteCaption = isset($Format["WriteCaption"]) ? $Format["WriteCaption"] : FALSE; - $Caption = isset($Format["Caption"]) ? $Format["Caption"] : NULL; - $CaptionAlign = isset($Format["CaptionAlign"]) ? $Format["CaptionAlign"] : CAPTION_LEFT_TOP; - $CaptionOffset = isset($Format["CaptionOffset"]) ? $Format["CaptionOffset"] : 10; - $CaptionR = isset($Format["CaptionR"]) ? $Format["CaptionR"] : 255; - $CaptionG = isset($Format["CaptionG"]) ? $Format["CaptionG"] : 255; - $CaptionB = isset($Format["CaptionB"]) ? $Format["CaptionB"] : 255; - $CaptionAlpha = isset($Format["CaptionAlpha"]) ? $Format["CaptionAlpha"] : 100; - $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : TRUE; - $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : FALSE; - $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 5; - $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : TRUE; - $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 3; - $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0; - $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0; - $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0; - $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 20; - $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : ""; - $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 255; - $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 255; - $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 255; - $BoxBorderAlpha = isset($Format["BoxBorderAlpha"]) ? $Format["BoxBorderAlpha"] : 100; - - $CaptionSettings = array("DrawBox"=>$DrawBox,"DrawBoxBorder"=>$DrawBoxBorder,"BorderOffset"=>$BorderOffset,"BoxRounded"=>$BoxRounded,"RoundedRadius"=>$RoundedRadius, - "BoxR"=>$BoxR,"BoxG"=>$BoxG,"BoxB"=>$BoxB,"BoxAlpha"=>$BoxAlpha,"BoxSurrounding"=>$BoxSurrounding, - "BoxBorderR"=>$BoxBorderR,"BoxBorderG"=>$BoxBorderG,"BoxBorderB"=>$BoxBorderB,"BoxBorderAlpha"=>$BoxBorderAlpha, - "R"=>$CaptionR,"G"=>$CaptionG,"B"=>$CaptionB,"Alpha"=>$CaptionAlpha); - - if ( $Caption == NULL ) { $Caption = $Value; } - - $Data = $this->pDataObject->getData(); - - if ( !isset($Data["Axis"][$AxisID]) ) { return(-1); } - - if ( $Data["Axis"][$AxisID]["Identity"] == AXIS_Y ) - { - $X1 = $this->pChartObject->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"]; - $X2 = $this->pChartObject->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"]; - $Y = $this->getPosArray($Value,$AxisID); - - $this->pChartObject->drawLine($X1,$Y,$X2,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); - - if ( $Wide ) - { - $this->pChartObject->drawLine($X1,$Y-1,$X2,$Y-1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); - $this->pChartObject->drawLine($X1,$Y+1,$X2,$Y+1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); - } - - if ( $WriteCaption ) - { - if ( $CaptionAlign == CAPTION_LEFT_TOP ) - { $X = $this->pChartObject->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"] + $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLELEFT; } - else - { $X = $this->pChartObject->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"] - $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLERIGHT; } - - $this->pChartObject->drawText($X,$Y,$Caption,$CaptionSettings); - } - - return(array("Y"=>$Y)); - } - elseif ( $Data["Axis"][$AxisID]["Identity"] == AXIS_X ) - { - $X = $this->getPosArray($Value,$AxisID); - $Y1 = $this->pChartObject->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"]; - $Y2 = $this->pChartObject->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"]; - - $this->pChartObject->drawLine($X,$Y1,$X,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); - - if ( $Wide ) - { - $this->pChartObject->drawLine($X-1,$Y1,$X-1,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); - $this->pChartObject->drawLine($X+1,$Y1,$X+1,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); - } - - if ( $WriteCaption ) - { - if ( $CaptionAlign == CAPTION_LEFT_TOP ) - { $Y = $this->pChartObject->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"] + $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE; } - else - { $Y = $this->pChartObject->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"] - $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_BOTTOMMIDDLE; } - - $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE; - $this->pChartObject->drawText($X,$Y,$Caption,$CaptionSettings); - } - - return(array("X"=>$X)); - } - } - - /* Draw a Scatter threshold area */ - function drawScatterThresholdArea($Value1,$Value2,$Format="") - { - $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0; - $R = isset($Format["R"]) ? $Format["R"] : 255; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 20; - $Border = isset($Format["Border"]) ? $Format["Border"] : TRUE; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B; - $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha + 20; - $BorderTicks = isset($Format["BorderTicks"]) ? $Format["BorderTicks"] : 2; - $AreaName = isset($Format["AreaName"]) ? $Format["AreaName"] : "La ouate de phoque"; //NULL; - $NameAngle = isset($Format["NameAngle"]) ? $Format["NameAngle"] : ZONE_NAME_ANGLE_AUTO; - $NameR = isset($Format["NameR"]) ? $Format["NameR"] : 255; - $NameG = isset($Format["NameG"]) ? $Format["NameG"] : 255; - $NameB = isset($Format["NameB"]) ? $Format["NameB"] : 255; - $NameAlpha = isset($Format["NameAlpha"]) ? $Format["NameAlpha"] : 100; - $DisableShadowOnArea = isset($Format["DisableShadowOnArea"]) ? $Format["DisableShadowOnArea"] : TRUE; - - if ($Value1 > $Value2) { list($Value1, $Value2) = array($Value2, $Value1); } - - $RestoreShadow = $this->pChartObject->Shadow; - if ( $DisableShadowOnArea && $this->pChartObject->Shadow ) { $this->pChartObject->Shadow = FALSE; } - - if ($BorderAlpha >100) { $BorderAlpha = 100;} - - $Data = $this->pDataObject->getData(); - - if ( !isset($Data["Axis"][$AxisID]) ) { return(-1); } - - if ( $Data["Axis"][$AxisID]["Identity"] == AXIS_X ) - { - $Y1 = $this->pChartObject->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"]; - $Y2 = $this->pChartObject->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"]; - $X1 = $this->getPosArray($Value1,$AxisID); - $X2 = $this->getPosArray($Value2,$AxisID); - - if ( $X1 <= $this->pChartObject->GraphAreaX1 ) { $X1 = $this->pChartObject->GraphAreaX1+$Data["Axis"][$AxisID]["Margin"]; } - if ( $X2 >= $this->pChartObject->GraphAreaX2 ) { $X2 = $this->pChartObject->GraphAreaX2-$Data["Axis"][$AxisID]["Margin"]; } - - $this->pChartObject->drawFilledRectangle($X1,$Y1,$X2,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); - - if ( $Border ) - { - $this->pChartObject->drawLine($X1,$Y1,$X1,$Y2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); - $this->pChartObject->drawLine($X2,$Y1,$X2,$Y2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); - } - - if ( $AreaName != NULL ) - { - $XPos = ($X2-$X1)/2 + $X1; - $YPos = ($Y2-$Y1)/2 + $Y1; - - if ( $NameAngle == ZONE_NAME_ANGLE_AUTO ) - { - $TxtPos = $this->pChartObject->getTextBox($XPos,$YPos,$this->pChartObject->FontName,$this->pChartObject->FontSize,0,$AreaName); - $TxtWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"]; - if ( abs($X2 - $X1) > $TxtWidth ) { $NameAngle = 0; } else { $NameAngle = 90; } - } - $this->pChartObject->Shadow = $RestoreShadow; - $this->pChartObject->drawText($XPos,$YPos,$AreaName,array("R"=>$NameR,"G"=>$NameG,"B"=>$NameB,"Alpha"=>$NameAlpha,"Angle"=>$NameAngle,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE)); - if ( $DisableShadowOnArea ) { $this->pChartObject->Shadow = FALSE; } - } - - $this->pChartObject->Shadow = $RestoreShadow; - return(array("X1"=>$X1,"X2"=>$X2)); - } - elseif ( $Data["Axis"][$AxisID]["Identity"] == AXIS_Y ) - { - $X1 = $this->pChartObject->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"]; - $X2 = $this->pChartObject->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"]; - $Y1 = $this->getPosArray($Value1,$AxisID); - $Y2 = $this->getPosArray($Value2,$AxisID); - - if ( $Y1 >= $this->pChartObject->GraphAreaY2 ) { $Y1 = $this->pChartObject->GraphAreaY2-$Data["Axis"][$AxisID]["Margin"]; } - if ( $Y2 <= $this->pChartObject->GraphAreaY1 ) { $Y2 = $this->pChartObject->GraphAreaY1+$Data["Axis"][$AxisID]["Margin"]; } - - $this->pChartObject->drawFilledRectangle($X1,$Y1,$X2,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); - - if ( $Border ) - { - $this->pChartObject->drawLine($X1,$Y1,$X2,$Y1,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); - $this->pChartObject->drawLine($X1,$Y2,$X2,$Y2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); - } - - if ( $AreaName != NULL ) - { - $XPos = ($X2-$X1)/2 + $X1; - $YPos = ($Y2-$Y1)/2 + $Y1; - - $this->pChartObject->Shadow = $RestoreShadow; - $this->pChartObject->drawText($YPos,$XPos,$AreaName,array("R"=>$NameR,"G"=>$NameG,"B"=>$NameB,"Alpha"=>$NameAlpha,"Angle"=>0,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE)); - if ( $DisableShadowOnArea ) { $this->Shadow = FALSE; } - } - - $this->pChartObject->Shadow = $RestoreShadow; - return(array("Y1"=>$Y1,"Y2"=>$Y2)); - } - } - } +pChartObject = $pChartObject; + $this->pDataObject = $pDataObject; + } + + /* Prepare the scale */ + function drawScatterScale($Format="") + { + $Mode = isset($Format["Mode"]) ? $Format["Mode"] : SCALE_MODE_FLOATING; + $Floating = isset($Format["Floating"]) ? $Format["Floating"] : FALSE; + $XLabelsRotation = isset($Format["XLabelsRotation"]) ? $Format["XLabelsRotation"] : 90; + $MinDivHeight = isset($Format["MinDivHeight"]) ? $Format["MinDivHeight"] : 20; + $Factors = isset($Format["Factors"]) ? $Format["Factors"] : array(1,2,5); + $ManualScale = isset($Format["ManualScale"]) ? $Format["ManualScale"] : array("0"=>array("Min"=>-100,"Max"=>100)); + $XMargin = isset($Format["XMargin"]) ? $Format["XMargin"] : 0; + $YMargin = isset($Format["YMargin"]) ? $Format["YMargin"] : 0; + $ScaleSpacing = isset($Format["ScaleSpacing"]) ? $Format["ScaleSpacing"] : 15; + $InnerTickWidth = isset($Format["InnerTickWidth"]) ? $Format["InnerTickWidth"] : 2; + $OuterTickWidth = isset($Format["OuterTickWidth"]) ? $Format["OuterTickWidth"] : 2; + $DrawXLines = isset($Format["DrawXLines"]) ? $Format["DrawXLines"] : ALL; + $DrawYLines = isset($Format["DrawYLines"]) ? $Format["DrawYLines"] : ALL; + $GridTicks = isset($Format["GridTicks"]) ? $Format["GridTicks"] : 4; + $GridR = isset($Format["GridR"]) ? $Format["GridR"] : 255; + $GridG = isset($Format["GridG"]) ? $Format["GridG"] : 255; + $GridB = isset($Format["GridB"]) ? $Format["GridB"] : 255; + $GridAlpha = isset($Format["GridAlpha"]) ? $Format["GridAlpha"] : 40; + $AxisRo = isset($Format["AxisR"]) ? $Format["AxisR"] : 0; + $AxisGo = isset($Format["AxisG"]) ? $Format["AxisG"] : 0; + $AxisBo = isset($Format["AxisB"]) ? $Format["AxisB"] : 0; + $AxisAlpha = isset($Format["AxisAlpha"]) ? $Format["AxisAlpha"] : 100; + $TickRo = isset($Format["TickR"]) ? $Format["TickR"] : 0; + $TickGo = isset($Format["TickG"]) ? $Format["TickG"] : 0; + $TickBo = isset($Format["TickB"]) ? $Format["TickB"] : 0; + $TickAlpha = isset($Format["TickAlpha"]) ? $Format["TickAlpha"] : 100; + $DrawSubTicks = isset($Format["DrawSubTicks"]) ? $Format["DrawSubTicks"] : FALSE; + $InnerSubTickWidth = isset($Format["InnerSubTickWidth"]) ? $Format["InnerSubTickWidth"] : 0; + $OuterSubTickWidth = isset($Format["OuterSubTickWidth"]) ? $Format["OuterSubTickWidth"] : 2; + $SubTickR = isset($Format["SubTickR"]) ? $Format["SubTickR"] : 255; + $SubTickG = isset($Format["SubTickG"]) ? $Format["SubTickG"] : 0; + $SubTickB = isset($Format["SubTickB"]) ? $Format["SubTickB"] : 0; + $SubTickAlpha = isset($Format["SubTickAlpha"]) ? $Format["SubTickAlpha"] : 100; + $XReleasePercent = isset($Format["XReleasePercent"]) ? $Format["XReleasePercent"] : 1; + $DrawArrows = isset($Format["DrawArrows"]) ? $Format["DrawArrows"] : FALSE; + $ArrowSize = isset($Format["ArrowSize"]) ? $Format["ArrowSize"] : 8; + $CycleBackground = isset($Format["CycleBackground"]) ? $Format["CycleBackground"] : FALSE; + $BackgroundR1 = isset($Format["BackgroundR1"]) ? $Format["BackgroundR1"] : 255; + $BackgroundG1 = isset($Format["BackgroundG1"]) ? $Format["BackgroundG1"] : 255; + $BackgroundB1 = isset($Format["BackgroundB1"]) ? $Format["BackgroundB1"] : 255; + $BackgroundAlpha1 = isset($Format["BackgroundAlpha1"]) ? $Format["BackgroundAlpha1"] : 10; + $BackgroundR2 = isset($Format["BackgroundR2"]) ? $Format["BackgroundR2"] : 230; + $BackgroundG2 = isset($Format["BackgroundG2"]) ? $Format["BackgroundG2"] : 230; + $BackgroundB2 = isset($Format["BackgroundB2"]) ? $Format["BackgroundB2"] : 230; + $BackgroundAlpha2 = isset($Format["BackgroundAlpha2"]) ? $Format["BackgroundAlpha2"] : 10; + + /* Check if we have at least both one X and Y axis */ + $GotXAxis = FALSE; $GotYAxis = FALSE; + foreach($this->pDataObject->Data["Axis"] as $AxisID => $AxisSettings) + { + if ( $AxisSettings["Identity"] == AXIS_X ) { $GotXAxis = TRUE; } + if ( $AxisSettings["Identity"] == AXIS_Y ) { $GotYAxis = TRUE; } + } + if ( !$GotXAxis ) { return(SCATTER_MISSING_X_SERIE); } + if ( !$GotYAxis ) { return(SCATTER_MISSING_Y_SERIE); } + + /* Skip a NOTICE event in case of an empty array */ + if ( $DrawYLines == NONE ) { $DrawYLines = array("zarma"=>"31"); } + + $Data = $this->pDataObject->getData(); + + foreach($Data["Axis"] as $AxisID => $AxisSettings) + { + if ( $AxisSettings["Identity"] == AXIS_X) + { $Width = $this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1 - $XMargin*2; } + else + { $Width = $this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1 - $YMargin*2; } + + $AxisMin = ABSOLUTE_MAX; $AxisMax = OUT_OF_SIGHT; + if ( $Mode == SCALE_MODE_FLOATING ) + { + foreach($Data["Series"] as $SerieID => $SerieParameter) + { + if ( $SerieParameter["Axis"] == $AxisID && $Data["Series"][$SerieID]["isDrawable"] ) + { + $AxisMax = max($AxisMax,$Data["Series"][$SerieID]["Max"]); + $AxisMin = min($AxisMin,$Data["Series"][$SerieID]["Min"]); + } + } + $AutoMargin = (($AxisMax-$AxisMin)/100)*$XReleasePercent; + + $Data["Axis"][$AxisID]["Min"] = $AxisMin-$AutoMargin; $Data["Axis"][$AxisID]["Max"] = $AxisMax+$AutoMargin; + } + elseif ( $Mode == SCALE_MODE_MANUAL ) + { + if ( isset($ManualScale[$AxisID]["Min"]) && isset($ManualScale[$AxisID]["Max"]) ) + { + $Data["Axis"][$AxisID]["Min"] = $ManualScale[$AxisID]["Min"]; + $Data["Axis"][$AxisID]["Max"] = $ManualScale[$AxisID]["Max"]; + } + else + { echo "Manual scale boundaries not set."; exit(); } + } + + /* Full manual scale */ + if ( isset($ManualScale[$AxisID]["Rows"]) && isset($ManualScale[$AxisID]["RowHeight"]) ) + $Scale = array("Rows"=>$ManualScale[$AxisID]["Rows"],"RowHeight"=>$ManualScale[$AxisID]["RowHeight"],"XMin"=>$ManualScale[$AxisID]["Min"],"XMax"=>$ManualScale[$AxisID]["Max"]); + else + { + $MaxDivs = floor($Width/$MinDivHeight); + $Scale = $this->pChartObject->computeScale($Data["Axis"][$AxisID]["Min"],$Data["Axis"][$AxisID]["Max"],$MaxDivs,$Factors,$AxisID); + } + + $Data["Axis"][$AxisID]["Margin"] = $AxisSettings["Identity"] == AXIS_X ? $XMargin : $YMargin; + $Data["Axis"][$AxisID]["ScaleMin"] = $Scale["XMin"]; + $Data["Axis"][$AxisID]["ScaleMax"] = $Scale["XMax"]; + $Data["Axis"][$AxisID]["Rows"] = $Scale["Rows"]; + $Data["Axis"][$AxisID]["RowHeight"] = $Scale["RowHeight"]; + + if ( isset($Scale["Format"]) ) { $Data["Axis"][$AxisID]["Format"] = $Scale["Format"]; } + + if ( !isset($Data["Axis"][$AxisID]["Display"]) ) { $Data["Axis"][$AxisID]["Display"] = NULL; } + if ( !isset($Data["Axis"][$AxisID]["Format"]) ) { $Data["Axis"][$AxisID]["Format"] = NULL; } + if ( !isset($Data["Axis"][$AxisID]["Unit"]) ) { $Data["Axis"][$AxisID]["Unit"] = NULL; } + } + + /* Get the default font color */ + $FontColorRo = $this->pChartObject->FontColorR; $FontColorGo = $this->pChartObject->FontColorG; $FontColorBo = $this->pChartObject->FontColorB; + + /* Set the original boundaries */ + $AxisPos["L"] = $this->pChartObject->GraphAreaX1; $AxisPos["R"] = $this->pChartObject->GraphAreaX2; $AxisPos["T"] = $this->pChartObject->GraphAreaY1; $AxisPos["B"] = $this->pChartObject->GraphAreaY2; + + foreach($Data["Axis"] as $AxisID => $AxisSettings) + { + if ( isset($AxisSettings["Color"]) ) + { + $AxisR = $AxisSettings["Color"]["R"]; $AxisG = $AxisSettings["Color"]["G"]; $AxisB = $AxisSettings["Color"]["B"]; + $TickR = $AxisSettings["Color"]["R"]; $TickG = $AxisSettings["Color"]["G"]; $TickB = $AxisSettings["Color"]["B"]; + $this->pChartObject->setFontProperties(array("R"=>$AxisSettings["Color"]["R"],"G"=>$AxisSettings["Color"]["G"],"B"=>$AxisSettings["Color"]["B"])); + } + else + { + $AxisR = $AxisRo; $AxisG = $AxisGo; $AxisB = $AxisBo; + $TickR = $TickRo; $TickG = $TickGo; $TickB = $TickBo; + $this->pChartObject->setFontProperties(array("R"=>$FontColorRo,"G"=>$FontColorGo,"B"=>$FontColorBo)); + } + + $LastValue = "w00t"; $ID = 1; + if ( $AxisSettings["Identity"] == AXIS_X ) + { + if ( $AxisSettings["Position"] == AXIS_POSITION_BOTTOM ) + { + if ( $XLabelsRotation == 0 ) { $LabelAlign = TEXT_ALIGN_TOPMIDDLE; $LabelOffset = 2; } + if ( $XLabelsRotation > 0 && $XLabelsRotation < 190 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $LabelOffset = 5; } + if ( $XLabelsRotation == 180 ) { $LabelAlign = TEXT_ALIGN_BOTTOMMIDDLE; $LabelOffset = 5; } + if ( $XLabelsRotation > 180 && $XLabelsRotation < 360 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $LabelOffset = 2; } + + if ( $Floating ) + { $FloatingOffset = $YMargin; $this->pChartObject->drawLine($this->pChartObject->GraphAreaX1+$AxisSettings["Margin"],$AxisPos["B"],$this->pChartObject->GraphAreaX2-$AxisSettings["Margin"],$AxisPos["B"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + else + { $FloatingOffset = 0; $this->pChartObject->drawLine($this->pChartObject->GraphAreaX1,$AxisPos["B"],$this->pChartObject->GraphAreaX2,$AxisPos["B"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + + if ( $DrawArrows ) { $this->pChartObject->drawArrow($this->pChartObject->GraphAreaX2-$AxisSettings["Margin"],$AxisPos["B"],$this->pChartObject->GraphAreaX2+($ArrowSize*2),$AxisPos["B"],array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } + + $Width = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1) - $AxisSettings["Margin"]*2; + $Step = $Width / $AxisSettings["Rows"]; $SubTicksSize = $Step /2; $MaxBottom = $AxisPos["B"]; + $LastX = NULL; + for($i=0;$i<=$AxisSettings["Rows"];$i++) + { + $XPos = $this->pChartObject->GraphAreaX1 + $AxisSettings["Margin"] + $Step*$i; + $YPos = $AxisPos["B"]; + $Value = $this->pChartObject->scaleFormat($AxisSettings["ScaleMin"] + $AxisSettings["RowHeight"]*$i,$AxisSettings["Display"],$AxisSettings["Format"],$AxisSettings["Unit"]); + + if ( $i%2 == 1 ) { $BGColor = array("R"=>$BackgroundR1,"G"=>$BackgroundG1,"B"=>$BackgroundB1,"Alpha"=>$BackgroundAlpha1); } else { $BGColor = array("R"=>$BackgroundR2,"G"=>$BackgroundG2,"B"=>$BackgroundB2,"Alpha"=>$BackgroundAlpha2); } + if ( $LastX != NULL && $CycleBackground && ( $DrawXLines == ALL || in_array($AxisID,$DrawXLines) )) { $this->pChartObject->drawFilledRectangle($LastX,$this->pChartObject->GraphAreaY1+$FloatingOffset,$XPos,$this->pChartObject->GraphAreaY2-$FloatingOffset,$BGColor); } + + if ( $DrawXLines == ALL || in_array($AxisID,$DrawXLines) ) { $this->pChartObject->drawLine($XPos,$this->pChartObject->GraphAreaY1+$FloatingOffset,$XPos,$this->pChartObject->GraphAreaY2-$FloatingOffset,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } + if ( $DrawSubTicks && $i != $AxisSettings["Rows"] ) + $this->pChartObject->drawLine($XPos+$SubTicksSize,$YPos-$InnerSubTickWidth,$XPos+$SubTicksSize,$YPos+$OuterSubTickWidth,array("R"=>$SubTickR,"G"=>$SubTickG,"B"=>$SubTickB,"Alpha"=>$SubTickAlpha)); + + $this->pChartObject->drawLine($XPos,$YPos-$InnerTickWidth,$XPos,$YPos+$OuterTickWidth,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); + $Bounds = $this->pChartObject->drawText($XPos,$YPos+$OuterTickWidth+$LabelOffset,$Value,array("Angle"=>$XLabelsRotation,"Align"=>$LabelAlign)); + $TxtBottom = $YPos+2+$OuterTickWidth+2+($Bounds[0]["Y"]-$Bounds[2]["Y"]); + $MaxBottom = max($MaxBottom,$TxtBottom); + + $LastX = $XPos; + } + + if ( isset($AxisSettings["Name"]) ) + { + $YPos = $MaxBottom+2; + $XPos = $this->pChartObject->GraphAreaX1+($this->pChartObject->GraphAreaX2-$this->pChartObject->GraphAreaX1)/2; + $Bounds = $this->pChartObject->drawText($XPos,$YPos,$AxisSettings["Name"],array("Align"=>TEXT_ALIGN_TOPMIDDLE)); + $MaxBottom = $Bounds[0]["Y"]; + + $this->pDataObject->Data["GraphArea"]["Y2"] = $MaxBottom + $this->pChartObject->FontSize; + } + + $AxisPos["B"] = $MaxBottom + $ScaleSpacing; + } + elseif ( $AxisSettings["Position"] == AXIS_POSITION_TOP ) + { + if ( $XLabelsRotation == 0 ) { $LabelAlign = TEXT_ALIGN_BOTTOMMIDDLE; $LabelOffset = 2; } + if ( $XLabelsRotation > 0 && $XLabelsRotation < 190 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $LabelOffset = 2; } + if ( $XLabelsRotation == 180 ) { $LabelAlign = TEXT_ALIGN_TOPMIDDLE; $LabelOffset = 5; } + if ( $XLabelsRotation > 180 && $SLabelxRotation < 360 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $LabelOffset = 5; } + + if ( $Floating ) + { $FloatingOffset = $YMargin; $this->pChartObject->drawLine($this->pChartObject->GraphAreaX1+$AxisSettings["Margin"],$AxisPos["T"],$this->pChartObject->GraphAreaX2-$AxisSettings["Margin"],$AxisPos["T"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + else + { $FloatingOffset = 0; $this->pChartObject->drawLine($this->pChartObject->GraphAreaX1,$AxisPos["T"],$this->pChartObject->GraphAreaX2,$AxisPos["T"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + + if ( $DrawArrows ) { $this->pChartObject->drawArrow($this->pChartObject->GraphAreaX2-$AxisSettings["Margin"],$AxisPos["T"],$this->pChartObject->GraphAreaX2+($ArrowSize*2),$AxisPos["T"],array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } + + $Width = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1) - $AxisSettings["Margin"]*2; + $Step = $Width / $AxisSettings["Rows"]; $SubTicksSize = $Step /2; $MinTop = $AxisPos["T"]; + $LastX = NULL; + for($i=0;$i<=$AxisSettings["Rows"];$i++) + { + $XPos = $this->pChartObject->GraphAreaX1 + $AxisSettings["Margin"] + $Step*$i; + $YPos = $AxisPos["T"]; + $Value = $this->pChartObject->scaleFormat($AxisSettings["ScaleMin"] + $AxisSettings["RowHeight"]*$i,$AxisSettings["Display"],$AxisSettings["Format"],$AxisSettings["Unit"]); + + if ( $i%2 == 1 ) { $BGColor = array("R"=>$BackgroundR1,"G"=>$BackgroundG1,"B"=>$BackgroundB1,"Alpha"=>$BackgroundAlpha1); } else { $BGColor = array("R"=>$BackgroundR2,"G"=>$BackgroundG2,"B"=>$BackgroundB2,"Alpha"=>$BackgroundAlpha2); } + if ( $LastX != NULL && $CycleBackground && ( $DrawXLines == ALL || in_array($AxisID,$DrawXLines) )) { $this->pChartObject->drawFilledRectangle($LastX,$this->pChartObject->GraphAreaY1+$FloatingOffset,$XPos,$this->pChartObject->GraphAreaY2-$FloatingOffset,$BGColor); } + + if ( $DrawXLines == ALL || in_array($AxisID,$DrawXLines) ) { $this->pChartObject->drawLine($XPos,$this->pChartObject->GraphAreaY1+$FloatingOffset,$XPos,$this->pChartObject->GraphAreaY2-$FloatingOffset,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } + + if ( $DrawSubTicks && $i != $AxisSettings["Rows"] ) + $this->pChartObject->drawLine($XPos+$SubTicksSize,$YPos-$OuterSubTickWidth,$XPos+$SubTicksSize,$YPos+$InnerSubTickWidth,array("R"=>$SubTickR,"G"=>$SubTickG,"B"=>$SubTickB,"Alpha"=>$SubTickAlpha)); + + $this->pChartObject->drawLine($XPos,$YPos-$OuterTickWidth,$XPos,$YPos+$InnerTickWidth,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); + $Bounds = $this->pChartObject->drawText($XPos,$YPos-$OuterTickWidth-$LabelOffset,$Value,array("Angle"=>$XLabelsRotation,"Align"=>$LabelAlign)); + $TxtBox = $YPos-$OuterTickWidth-4-($Bounds[0]["Y"]-$Bounds[2]["Y"]); + $MinTop = min($MinTop,$TxtBox); + + $LastX = $XPos; + } + + if ( isset($AxisSettings["Name"]) ) + { + $YPos = $MinTop-2; + $XPos = $this->pChartObject->GraphAreaX1+($this->pChartObject->GraphAreaX2-$this->pChartObject->GraphAreaX1)/2; + $Bounds = $this->pChartObject->drawText($XPos,$YPos,$AxisSettings["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); + $MinTop = $Bounds[2]["Y"]; + + $this->pDataObject->Data["GraphArea"]["Y1"] = $MinTop; + } + + $AxisPos["T"] = $MinTop - $ScaleSpacing; + } + } + elseif ( $AxisSettings["Identity"] == AXIS_Y ) + { + if ( $AxisSettings["Position"] == AXIS_POSITION_LEFT ) + { + + if ( $Floating ) + { $FloatingOffset = $XMargin; $this->pChartObject->drawLine($AxisPos["L"],$this->pChartObject->GraphAreaY1+$AxisSettings["Margin"],$AxisPos["L"],$this->pChartObject->GraphAreaY2-$AxisSettings["Margin"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + else + { $FloatingOffset = 0; $this->pChartObject->drawLine($AxisPos["L"],$this->pChartObject->GraphAreaY1,$AxisPos["L"],$this->pChartObject->GraphAreaY2,array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + + if ( $DrawArrows ) { $this->pChartObject->drawArrow($AxisPos["L"],$this->pChartObject->GraphAreaY1+$AxisSettings["Margin"],$AxisPos["L"],$this->pChartObject->GraphAreaY1-($ArrowSize*2),array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } + + $Height = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1) - $AxisSettings["Margin"]*2; + $Step = $Height / $AxisSettings["Rows"]; $SubTicksSize = $Step /2; $MinLeft = $AxisPos["L"]; + $LastY = NULL; + for($i=0;$i<=$AxisSettings["Rows"];$i++) + { + $YPos = $this->pChartObject->GraphAreaY2 - $AxisSettings["Margin"] - $Step*$i; + $XPos = $AxisPos["L"]; + $Value = $this->pChartObject->scaleFormat($AxisSettings["ScaleMin"] + $AxisSettings["RowHeight"]*$i,$AxisSettings["Display"],$AxisSettings["Format"],$AxisSettings["Unit"]); + + if ( $i%2 == 1 ) { $BGColor = array("R"=>$BackgroundR1,"G"=>$BackgroundG1,"B"=>$BackgroundB1,"Alpha"=>$BackgroundAlpha1); } else { $BGColor = array("R"=>$BackgroundR2,"G"=>$BackgroundG2,"B"=>$BackgroundB2,"Alpha"=>$BackgroundAlpha2); } + if ( $LastY != NULL && $CycleBackground && ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) )) { $this->pChartObject->drawFilledRectangle($this->pChartObject->GraphAreaX1+$FloatingOffset,$LastY,$this->pChartObject->GraphAreaX2-$FloatingOffset,$YPos,$BGColor); } + + if ( ($YPos != $this->pChartObject->GraphAreaY1 && $YPos != $this->pChartObject->GraphAreaY2) && ($DrawYLines == ALL || in_array($AxisID,$DrawYLines) )) { $this->pChartObject->drawLine($this->pChartObject->GraphAreaX1+$FloatingOffset,$YPos,$this->pChartObject->GraphAreaX2-$FloatingOffset,$YPos,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } + + if ( $DrawSubTicks && $i != $AxisSettings["Rows"] ) + $this->pChartObject->drawLine($XPos-$OuterSubTickWidth,$YPos-$SubTicksSize,$XPos+$InnerSubTickWidth,$YPos-$SubTicksSize,array("R"=>$SubTickR,"G"=>$SubTickG,"B"=>$SubTickB,"Alpha"=>$SubTickAlpha)); + + $this->pChartObject->drawLine($XPos-$OuterTickWidth,$YPos,$XPos+$InnerTickWidth,$YPos,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); + $Bounds = $this->pChartObject->drawText($XPos-$OuterTickWidth-2,$YPos,$Value,array("Align"=>TEXT_ALIGN_MIDDLERIGHT)); + $TxtLeft = $XPos-$OuterTickWidth-2-($Bounds[1]["X"]-$Bounds[0]["X"]); + $MinLeft = min($MinLeft,$TxtLeft); + + $LastY = $YPos; + } + + if ( isset($AxisSettings["Name"]) ) + { + $XPos = $MinLeft-2; + $YPos = $this->pChartObject->GraphAreaY1+($this->pChartObject->GraphAreaY2-$this->pChartObject->GraphAreaY1)/2; + $Bounds = $this->pChartObject->drawText($XPos,$YPos,$AxisSettings["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE,"Angle"=>90)); + $MinLeft = $Bounds[2]["X"]; + + $this->pDataObject->Data["GraphArea"]["X1"] = $MinLeft; + } + + $AxisPos["L"] = $MinLeft - $ScaleSpacing; + } + elseif ( $AxisSettings["Position"] == AXIS_POSITION_RIGHT ) + { + + if ( $Floating ) + { $FloatingOffset = $XMargin; $this->pChartObject->drawLine($AxisPos["R"],$this->pChartObject->GraphAreaY1+$AxisSettings["Margin"],$AxisPos["R"],$this->pChartObject->GraphAreaY2-$AxisSettings["Margin"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + else + { $FloatingOffset = 0; $this->pChartObject->drawLine($AxisPos["R"],$this->pChartObject->GraphAreaY1,$AxisPos["R"],$this->pChartObject->GraphAreaY2,array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } + + if ( $DrawArrows ) { $this->pChartObject->drawArrow($AxisPos["R"],$this->pChartObject->GraphAreaY1+$AxisSettings["Margin"],$AxisPos["R"],$this->pChartObject->GraphAreaY1-($ArrowSize*2),array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } + + $Height = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1) - $AxisSettings["Margin"]*2; + $Step = $Height / $AxisSettings["Rows"]; $SubTicksSize = $Step /2; $MaxLeft = $AxisPos["R"]; + $LastY = NULL; + for($i=0;$i<=$AxisSettings["Rows"];$i++) + { + $YPos = $this->pChartObject->GraphAreaY2 - $AxisSettings["Margin"] - $Step*$i; + $XPos = $AxisPos["R"]; + $Value = $this->pChartObject->scaleFormat($AxisSettings["ScaleMin"] + $AxisSettings["RowHeight"]*$i,$AxisSettings["Display"],$AxisSettings["Format"],$AxisSettings["Unit"]); + + if ( $i%2 == 1 ) { $BGColor = array("R"=>$BackgroundR1,"G"=>$BackgroundG1,"B"=>$BackgroundB1,"Alpha"=>$BackgroundAlpha1); } else { $BGColor = array("R"=>$BackgroundR2,"G"=>$BackgroundG2,"B"=>$BackgroundB2,"Alpha"=>$BackgroundAlpha2); } + if ( $LastY != NULL && $CycleBackground && ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) )) { $this->pChartObject->drawFilledRectangle($this->pChartObject->GraphAreaX1+$FloatingOffset,$LastY,$this->pChartObject->GraphAreaX2-$FloatingOffset,$YPos,$BGColor); } + + if ( ($YPos != $this->pChartObject->GraphAreaY1 && $YPos != $this->pChartObject->GraphAreaY2) && ($DrawYLines == ALL || in_array($AxisID,$DrawYLines)) ) { $this->pChartObject->drawLine($this->pChartObject->GraphAreaX1+$FloatingOffset,$YPos,$this->pChartObject->GraphAreaX2-$FloatingOffset,$YPos,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); } + + if ( $DrawSubTicks && $i != $AxisSettings["Rows"] ) + $this->pChartObject->drawLine($XPos-$InnerSubTickWidth,$YPos-$SubTicksSize,$XPos+$OuterSubTickWidth,$YPos-$SubTicksSize,array("R"=>$SubTickR,"G"=>$SubTickG,"B"=>$SubTickB,"Alpha"=>$SubTickAlpha)); + + $this->pChartObject->drawLine($XPos-$InnerTickWidth,$YPos,$XPos+$OuterTickWidth,$YPos,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); + $Bounds = $this->pChartObject->drawText($XPos+$OuterTickWidth+2,$YPos,$Value,array("Align"=>TEXT_ALIGN_MIDDLELEFT)); + $TxtLeft = $XPos+$OuterTickWidth+2+($Bounds[1]["X"]-$Bounds[0]["X"]); + $MaxLeft = max($MaxLeft,$TxtLeft); + + $LastY = $YPos; + } + + if ( isset($AxisSettings["Name"]) ) + { + $XPos = $MaxLeft+6; + $YPos = $this->pChartObject->GraphAreaY1+($this->pChartObject->GraphAreaY2-$this->pChartObject->GraphAreaY1)/2; + $Bounds = $this->pChartObject->drawText($XPos,$YPos,$AxisSettings["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE,"Angle"=>270)); + $MaxLeft = $Bounds[2]["X"]; + + $this->pDataObject->Data["GraphArea"]["X2"] = $MaxLeft + $this->pChartObject->FontSize; + } + + $AxisPos["R"] = $MaxLeft + $ScaleSpacing; + } + } + } + + $this->pDataObject->saveAxisConfig($Data["Axis"]); + } + + /* Draw a scatter plot chart */ + function drawScatterPlotChart($Format=NULL) + { + $PlotSize = isset($Format["PlotSize"]) ? $Format["PlotSize"] : 3; + $PlotBorder = isset($Format["PlotBorder"]) ? $Format["PlotBorder"] : FALSE; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 250; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 250; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 250; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 30; + $BorderSize = isset($Format["BorderSize"]) ? $Format["BorderSize"] : 1; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; + $ImageMapTitle = isset($Format["ImageMapTitle"]) ? $Format["ImageMapTitle"] : NULL; + $ImageMapPrecision = isset($Format["ImageMapPrecision"]) ? $Format["ImageMapPrecision"] : 2; + + $Data = $this->pDataObject->getData(); + $Palette = $this->pDataObject->getPalette(); + + $BorderColor = array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha); + + foreach($Data["ScatterSeries"] as $Key => $Series) + { + if ( $Series["isDrawable"] == TRUE ) + { + $SerieX = $Series["X"]; $SerieValuesX = $Data["Series"][$SerieX]["Data"]; $SerieXAxis = $Data["Series"][$SerieX]["Axis"]; + $SerieY = $Series["Y"]; $SerieValuesY = $Data["Series"][$SerieY]["Data"]; $SerieYAxis = $Data["Series"][$SerieY]["Axis"]; + + if ( $ImageMapTitle == NULL ) { $Description = $Data["Series"][$Series["X"]]["Description"]." / ".$Data["Series"][$Series["Y"]]["Description"]; } else { $Description = $ImageMapTitle; } + + if ( isset($Series["Picture"]) && $Series["Picture"] != "" ) + { $Picture = $Series["Picture"]; list($PicWidth,$PicHeight,$PicType) = $this->pChartObject->getPicInfo($Picture); } + else + { $Picture = NULL; } + + $PosArrayX = $this->getPosArray($SerieValuesX,$SerieXAxis); + if ( !is_array($PosArrayX) ) { $Value = $PosArrayX; $PosArrayX = ""; $PosArrayX[0] = $Value; } + $PosArrayY = $this->getPosArray($SerieValuesY,$SerieYAxis); + if ( !is_array($PosArrayY) ) { $Value = $PosArrayY; $PosArrayY = ""; $PosArrayY[0] = $Value; } + + $Color = array("R"=>$Series["Color"]["R"],"G"=>$Series["Color"]["G"],"B"=>$Series["Color"]["B"],"Alpha"=>$Series["Color"]["Alpha"]); + + foreach($PosArrayX as $Key => $Value) + { + $X = $Value; $Y = $PosArrayY[$Key]; + + if ( $X != VOID && $Y != VOID ) + { + $RealValue = round($Data["Series"][$Series["X"]]["Data"][$Key],2)." / ".round($Data["Series"][$Series["Y"]]["Data"][$Key],2); + if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".floor($PlotSize+$BorderSize),$this->pChartObject->toHTMLColor($Series["Color"]["R"],$Series["Color"]["G"],$Series["Color"]["B"]),$Description,$RealValue); } + + if( isset($Series["Shape"]) ) + { $this->pChartObject->drawShape($X,$Y,$Series["Shape"],$PlotSize,$PlotBorder,$BorderSize,$Series["Color"]["R"],$Series["Color"]["G"],$Series["Color"]["B"],$Series["Color"]["Alpha"],$BorderR,$BorderG,$BorderB,$BorderAlpha); } + elseif ( $Picture == NULL ) + { + if ( $PlotBorder ) { $this->pChartObject->drawFilledCircle($X,$Y,$PlotSize+$BorderSize,$BorderColor); } + $this->pChartObject->drawFilledCircle($X,$Y,$PlotSize,$Color); + } + else + { $this->pChartObject->drawFromPicture($PicType,$Picture,$X-$PicWidth/2,$Y-$PicHeight/2); } + } + } + } + } + } + + /* Draw a scatter line chart */ + function drawScatterLineChart($Format=NULL) + { + $Data = $this->pDataObject->getData(); + $Palette = $this->pDataObject->getPalette(); + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; + $ImageMapTitle = isset($Format["ImageMapTitle"]) ? $Format["ImageMapTitle"] : NULL; + $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 10; + $ImageMapPrecision = isset($Format["ImageMapPrecision"]) ? $Format["ImageMapPrecision"] : 2; + + /* Parse all the series to draw */ + foreach($Data["ScatterSeries"] as $Key => $Series) + { + if ( $Series["isDrawable"] == TRUE ) + { + $SerieX = $Series["X"]; $SerieValuesX = $Data["Series"][$SerieX]["Data"]; $SerieXAxis = $Data["Series"][$SerieX]["Axis"]; + $SerieY = $Series["Y"]; $SerieValuesY = $Data["Series"][$SerieY]["Data"]; $SerieYAxis = $Data["Series"][$SerieY]["Axis"]; + $Ticks = $Series["Ticks"]; + $Weight = $Series["Weight"]; + + if ( $ImageMapTitle == NULL ) { $Description = $Data["Series"][$Series["X"]]["Description"]." / ".$Data["Series"][$Series["Y"]]["Description"]; } else { $Description = $ImageMapTitle; } + + $PosArrayX = $this->getPosArray($SerieValuesX,$SerieXAxis); + if ( !is_array($PosArrayX) ) { $Value = $PosArrayX; $PosArrayX = ""; $PosArrayX[0] = $Value; } + $PosArrayY = $this->getPosArray($SerieValuesY,$SerieYAxis); + if ( !is_array($PosArrayY) ) { $Value = $PosArrayY; $PosArrayY = ""; $PosArrayY[0] = $Value; } + + $Color = array("R"=>$Series["Color"]["R"],"G"=>$Series["Color"]["G"],"B"=>$Series["Color"]["B"],"Alpha"=>$Series["Color"]["Alpha"]); + if ( $Ticks != 0 ) { $Color["Ticks"] = $Ticks; } + if ( $Weight != 0 ) { $Color["Weight"] = $Weight; } + + $LastX = VOID; $LastY = VOID; + foreach($PosArrayX as $Key => $Value) + { + $X = $Value; $Y = $PosArrayY[$Key]; + + if ( $X != VOID && $Y != VOID ) + { + $RealValue = round($Data["Series"][$Series["X"]]["Data"][$Key],2)." / ".round($Data["Series"][$Series["Y"]]["Data"][$Key],2); + if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$ImageMapPlotSize,$this->pChartObject->toHTMLColor($Series["Color"]["R"],$Series["Color"]["G"],$Series["Color"]["B"]),$Description,$RealValue); } + } + + if ( $X != VOID && $Y != VOID && $LastX != VOID && $LastY != VOID) + $this->pChartObject->drawLine($LastX,$LastY,$X,$Y,$Color); + + $LastX = $X; $LastY = $Y; + } + } + } + } + + /* Draw a scatter spline chart */ + function drawScatterSplineChart($Format=NULL) + { + $Data = $this->pDataObject->getData(); + $Palette = $this->pDataObject->getPalette(); + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; + $ImageMapTitle = isset($Format["ImageMapTitle"]) ? $Format["ImageMapTitle"] : NULL; + $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 10; + $ImageMapPrecision = isset($Format["ImageMapPrecision"]) ? $Format["ImageMapPrecision"] : 2; + + foreach($Data["ScatterSeries"] as $Key => $Series) + { + if ( $Series["isDrawable"] == TRUE ) + { + $SerieX = $Series["X"]; $SerieValuesX = $Data["Series"][$SerieX]["Data"]; $SerieXAxis = $Data["Series"][$SerieX]["Axis"]; + $SerieY = $Series["Y"]; $SerieValuesY = $Data["Series"][$SerieY]["Data"]; $SerieYAxis = $Data["Series"][$SerieY]["Axis"]; + $Ticks = $Series["Ticks"]; + $Weight = $Series["Weight"]; + + if ( $ImageMapTitle == NULL ) { $Description = $Data["Series"][$Series["X"]]["Description"]." / ".$Data["Series"][$Series["Y"]]["Description"]; } else { $Description = $ImageMapTitle; } + + $PosArrayX = $this->getPosArray($SerieValuesX,$SerieXAxis); + if ( !is_array($PosArrayX) ) { $Value = $PosArrayX; $PosArrayX = ""; $PosArrayX[0] = $Value; } + $PosArrayY = $this->getPosArray($SerieValuesY,$SerieYAxis); + if ( !is_array($PosArrayY) ) { $Value = $PosArrayY; $PosArrayY = ""; $PosArrayY[0] = $Value; } + + $SplineSettings = array("R"=>$Series["Color"]["R"],"G"=>$Series["Color"]["G"],"B"=>$Series["Color"]["B"],"Alpha"=>$Series["Color"]["Alpha"]); + if ( $Ticks != 0 ) { $SplineSettings["Ticks"] = $Ticks; } + if ( $Weight != 0 ) { $SplineSettings["Weight"] = $Weight; } + + $LastX = VOID; $LastY = VOID; $WayPoints = ""; $Forces = ""; + foreach($PosArrayX as $Key => $Value) + { + $X = $Value; $Y = $PosArrayY[$Key]; + $Force = $this->pChartObject->getLength($LastX,$LastY,$X,$Y)/5; + + if ( $X != VOID && $Y != VOID ) + { + $RealValue = round($Data["Series"][$Series["X"]]["Data"][$Key],2)." / ".round($Data["Series"][$Series["Y"]]["Data"][$Key],2); + if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$ImageMapPlotSize,$this->pChartObject->toHTMLColor($Series["Color"]["R"],$Series["Color"]["G"],$Series["Color"]["B"]),$Description,$RealValue); } + } + + if ( $X != VOID && $Y != VOID ) + { $WayPoints[] = array($X,$Y); $Forces[] = $Force; } + + if ( $Y == VOID || $X == VOID ) + { $SplineSettings["Forces"] = $Forces; $this->pChartObject->drawSpline($WayPoints,$SplineSettings); $WayPoints = ""; $Forces = "";} + + $LastX = $X; $LastY = $Y; + } + $SplineSettings["Forces"] = $Forces; + $this->pChartObject->drawSpline($WayPoints,$SplineSettings); + } + } + } + + /* Return the scaled plot position */ + function getPosArray($Values,$AxisID) + { + $Data = $this->pDataObject->getData(); + + if ( !is_array($Values) ) { $Values = array($Values); } + + if ( $Data["Axis"][$AxisID]["Identity"] == AXIS_X ) + { + $Height = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1) - $Data["Axis"][$AxisID]["Margin"]*2; + $ScaleHeight = $Data["Axis"][$AxisID]["ScaleMax"] - $Data["Axis"][$AxisID]["ScaleMin"]; + $Step = $Height / $ScaleHeight; + + $Result = ""; + foreach($Values as $Key => $Value) + { + if ( $Value == VOID ) + $Result[] = VOID; + else + $Result[] = $this->pChartObject->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"] + ($Step * ($Value-$Data["Axis"][$AxisID]["ScaleMin"])); + } + + if ( count($Result) == 1 ) { return($Result[0]); } else { return($Result); } + } + else + { + $Height = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1) - $Data["Axis"][$AxisID]["Margin"]*2; + $ScaleHeight = $Data["Axis"][$AxisID]["ScaleMax"] - $Data["Axis"][$AxisID]["ScaleMin"]; + $Step = $Height / $ScaleHeight; + + $Result = ""; + foreach($Values as $Key => $Value) + { + if ( $Value == VOID ) + $Result[] = VOID; + else + $Result[] = $this->pChartObject->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"] - ($Step * ($Value-$Data["Axis"][$AxisID]["ScaleMin"])); + } + + if ( count($Result) == 1 ) { return($Result[0]); } else { return($Result); } + } + } + + /* Draw the legend of the active series */ + function drawScatterLegend($X,$Y,$Format="") + { + $Family = isset($Format["Family"]) ? $Format["Family"] : LEGEND_FAMILY_BOX; + $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->pChartObject->FontName; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->pChartObject->FontSize; + $FontR = isset($Format["FontR"]) ? $Format["FontR"] : $this->pChartObject->FontColorR; + $FontG = isset($Format["FontG"]) ? $Format["FontG"] : $this->pChartObject->FontColorG; + $FontB = isset($Format["FontB"]) ? $Format["FontB"] : $this->pChartObject->FontColorB; + $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 5; + $BoxHeight = isset($Format["BoxHeight"]) ? $Format["BoxHeight"] : 5; + $IconAreaWidth = isset($Format["IconAreaWidth"]) ? $Format["IconAreaWidth"] : $BoxWidth; + $IconAreaHeight = isset($Format["IconAreaHeight"]) ? $Format["IconAreaHeight"] : $BoxHeight; + $XSpacing = isset($Format["XSpacing"]) ? $Format["XSpacing"] : 5; + $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5; + $R = isset($Format["R"]) ? $Format["R"] : 200; + $G = isset($Format["G"]) ? $Format["G"] : 200; + $B = isset($Format["B"]) ? $Format["B"] : 200; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; + $Style = isset($Format["Style"]) ? $Format["Style"] : LEGEND_ROUND; + $Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL; + + if ( $Surrounding != NULL ) { $BorderR = $R + $Surrounding; $BorderG = $G + $Surrounding; $BorderB = $B + $Surrounding; } + + $Data = $this->pDataObject->getData(); + + foreach($Data["ScatterSeries"] as $Key => $Series) + { + if ( $Series["isDrawable"] == TRUE && isset($Series["Picture"])) + { + list($PicWidth,$PicHeight) = $this->pChartObject->getPicInfo($Series["Picture"]); + if ( $IconAreaWidth < $PicWidth ) { $IconAreaWidth = $PicWidth; } + if ( $IconAreaHeight < $PicHeight ) { $IconAreaHeight = $PicHeight; } + } + } + + $YStep = max($this->pChartObject->FontSize,$IconAreaHeight) + 5; + $XStep = $IconAreaWidth + 5; + $XStep = $XSpacing; + + $Boundaries = ""; $Boundaries["L"] = $X; $Boundaries["T"] = $Y; $Boundaries["R"] = 0; $Boundaries["B"] = 0; $vY = $Y; $vX = $X; + foreach($Data["ScatterSeries"] as $Key => $Series) + { + if ( $Series["isDrawable"] == TRUE ) + { + if ( $Mode == LEGEND_VERTICAL ) + { + $BoxArray = $this->pChartObject->getTextBox($vX+$IconAreaWidth+4,$vY+$IconAreaHeight/2,$FontName,$FontSize,0,$Series["Description"]); + + if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$IconAreaHeight/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$IconAreaHeight/2; } + if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; } + if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$IconAreaHeight/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$IconAreaHeight/2; } + + $Lines = preg_split("/\n/",$Series["Description"]); + $vY = $vY + max($this->pChartObject->FontSize*count($Lines),$IconAreaHeight) + 5; + } + elseif ( $Mode == LEGEND_HORIZONTAL ) + { + $Lines = preg_split("/\n/",$Series["Description"]); + $Width = ""; + foreach($Lines as $Key => $Value) + { + $BoxArray = $this->pChartObject->getTextBox($vX+$IconAreaWidth+6,$Y+$IconAreaHeight/2+(($this->pChartObject->FontSize+3)*$Key),$FontName,$FontSize,0,$Value); + + if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$IconAreaHeight/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$IconAreaHeight/2; } + if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; } + if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$IconAreaHeight/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$IconAreaHeight/2; } + + $Width[] = $BoxArray[1]["X"]; + } + + $vX=max($Width)+$XStep; + } + } + } + $vY=$vY-$YStep; $vX=$vX-$XStep; + + $TopOffset = $Y - $Boundaries["T"]; + if ( $Boundaries["B"]-($vY+$IconAreaHeight) < $TopOffset ) { $Boundaries["B"] = $vY+$IconAreaHeight+$TopOffset; } + + if ( $Style == LEGEND_ROUND ) + $this->pChartObject->drawRoundedFilledRectangle($Boundaries["L"]-$Margin,$Boundaries["T"]-$Margin,$Boundaries["R"]+$Margin,$Boundaries["B"]+$Margin,$Margin,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB)); + elseif ( $Style == LEGEND_BOX ) + $this->pChartObject->drawFilledRectangle($Boundaries["L"]-$Margin,$Boundaries["T"]-$Margin,$Boundaries["R"]+$Margin,$Boundaries["B"]+$Margin,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB)); + + $RestoreShadow = $this->pChartObject->Shadow; $this->Shadow = FALSE; + foreach($Data["ScatterSeries"] as $Key => $Series) + { + if ( $Series["isDrawable"] == TRUE ) + { + $R = $Series["Color"]["R"]; $G = $Series["Color"]["G"]; $B = $Series["Color"]["B"]; + $Ticks = $Series["Ticks"]; $Weight = $Series["Weight"]; + + if ( isset($Series["Picture"]) ) + { + $Picture = $Series["Picture"]; + list($PicWidth,$PicHeight) = $this->pChartObject->getPicInfo($Picture); + $PicX = $X+$IconAreaWidth/2; $PicY = $Y+$IconAreaHeight/2; + + $this->pChartObject->drawFromPNG($PicX-$PicWidth/2,$PicY-$PicHeight/2,$Picture); + } + else + { + if ( $Family == LEGEND_FAMILY_BOX ) + { + if ( $BoxWidth != $IconAreaWidth ) { $XOffset = floor(($IconAreaWidth-$BoxWidth)/2); } else { $XOffset = 0; } + if ( $BoxHeight != $IconAreaHeight ) { $YOffset = floor(($IconAreaHeight-$BoxHeight)/2); } else { $YOffset = 0; } + + $this->pChartObject->drawFilledRectangle($X+1+$XOffset,$Y+1+$YOffset,$X+$BoxWidth+$XOffset+1,$Y+$BoxHeight+1+$YOffset,array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20)); + $this->pChartObject->drawFilledRectangle($X+$XOffset,$Y+$YOffset,$X+$BoxWidth+$XOffset,$Y+$BoxHeight+$YOffset,array("R"=>$R,"G"=>$G,"B"=>$B,"Surrounding"=>20)); + } + elseif ( $Family == LEGEND_FAMILY_CIRCLE ) + { + $this->pChartObject->drawFilledCircle($X+1+$IconAreaWidth/2,$Y+1+$IconAreaHeight/2,min($IconAreaHeight/2,$IconAreaWidth/2),array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20)); + $this->pChartObject->drawFilledCircle($X+$IconAreaWidth/2,$Y+$IconAreaHeight/2,min($IconAreaHeight/2,$IconAreaWidth/2),array("R"=>$R,"G"=>$G,"B"=>$B,"Surrounding"=>20)); + } + elseif ( $Family == LEGEND_FAMILY_LINE ) + { + $this->pChartObject->drawLine($X+1,$Y+1+$IconAreaHeight/2,$X+1+$IconAreaWidth,$Y+1+$IconAreaHeight/2,array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20,"Ticks"=>$Ticks,"Weight"=>$Weight)); + $this->pChartObject->drawLine($X,$Y+$IconAreaHeight/2,$X+$IconAreaWidth,$Y+$IconAreaHeight/2,array("R"=>$R,"G"=>$G,"B"=>$B,"Ticks"=>$Ticks,"Weight"=>$Weight)); + } + } + + if ( $Mode == LEGEND_VERTICAL ) + { + $Lines = preg_split("/\n/",$Series["Description"]); + foreach($Lines as $Key => $Value) + $this->pChartObject->drawText($X+$IconAreaWidth+4,$Y+$IconAreaHeight/2+(($this->pChartObject->FontSize+3)*$Key),$Value,array("R"=>$FontR,"G"=>$FontG,"B"=>$FontB,"Align"=>TEXT_ALIGN_MIDDLELEFT)); + + $Y=$Y+max($this->pChartObject->FontSize*count($Lines),$IconAreaHeight) + 5; + } + elseif ( $Mode == LEGEND_HORIZONTAL ) + { + $Lines = preg_split("/\n/",$Series["Description"]); + $Width = ""; + foreach($Lines as $Key => $Value) + { + $BoxArray = $this->pChartObject->drawText($X+$IconAreaWidth+4,$Y+$IconAreaHeight/2+(($this->pChartObject->FontSize+3)*$Key),$Value,array("R"=>$FontR,"G"=>$FontG,"B"=>$FontB,"Align"=>TEXT_ALIGN_MIDDLELEFT)); + $Width[] = $BoxArray[1]["X"]; + } + $X=max($Width)+2+$XStep; + } + } + } + + $this->Shadow = $RestoreShadow; + } + + /* Get the legend box size */ + function getScatterLegendSize($Format="") + { + $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->pChartObject->FontName; + $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->pChartObject->FontSize; + $BoxSize = isset($Format["BoxSize"]) ? $Format["BoxSize"] : 5; + $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5; + $Style = isset($Format["Style"]) ? $Format["Style"] : LEGEND_ROUND; + $Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL; + + $YStep = max($this->pChartObject->FontSize,$BoxSize) + 5; + $XStep = $BoxSize + 5; + + $X=100; $Y=100; + + $Data = $this->pDataObject->getData(); + + foreach($Data["ScatterSeries"] as $Key => $Series) + { + if ( $Series["isDrawable"] == TRUE && isset($Series["Picture"])) + { + list($PicWidth,$PicHeight) = $this->pChartObject->getPicInfo($Series["Picture"]); + if ( $IconAreaWidth < $PicWidth ) { $IconAreaWidth = $PicWidth; } + if ( $IconAreaHeight < $PicHeight ) { $IconAreaHeight = $PicHeight; } + } + } + + $YStep = max($this->pChartObject->FontSize,$IconAreaHeight) + 5; + $XStep = $IconAreaWidth + 5; + $XStep = $XSpacing; + + $Boundaries = ""; $Boundaries["L"] = $X; $Boundaries["T"] = $Y; $Boundaries["R"] = 0; $Boundaries["B"] = 0; $vY = $Y; $vX = $X; + foreach($Data["ScatterSeries"] as $Key => $Series) + { + if ( $Series["isDrawable"] == TRUE ) + { + if ( $Mode == LEGEND_VERTICAL ) + { + $BoxArray = $this->pChartObject->getTextBox($vX+$IconAreaWidth+4,$vY+$IconAreaHeight/2,$FontName,$FontSize,0,$Series["Description"]); + + if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$IconAreaHeight/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$IconAreaHeight/2; } + if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; } + if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$IconAreaHeight/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$IconAreaHeight/2; } + + $Lines = preg_split("/\n/",$Series["Description"]); + $vY = $vY + max($this->pChartObject->FontSize*count($Lines),$IconAreaHeight) + 5; + } + elseif ( $Mode == LEGEND_HORIZONTAL ) + { + $Lines = preg_split("/\n/",$Series["Description"]); + $Width = ""; + foreach($Lines as $Key => $Value) + { + $BoxArray = $this->pChartObject->getTextBox($vX+$IconAreaWidth+6,$Y+$IconAreaHeight/2+(($this->pChartObject->FontSize+3)*$Key),$FontName,$FontSize,0,$Value); + + if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$IconAreaHeight/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$IconAreaHeight/2; } + if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; } + if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$IconAreaHeight/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$IconAreaHeight/2; } + + $Width[] = $BoxArray[1]["X"]; + } + + $vX=max($Width)+$XStep; + } + } + } + $vY=$vY-$YStep; $vX=$vX-$XStep; + + $TopOffset = $Y - $Boundaries["T"]; + if ( $Boundaries["B"]-($vY+$BoxSize) < $TopOffset ) { $Boundaries["B"] = $vY+$BoxSize+$TopOffset; } + + $Width = ($Boundaries["R"]+$Margin) - ($Boundaries["L"]-$Margin); + $Height = ($Boundaries["B"]+$Margin) - ($Boundaries["T"]-$Margin); + + return(array("Width"=>$Width,"Height"=>$Height)); + } + + /* Draw the line of best fit */ + function drawScatterBestFit($Format="") + { + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : 0; + + $Data = $this->pDataObject->getData(); + + foreach($Data["ScatterSeries"] as $Key => $Series) + { + if ( $Series["isDrawable"] == TRUE ) + { + $SerieX = $Series["X"]; $SerieValuesX = $Data["Series"][$SerieX]["Data"]; $SerieXAxis = $Data["Series"][$SerieX]["Axis"]; + $SerieY = $Series["Y"]; $SerieValuesY = $Data["Series"][$SerieY]["Data"]; $SerieYAxis = $Data["Series"][$SerieY]["Axis"]; + + $Color = array("R"=>$Series["Color"]["R"],"G"=>$Series["Color"]["G"],"B"=>$Series["Color"]["B"],"Alpha"=>$Series["Color"]["Alpha"]); + $Color["Ticks"] = $Ticks; + + $PosArrayX = $Data["Series"][$Series["X"]]["Data"]; + $PosArrayY = $Data["Series"][$Series["Y"]]["Data"]; + + $Sxy = 0; $Sx = 0; $Sy = 0; $Sxx = 0; + foreach($PosArrayX as $Key => $Value) + { + $X = $Value; $Y = $PosArrayY[$Key]; + + $Sxy = $Sxy + $X*$Y; + $Sx = $Sx + $X; + $Sy = $Sy + $Y; + $Sxx = $Sxx + $X*$X; + } + + $n = count($PosArrayX); + + if ((($n*$Sxx) == ($Sx*$Sx))) + { + $X1 = $this->getPosArray($Data["Axis"][$SerieXAxis]["ScaleMin"],$SerieXAxis); + $X2 = $X1; + $Y1 = $this->pChartObject->GraphAreaY1; + $Y2 = $this->pChartObject->GraphAreaY2; + } + else + { + $M = (($n*$Sxy)-($Sx*$Sy)) / (($n*$Sxx)-($Sx*$Sx)); + $B = (($Sy)-($M*$Sx))/($n); + + $X1 = $this->getPosArray($Data["Axis"][$SerieXAxis]["ScaleMin"],$SerieXAxis); + $Y1 = $this->getPosArray($M * $Data["Axis"][$SerieXAxis]["ScaleMin"] + $B,$SerieYAxis); + $X2 = $this->getPosArray($Data["Axis"][$SerieXAxis]["ScaleMax"],$SerieXAxis); + $Y2 = $this->getPosArray($M * $Data["Axis"][$SerieXAxis]["ScaleMax"] + $B,$SerieYAxis); + + $RealM = -($Y2-$Y1)/($X2-$X1); + + if ( $Y1 < $this->pChartObject->GraphAreaY1 ) { $X1 = $X1 + ($this->pChartObject->GraphAreaY1-$Y1/$RealM); $Y1 = $this->pChartObject->GraphAreaY1; } + if ( $Y1 > $this->pChartObject->GraphAreaY2 ) { $X1 = $X1 + ($Y1-$this->pChartObject->GraphAreaY2)/$RealM; $Y1 = $this->pChartObject->GraphAreaY2; } + if ( $Y2 < $this->pChartObject->GraphAreaY1 ) { $X2 = $X2 - ($this->pChartObject->GraphAreaY1-$Y2)/$RealM; $Y2 = $this->pChartObject->GraphAreaY1; } + if ( $Y2 > $this->pChartObject->GraphAreaY2 ) { $X2 = $X2 - ($Y2-$this->pChartObject->GraphAreaY2)/$RealM; $Y2 = $this->pChartObject->GraphAreaY2; } + } + + $this->pChartObject->drawLine($X1,$Y1,$X2,$Y2,$Color); + } + } + } + + function writeScatterLabel($ScatterSerieID,$Points,$Format="") + { + $OverrideTitle = isset($Format["OverrideTitle"]) ? $Format["OverrideTitle"] : NULL; + $DrawPoint = isset($Format["DrawPoint"]) ? $Format["DrawPoint"] : LABEL_POINT_BOX; + $Decimals = isset($Format["Decimals"]) ? $Format["Decimals"] : NULL; + + $Data = $this->pDataObject->getData(); + $Palette = $this->pDataObject->getPalette(); + + if ( !is_array($Points) ) { $Point = $Points; $Points = ""; $Points[0] = $Point; } + + if ( !isset($Data["ScatterSeries"][$ScatterSerieID]) ) + return(0); + + $Series = $Data["ScatterSeries"][$ScatterSerieID]; + $SerieX = $Series["X"]; $SerieValuesX = $Data["Series"][$SerieX]["Data"]; $SerieXAxis = $Data["Series"][$SerieX]["Axis"]; + $SerieY = $Series["Y"]; $SerieValuesY = $Data["Series"][$SerieY]["Data"]; $SerieYAxis = $Data["Series"][$SerieY]["Axis"]; + + $PosArrayX = $this->getPosArray($SerieValuesX,$SerieXAxis); + if ( !is_array($PosArrayX) ) { $Value = $PosArrayX; $PosArrayX = ""; $PosArrayX[0] = $Value; } + $PosArrayY = $this->getPosArray($SerieValuesY,$SerieYAxis); + if ( !is_array($PosArrayY) ) { $Value = $PosArrayY; $PosArrayY = ""; $PosArrayY[0] = $Value; } + + foreach($Points as $Key => $Point) + { + if ( isset($PosArrayX[$Point]) && isset($PosArrayY[$Point]) ) + { + $X = floor($PosArrayX[$Point]); + $Y = floor($PosArrayY[$Point]); + + if ( $DrawPoint == LABEL_POINT_CIRCLE ) + $this->pChartObject->drawFilledCircle($X,$Y,3,array("R"=>255,"G"=>255,"B"=>255,"BorderR"=>0,"BorderG"=>0,"BorderB"=>0)); + elseif ( $DrawPoint == LABEL_POINT_BOX ) + $this->pChartObject->drawFilledRectangle($X-2,$Y-2,$X+2,$Y+2,array("R"=>255,"G"=>255,"B"=>255,"BorderR"=>0,"BorderG"=>0,"BorderB"=>0)); + + $Serie = ""; + $Serie["R"] = $Series["Color"]["R"]; + $Serie["G"] = $Series["Color"]["G"]; + $Serie["B"] = $Series["Color"]["B"]; + $Serie["Alpha"] = $Series["Color"]["Alpha"]; + + $XAxisMode = $Data["Axis"][$SerieXAxis]["Display"]; + $XAxisFormat = $Data["Axis"][$SerieXAxis]["Format"]; + $XAxisUnit = $Data["Axis"][$SerieXAxis]["Unit"]; + if ( $Decimals == NULL ) { $XValue = $SerieValuesX[$Point]; } else { $XValue = round($SerieValuesX[$Point],$Decimals); } + $XValue = $this->pChartObject->scaleFormat($XValue,$XAxisMode,$XAxisFormat,$XAxisUnit); + + $YAxisMode = $Data["Axis"][$SerieYAxis]["Display"]; + $YAxisFormat = $Data["Axis"][$SerieYAxis]["Format"]; + $YAxisUnit = $Data["Axis"][$SerieYAxis]["Unit"]; + if ( $Decimals == NULL ) { $YValue = $SerieValuesY[$Point]; } else { $YValue = round($SerieValuesY[$Point],$Decimals); } + $YValue = $this->pChartObject->scaleFormat($YValue,$YAxisMode,$YAxisFormat,$YAxisUnit); + + $Caption = $XValue." / ".$YValue; + + if ( isset($Series["Description"]) ) + $Description = $Series["Description"]; + else + $Description = "No description"; + + $Series = ""; + $Series[] = array("Format"=>$Serie,"Caption"=>$Caption); + + $this->pChartObject->drawLabelBox($X,$Y-3,$Description,$Series,$Format); + } + } + } + + /* Draw a Scatter threshold */ + function drawScatterThreshold($Value,$Format="") + { + $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0; + $R = isset($Format["R"]) ? $Format["R"] : 255; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 50; + $Weight = isset($Format["Weight"]) ? $Format["Weight"] : NULL; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : 3; + $Wide = isset($Format["Wide"]) ? $Format["Wide"] : FALSE; + $WideFactor = isset($Format["WideFactor"]) ? $Format["WideFactor"] : 5; + $WriteCaption = isset($Format["WriteCaption"]) ? $Format["WriteCaption"] : FALSE; + $Caption = isset($Format["Caption"]) ? $Format["Caption"] : NULL; + $CaptionAlign = isset($Format["CaptionAlign"]) ? $Format["CaptionAlign"] : CAPTION_LEFT_TOP; + $CaptionOffset = isset($Format["CaptionOffset"]) ? $Format["CaptionOffset"] : 10; + $CaptionR = isset($Format["CaptionR"]) ? $Format["CaptionR"] : 255; + $CaptionG = isset($Format["CaptionG"]) ? $Format["CaptionG"] : 255; + $CaptionB = isset($Format["CaptionB"]) ? $Format["CaptionB"] : 255; + $CaptionAlpha = isset($Format["CaptionAlpha"]) ? $Format["CaptionAlpha"] : 100; + $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : TRUE; + $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : FALSE; + $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 5; + $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : TRUE; + $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 3; + $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0; + $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0; + $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0; + $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 20; + $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : ""; + $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 255; + $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 255; + $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 255; + $BoxBorderAlpha = isset($Format["BoxBorderAlpha"]) ? $Format["BoxBorderAlpha"] : 100; + + $CaptionSettings = array("DrawBox"=>$DrawBox,"DrawBoxBorder"=>$DrawBoxBorder,"BorderOffset"=>$BorderOffset,"BoxRounded"=>$BoxRounded,"RoundedRadius"=>$RoundedRadius, + "BoxR"=>$BoxR,"BoxG"=>$BoxG,"BoxB"=>$BoxB,"BoxAlpha"=>$BoxAlpha,"BoxSurrounding"=>$BoxSurrounding, + "BoxBorderR"=>$BoxBorderR,"BoxBorderG"=>$BoxBorderG,"BoxBorderB"=>$BoxBorderB,"BoxBorderAlpha"=>$BoxBorderAlpha, + "R"=>$CaptionR,"G"=>$CaptionG,"B"=>$CaptionB,"Alpha"=>$CaptionAlpha); + + if ( $Caption == NULL ) { $Caption = $Value; } + + $Data = $this->pDataObject->getData(); + + if ( !isset($Data["Axis"][$AxisID]) ) { return(-1); } + + if ( $Data["Axis"][$AxisID]["Identity"] == AXIS_Y ) + { + $X1 = $this->pChartObject->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"]; + $X2 = $this->pChartObject->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"]; + $Y = $this->getPosArray($Value,$AxisID); + + $this->pChartObject->drawLine($X1,$Y,$X2,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); + + if ( $Wide ) + { + $this->pChartObject->drawLine($X1,$Y-1,$X2,$Y-1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); + $this->pChartObject->drawLine($X1,$Y+1,$X2,$Y+1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); + } + + if ( $WriteCaption ) + { + if ( $CaptionAlign == CAPTION_LEFT_TOP ) + { $X = $this->pChartObject->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"] + $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLELEFT; } + else + { $X = $this->pChartObject->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"] - $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLERIGHT; } + + $this->pChartObject->drawText($X,$Y,$Caption,$CaptionSettings); + } + + return(array("Y"=>$Y)); + } + elseif ( $Data["Axis"][$AxisID]["Identity"] == AXIS_X ) + { + $X = $this->getPosArray($Value,$AxisID); + $Y1 = $this->pChartObject->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"]; + $Y2 = $this->pChartObject->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"]; + + $this->pChartObject->drawLine($X,$Y1,$X,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); + + if ( $Wide ) + { + $this->pChartObject->drawLine($X-1,$Y1,$X-1,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); + $this->pChartObject->drawLine($X+1,$Y1,$X+1,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks)); + } + + if ( $WriteCaption ) + { + if ( $CaptionAlign == CAPTION_LEFT_TOP ) + { $Y = $this->pChartObject->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"] + $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE; } + else + { $Y = $this->pChartObject->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"] - $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_BOTTOMMIDDLE; } + + $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE; + $this->pChartObject->drawText($X,$Y,$Caption,$CaptionSettings); + } + + return(array("X"=>$X)); + } + } + + /* Draw a Scatter threshold area */ + function drawScatterThresholdArea($Value1,$Value2,$Format="") + { + $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0; + $R = isset($Format["R"]) ? $Format["R"] : 255; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 20; + $Border = isset($Format["Border"]) ? $Format["Border"] : TRUE; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B; + $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha + 20; + $BorderTicks = isset($Format["BorderTicks"]) ? $Format["BorderTicks"] : 2; + $AreaName = isset($Format["AreaName"]) ? $Format["AreaName"] : "La ouate de phoque"; //NULL; + $NameAngle = isset($Format["NameAngle"]) ? $Format["NameAngle"] : ZONE_NAME_ANGLE_AUTO; + $NameR = isset($Format["NameR"]) ? $Format["NameR"] : 255; + $NameG = isset($Format["NameG"]) ? $Format["NameG"] : 255; + $NameB = isset($Format["NameB"]) ? $Format["NameB"] : 255; + $NameAlpha = isset($Format["NameAlpha"]) ? $Format["NameAlpha"] : 100; + $DisableShadowOnArea = isset($Format["DisableShadowOnArea"]) ? $Format["DisableShadowOnArea"] : TRUE; + + if ($Value1 > $Value2) { list($Value1, $Value2) = array($Value2, $Value1); } + + $RestoreShadow = $this->pChartObject->Shadow; + if ( $DisableShadowOnArea && $this->pChartObject->Shadow ) { $this->pChartObject->Shadow = FALSE; } + + if ($BorderAlpha >100) { $BorderAlpha = 100;} + + $Data = $this->pDataObject->getData(); + + if ( !isset($Data["Axis"][$AxisID]) ) { return(-1); } + + if ( $Data["Axis"][$AxisID]["Identity"] == AXIS_X ) + { + $Y1 = $this->pChartObject->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"]; + $Y2 = $this->pChartObject->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"]; + $X1 = $this->getPosArray($Value1,$AxisID); + $X2 = $this->getPosArray($Value2,$AxisID); + + if ( $X1 <= $this->pChartObject->GraphAreaX1 ) { $X1 = $this->pChartObject->GraphAreaX1+$Data["Axis"][$AxisID]["Margin"]; } + if ( $X2 >= $this->pChartObject->GraphAreaX2 ) { $X2 = $this->pChartObject->GraphAreaX2-$Data["Axis"][$AxisID]["Margin"]; } + + $this->pChartObject->drawFilledRectangle($X1,$Y1,$X2,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); + + if ( $Border ) + { + $this->pChartObject->drawLine($X1,$Y1,$X1,$Y2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); + $this->pChartObject->drawLine($X2,$Y1,$X2,$Y2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); + } + + if ( $AreaName != NULL ) + { + $XPos = ($X2-$X1)/2 + $X1; + $YPos = ($Y2-$Y1)/2 + $Y1; + + if ( $NameAngle == ZONE_NAME_ANGLE_AUTO ) + { + $TxtPos = $this->pChartObject->getTextBox($XPos,$YPos,$this->pChartObject->FontName,$this->pChartObject->FontSize,0,$AreaName); + $TxtWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"]; + if ( abs($X2 - $X1) > $TxtWidth ) { $NameAngle = 0; } else { $NameAngle = 90; } + } + $this->pChartObject->Shadow = $RestoreShadow; + $this->pChartObject->drawText($XPos,$YPos,$AreaName,array("R"=>$NameR,"G"=>$NameG,"B"=>$NameB,"Alpha"=>$NameAlpha,"Angle"=>$NameAngle,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE)); + if ( $DisableShadowOnArea ) { $this->pChartObject->Shadow = FALSE; } + } + + $this->pChartObject->Shadow = $RestoreShadow; + return(array("X1"=>$X1,"X2"=>$X2)); + } + elseif ( $Data["Axis"][$AxisID]["Identity"] == AXIS_Y ) + { + $X1 = $this->pChartObject->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"]; + $X2 = $this->pChartObject->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"]; + $Y1 = $this->getPosArray($Value1,$AxisID); + $Y2 = $this->getPosArray($Value2,$AxisID); + + if ( $Y1 >= $this->pChartObject->GraphAreaY2 ) { $Y1 = $this->pChartObject->GraphAreaY2-$Data["Axis"][$AxisID]["Margin"]; } + if ( $Y2 <= $this->pChartObject->GraphAreaY1 ) { $Y2 = $this->pChartObject->GraphAreaY1+$Data["Axis"][$AxisID]["Margin"]; } + + $this->pChartObject->drawFilledRectangle($X1,$Y1,$X2,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha)); + + if ( $Border ) + { + $this->pChartObject->drawLine($X1,$Y1,$X2,$Y1,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); + $this->pChartObject->drawLine($X1,$Y2,$X2,$Y2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks)); + } + + if ( $AreaName != NULL ) + { + $XPos = ($X2-$X1)/2 + $X1; + $YPos = ($Y2-$Y1)/2 + $Y1; + + $this->pChartObject->Shadow = $RestoreShadow; + $this->pChartObject->drawText($YPos,$XPos,$AreaName,array("R"=>$NameR,"G"=>$NameG,"B"=>$NameB,"Alpha"=>$NameAlpha,"Angle"=>0,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE)); + if ( $DisableShadowOnArea ) { $this->Shadow = FALSE; } + } + + $this->pChartObject->Shadow = $RestoreShadow; + return(array("Y1"=>$Y1,"Y2"=>$Y2)); + } + } + } ?> \ No newline at end of file diff --git a/www/libs/pChart2.1.3/class/pSplit.class.php b/www/libs/pChart2.1.4/class/pSplit.class.php similarity index 96% rename from www/libs/pChart2.1.3/class/pSplit.class.php rename to www/libs/pChart2.1.4/class/pSplit.class.php index 248ffeaa..30436036 100644 --- a/www/libs/pChart2.1.3/class/pSplit.class.php +++ b/www/libs/pChart2.1.4/class/pSplit.class.php @@ -1,131 +1,131 @@ -pChartObject = $Object; - - $Spacing = isset($Format["Spacing"]) ? $Format["Spacing"] : 20; - $TextPadding = isset($Format["TextPadding"]) ? $Format["TextPadding"] : 2; - $TextPos = isset($Format["TextPos"]) ? $Format["TextPos"] : TEXT_POS_TOP; - $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; - $Force = isset($Format["Force"]) ? $Format["Force"] : 70; - $Segments = isset($Format["Segments"]) ? $Format["Segments"] : 15; - $FontSize = $Object->FontSize; - $X1 = $Object->GraphAreaX1; - $Y1 = $Object->GraphAreaY1; - $X2 = $Object->GraphAreaX2; - $Y2 = $Object->GraphAreaY2; - - /* Data Processing */ - $Data = $Values->getData(); - $Palette = $Values->getPalette(); - - $LabelSerie = $Data["Abscissa"]; - $DataSerie = ""; - - foreach($Data["Series"] as $SerieName => $Value) - { if ( $SerieName != $LabelSerie && $DataSerie == "" ) { $DataSerie = $SerieName; } } - - $DataSerieSum = array_sum($Data["Series"][$DataSerie]["Data"]); - $DataSerieCount = count($Data["Series"][$DataSerie]["Data"]); - - /* Scale Processing */ - if ( $TextPos == TEXT_POS_RIGHT ) - $YScale = (($Y2-$Y1) - (($DataSerieCount+1)*$Spacing)) / $DataSerieSum; - else - $YScale = (($Y2-$Y1) - ($DataSerieCount*$Spacing)) / $DataSerieSum; - $LeftHeight = $DataSerieSum * $YScale; - - /* Re-compute graph width depending of the text mode choosen */ - if ( $TextPos == TEXT_POS_RIGHT ) - { - $MaxWidth = 0; - foreach($Data["Series"][$LabelSerie]["Data"] as $Key => $Label) - { - $Boundardies = $Object->getTextBox(0,0,$Object->FontName,$Object->FontSize,0,$Label); - if ( $Boundardies[1]["X"] > $MaxWidth ) { $MaxWidth = $Boundardies[1]["X"] + $TextPadding*2; } - } - $X2 = $X2 - $MaxWidth; - } - - /* Drawing */ - $LeftY = ((($Y2-$Y1) / 2) + $Y1) - ($LeftHeight/2); - $RightY = $Y1; - $VectorX = (($X2-$X1) / 2); - - foreach($Data["Series"][$DataSerie]["Data"] as $Key => $Value) - { - if ( isset($Data["Series"][$LabelSerie]["Data"][$Key]) ) - $Label = $Data["Series"][$LabelSerie]["Data"][$Key]; - else - $Label = "-"; - - $LeftY1 = $LeftY; - $LeftY2 = $LeftY + $Value * $YScale; - - $RightY1 = $RightY + $Spacing; - $RightY2 = $RightY + $Spacing + $Value * $YScale;; - - $Settings = array("R"=>$Palette[$Key]["R"],"G"=>$Palette[$Key]["G"],"B"=>$Palette[$Key]["B"],"Alpha"=>$Palette[$Key]["Alpha"],"NoDraw"=>TRUE,"Segments"=>$Segments,"Surrounding"=>$Surrounding); - - $PolyGon = ""; - - $Angle = $Object->getAngle($X2,$RightY1,$X1,$LeftY1); - $VectorX1 = cos(deg2rad($Angle+90)) * $Force + ($X2-$X1)/2 + $X1; - $VectorY1 = sin(deg2rad($Angle+90)) * $Force + ($RightY1-$LeftY1)/2 + $LeftY1; - $VectorX2 = cos(deg2rad($Angle-90)) * $Force + ($X2-$X1)/2 + $X1; - $VectorY2 = sin(deg2rad($Angle-90)) * $Force + ($RightY1-$LeftY1)/2 + $LeftY1; - - $Points = $Object->drawBezier($X1,$LeftY1,$X2,$RightY1,$VectorX1,$VectorY1,$VectorX2,$VectorY2,$Settings); - foreach($Points as $Key => $Pos) { $PolyGon[] = $Pos["X"]; $PolyGon[] = $Pos["Y"]; } - - - $Angle = $Object->getAngle($X2,$RightY2,$X1,$LeftY2); - $VectorX1 = cos(deg2rad($Angle+90)) * $Force + ($X2-$X1)/2 +$X1; - $VectorY1 = sin(deg2rad($Angle+90)) * $Force + ($RightY2-$LeftY2)/2 + $LeftY2; - $VectorX2 = cos(deg2rad($Angle-90)) * $Force + ($X2-$X1)/2 +$X1; - $VectorY2 = sin(deg2rad($Angle-90)) * $Force + ($RightY2-$LeftY2)/2 + $LeftY2; - - $Points = $Object->drawBezier($X1,$LeftY2,$X2,$RightY2,$VectorX1,$VectorY1,$VectorX2,$VectorY2,$Settings); - $Points = array_reverse($Points); - foreach($Points as $Key => $Pos) { $PolyGon[] = $Pos["X"]; $PolyGon[] = $Pos["Y"]; } - - $Object->drawPolygon($PolyGon,$Settings); - - if ( $TextPos == TEXT_POS_RIGHT ) - $Object->drawText($X2+$TextPadding,($RightY2-$RightY1)/2+$RightY1,$Label,array("Align"=>TEXT_ALIGN_MIDDLELEFT)); - else - $Object->drawText($X2,$RightY1-$TextPadding,$Label,array("Align"=>TEXT_ALIGN_BOTTOMRIGHT)); - - $LeftY = $LeftY2; - $RightY = $RightY2; - } - } - } +pChartObject = $Object; + + $Spacing = isset($Format["Spacing"]) ? $Format["Spacing"] : 20; + $TextPadding = isset($Format["TextPadding"]) ? $Format["TextPadding"] : 2; + $TextPos = isset($Format["TextPos"]) ? $Format["TextPos"] : TEXT_POS_TOP; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL; + $Force = isset($Format["Force"]) ? $Format["Force"] : 70; + $Segments = isset($Format["Segments"]) ? $Format["Segments"] : 15; + $FontSize = $Object->FontSize; + $X1 = $Object->GraphAreaX1; + $Y1 = $Object->GraphAreaY1; + $X2 = $Object->GraphAreaX2; + $Y2 = $Object->GraphAreaY2; + + /* Data Processing */ + $Data = $Values->getData(); + $Palette = $Values->getPalette(); + + $LabelSerie = $Data["Abscissa"]; + $DataSerie = ""; + + foreach($Data["Series"] as $SerieName => $Value) + { if ( $SerieName != $LabelSerie && $DataSerie == "" ) { $DataSerie = $SerieName; } } + + $DataSerieSum = array_sum($Data["Series"][$DataSerie]["Data"]); + $DataSerieCount = count($Data["Series"][$DataSerie]["Data"]); + + /* Scale Processing */ + if ( $TextPos == TEXT_POS_RIGHT ) + $YScale = (($Y2-$Y1) - (($DataSerieCount+1)*$Spacing)) / $DataSerieSum; + else + $YScale = (($Y2-$Y1) - ($DataSerieCount*$Spacing)) / $DataSerieSum; + $LeftHeight = $DataSerieSum * $YScale; + + /* Re-compute graph width depending of the text mode choosen */ + if ( $TextPos == TEXT_POS_RIGHT ) + { + $MaxWidth = 0; + foreach($Data["Series"][$LabelSerie]["Data"] as $Key => $Label) + { + $Boundardies = $Object->getTextBox(0,0,$Object->FontName,$Object->FontSize,0,$Label); + if ( $Boundardies[1]["X"] > $MaxWidth ) { $MaxWidth = $Boundardies[1]["X"] + $TextPadding*2; } + } + $X2 = $X2 - $MaxWidth; + } + + /* Drawing */ + $LeftY = ((($Y2-$Y1) / 2) + $Y1) - ($LeftHeight/2); + $RightY = $Y1; + $VectorX = (($X2-$X1) / 2); + + foreach($Data["Series"][$DataSerie]["Data"] as $Key => $Value) + { + if ( isset($Data["Series"][$LabelSerie]["Data"][$Key]) ) + $Label = $Data["Series"][$LabelSerie]["Data"][$Key]; + else + $Label = "-"; + + $LeftY1 = $LeftY; + $LeftY2 = $LeftY + $Value * $YScale; + + $RightY1 = $RightY + $Spacing; + $RightY2 = $RightY + $Spacing + $Value * $YScale;; + + $Settings = array("R"=>$Palette[$Key]["R"],"G"=>$Palette[$Key]["G"],"B"=>$Palette[$Key]["B"],"Alpha"=>$Palette[$Key]["Alpha"],"NoDraw"=>TRUE,"Segments"=>$Segments,"Surrounding"=>$Surrounding); + + $PolyGon = ""; + + $Angle = $Object->getAngle($X2,$RightY1,$X1,$LeftY1); + $VectorX1 = cos(deg2rad($Angle+90)) * $Force + ($X2-$X1)/2 + $X1; + $VectorY1 = sin(deg2rad($Angle+90)) * $Force + ($RightY1-$LeftY1)/2 + $LeftY1; + $VectorX2 = cos(deg2rad($Angle-90)) * $Force + ($X2-$X1)/2 + $X1; + $VectorY2 = sin(deg2rad($Angle-90)) * $Force + ($RightY1-$LeftY1)/2 + $LeftY1; + + $Points = $Object->drawBezier($X1,$LeftY1,$X2,$RightY1,$VectorX1,$VectorY1,$VectorX2,$VectorY2,$Settings); + foreach($Points as $Key => $Pos) { $PolyGon[] = $Pos["X"]; $PolyGon[] = $Pos["Y"]; } + + + $Angle = $Object->getAngle($X2,$RightY2,$X1,$LeftY2); + $VectorX1 = cos(deg2rad($Angle+90)) * $Force + ($X2-$X1)/2 +$X1; + $VectorY1 = sin(deg2rad($Angle+90)) * $Force + ($RightY2-$LeftY2)/2 + $LeftY2; + $VectorX2 = cos(deg2rad($Angle-90)) * $Force + ($X2-$X1)/2 +$X1; + $VectorY2 = sin(deg2rad($Angle-90)) * $Force + ($RightY2-$LeftY2)/2 + $LeftY2; + + $Points = $Object->drawBezier($X1,$LeftY2,$X2,$RightY2,$VectorX1,$VectorY1,$VectorX2,$VectorY2,$Settings); + $Points = array_reverse($Points); + foreach($Points as $Key => $Pos) { $PolyGon[] = $Pos["X"]; $PolyGon[] = $Pos["Y"]; } + + $Object->drawPolygon($PolyGon,$Settings); + + if ( $TextPos == TEXT_POS_RIGHT ) + $Object->drawText($X2+$TextPadding,($RightY2-$RightY1)/2+$RightY1,$Label,array("Align"=>TEXT_ALIGN_MIDDLELEFT)); + else + $Object->drawText($X2,$RightY1-$TextPadding,$Label,array("Align"=>TEXT_ALIGN_BOTTOMRIGHT)); + + $LeftY = $LeftY2; + $RightY = $RightY2; + } + } + } ?> \ No newline at end of file diff --git a/www/libs/pChart2.1.3/class/pSpring.class.php b/www/libs/pChart2.1.4/class/pSpring.class.php similarity index 97% rename from www/libs/pChart2.1.3/class/pSpring.class.php rename to www/libs/pChart2.1.4/class/pSpring.class.php index 30ea1a66..65b9cb61 100644 --- a/www/libs/pChart2.1.3/class/pSpring.class.php +++ b/www/libs/pChart2.1.4/class/pSpring.class.php @@ -1,868 +1,868 @@ -Data = ""; - $this->Links = ""; - - /* Set nodes defaults */ - $this->Default["R"] = 255; - $this->Default["G"] = 255; - $this->Default["B"] = 255; - $this->Default["Alpha"] = 100; - $this->Default["BorderR"] = 0; - $this->Default["BorderG"] = 0; - $this->Default["BorderB"] = 0; - $this->Default["BorderAlpha"] = 100; - $this->Default["Surrounding"] = NULL; - $this->Default["BackgroundR"] = 255; - $this->Default["BackgroundG"] = 255; - $this->Default["BackgroundB"] = 255; - $this->Default["BackgroundAlpha"] = 0; - $this->Default["Force"] = 1; - $this->Default["NodeType"] = NODE_TYPE_FREE; - $this->Default["Size"] = 5; - $this->Default["Shape"] = NODE_SHAPE_CIRCLE; - $this->Default["FreeZone"] = 40; - $this->Default["LinkR"] = 0; - $this->Default["LinkG"] = 0; - $this->Default["LinkB"] = 0; - $this->Default["LinkAlpha"] = 0; - - $this->Labels["Type"] = LABEL_CLASSIC; - $this->Labels["R"] = 0; - $this->Labels["G"] = 0; - $this->Labels["B"] = 0; - $this->Labels["Alpha"] = 100; - - $this->AutoComputeFreeZone = FALSE; - } - - /* Set default links options */ - function setLinkDefaults($Settings="") - { - if ( isset($Settings["R"]) ) { $this->Default["LinkR"] = $Settings["R"]; } - if ( isset($Settings["G"]) ) { $this->Default["LinkG"] = $Settings["G"]; } - if ( isset($Settings["B"]) ) { $this->Default["LinkB"] = $Settings["B"]; } - if ( isset($Settings["Alpha"]) ) { $this->Default["LinkAlpha"] = $Settings["Alpha"]; } - } - - /* Set default links options */ - function setLabelsSettings($Settings="") - { - if ( isset($Settings["Type"]) ) { $this->Labels["Type"] = $Settings["Type"]; } - if ( isset($Settings["R"]) ) { $this->Labels["R"] = $Settings["R"]; } - if ( isset($Settings["G"]) ) { $this->Labels["G"] = $Settings["G"]; } - if ( isset($Settings["B"]) ) { $this->Labels["B"] = $Settings["B"]; } - if ( isset($Settings["Alpha"]) ) { $this->Labels["Alpha"] = $Settings["Alpha"]; } - } - - /* Auto compute the FreeZone size based on the number of connections */ - function autoFreeZone() - { - /* Check connections reciprocity */ - foreach($this->Data as $Key => $Settings) - { - if ( isset($Settings["Connections"]) ) - { $this->Data[$Key]["FreeZone"] = count($Settings["Connections"])*10 + 20; } - else - { $this->Data[$Key]["FreeZone"] = 20; } - } - - } - - /* Set link properties */ - function linkProperties($FromNode,$ToNode,$Settings) - { - if ( !isset($this->Data[$FromNode]) ) { return(0); } - if ( !isset($this->Data[$ToNode]) ) { return(0); } - - $R = isset($Settings["R"]) ? $Settings["R"] : 0; - $G = isset($Settings["G"]) ? $Settings["G"] : 0; - $B = isset($Settings["B"]) ? $Settings["B"] : 0; - $Alpha = isset($Settings["Alpha"]) ? $Settings["Alpha"] : 100; - $Name = isset($Settings["Name"]) ? $Settings["Name"] : NULL; - $Ticks = isset($Settings["Ticks"]) ? $Settings["Ticks"] : NULL; - - $this->Links[$FromNode][$ToNode]["R"] = $R; $this->Links[$ToNode][$FromNode]["R"] = $R; - $this->Links[$FromNode][$ToNode]["G"] = $G; $this->Links[$ToNode][$FromNode]["G"] = $G; - $this->Links[$FromNode][$ToNode]["B"] = $B; $this->Links[$ToNode][$FromNode]["B"] = $B; - $this->Links[$FromNode][$ToNode]["Alpha"] = $Alpha; $this->Links[$ToNode][$FromNode]["Alpha"] = $Alpha; - $this->Links[$FromNode][$ToNode]["Name"] = $Name; $this->Links[$ToNode][$FromNode]["Name"] = $Name; - $this->Links[$FromNode][$ToNode]["Ticks"] = $Ticks; $this->Links[$ToNode][$FromNode]["Ticks"] = $Ticks; - } - - function setNodeDefaults($Settings="") - { - if ( isset($Settings["R"]) ) { $this->Default["R"] = $Settings["R"]; } - if ( isset($Settings["G"]) ) { $this->Default["G"] = $Settings["G"]; } - if ( isset($Settings["B"]) ) { $this->Default["B"] = $Settings["B"]; } - if ( isset($Settings["Alpha"]) ) { $this->Default["Alpha"] = $Settings["Alpha"]; } - if ( isset($Settings["BorderR"]) ) { $this->Default["BorderR"] = $Settings["BorderR"]; } - if ( isset($Settings["BorderG"]) ) { $this->Default["BorderG"] = $Settings["BorderG"]; } - if ( isset($Settings["BorderB"]) ) { $this->Default["BorderB"] = $Settings["BorderB"]; } - if ( isset($Settings["BorderAlpha"]) ) { $this->Default["BorderAlpha"] = $Settings["BorderAlpha"]; } - if ( isset($Settings["Surrounding"]) ) { $this->Default["Surrounding"] = $Settings["Surrounding"]; } - if ( isset($Settings["BackgroundR"]) ) { $this->Default["BackgroundR"] = $Settings["BackgroundR"]; } - if ( isset($Settings["BackgroundG"]) ) { $this->Default["BackgroundG"] = $Settings["BackgroundG"]; } - if ( isset($Settings["BackgroundB"]) ) { $this->Default["BackgroundB"] = $Settings["BackgroundB"]; } - if ( isset($Settings["BackgroundAlpha"]) ) { $this->Default["BackgroundAlpha"] = $Settings["BackgroundAlpha"]; } - if ( isset($Settings["NodeType"]) ) { $this->Default["NodeType"] = $Settings["NodeType"]; } - if ( isset($Settings["Size"]) ) { $this->Default["Size"] = $Settings["Size"]; } - if ( isset($Settings["Shape"]) ) { $this->Default["Shape"] = $Settings["Shape"]; } - if ( isset($Settings["FreeZone"]) ) { $this->Default["FreeZone"] = $Settings["FreeZone"]; } - } - - /* Add a node */ - function addNode($NodeID,$Settings="") - { - /* if the node already exists, ignore */ - if (isset($this->Data[$NodeID])) { return(0); } - - $Name = isset($Settings["Name"]) ? $Settings["Name"] : "Node ".$NodeID; - $Connections = isset($Settings["Connections"]) ? $Settings["Connections"] : NULL; - - $R = isset($Settings["R"]) ? $Settings["R"] : $this->Default["R"]; - $G = isset($Settings["G"]) ? $Settings["G"] : $this->Default["G"]; - $B = isset($Settings["B"]) ? $Settings["B"] : $this->Default["B"]; - $Alpha = isset($Settings["Alpha"]) ? $Settings["Alpha"] : $this->Default["Alpha"]; - $BorderR = isset($Settings["BorderR"]) ? $Settings["BorderR"] : $this->Default["BorderR"]; - $BorderG = isset($Settings["BorderG"]) ? $Settings["BorderG"] : $this->Default["BorderG"]; - $BorderB = isset($Settings["BorderB"]) ? $Settings["BorderB"] : $this->Default["BorderB"]; - $BorderAlpha = isset($Settings["BorderAlpha"]) ? $Settings["BorderAlpha"] : $this->Default["BorderAlpha"]; - $Surrounding = isset($Settings["Surrounding"]) ? $Settings["Surrounding"] : $this->Default["Surrounding"]; - $BackgroundR = isset($Settings["BackgroundR"]) ? $Settings["BackgroundR"] : $this->Default["BackgroundR"]; - $BackgroundG = isset($Settings["BackgroundG"]) ? $Settings["BackgroundG"] : $this->Default["BackgroundG"]; - $BackgroundB = isset($Settings["BackgroundB"]) ? $Settings["BackgroundB"] : $this->Default["BackgroundB"]; - $BackgroundAlpha = isset($Settings["BackgroundAlpha"]) ? $Settings["BackgroundAlpha"] : $this->Default["BackgroundAlpha"]; - $Force = isset($Settings["Force"]) ? $Settings["Force"] : $this->Default["Force"]; - $NodeType = isset($Settings["NodeType"]) ? $Settings["NodeType"] : $this->Default["NodeType"]; - $Size = isset($Settings["Size"]) ? $Settings["Size"] : $this->Default["Size"]; - $Shape = isset($Settings["Shape"]) ? $Settings["Shape"] : $this->Default["Shape"]; - $FreeZone = isset($Settings["FreeZone"]) ? $Settings["FreeZone"] : $this->Default["FreeZone"]; - - if ( $Surrounding != NULL ) { $BorderR = $R + $Surrounding; $BorderG = $G + $Surrounding; $BorderB = $B + $Surrounding; } - - $this->Data[$NodeID]["R"] = $R; $this->Data[$NodeID]["G"] = $G; $this->Data[$NodeID]["B"] = $B; $this->Data[$NodeID]["Alpha"] = $Alpha; - $this->Data[$NodeID]["BorderR"] = $BorderR; $this->Data[$NodeID]["BorderG"] = $BorderG; $this->Data[$NodeID]["BorderB"] = $BorderB; $this->Data[$NodeID]["BorderAlpha"] = $BorderAlpha; - $this->Data[$NodeID]["BackgroundR"] = $BackgroundR; $this->Data[$NodeID]["BackgroundG"] = $BackgroundG; $this->Data[$NodeID]["BackgroundB"] = $BackgroundB; $this->Data[$NodeID]["BackgroundAlpha"] = $BackgroundAlpha; - $this->Data[$NodeID]["Name"] = $Name; - $this->Data[$NodeID]["Force"] = $Force; - $this->Data[$NodeID]["Type"] = $NodeType; - $this->Data[$NodeID]["Size"] = $Size; - $this->Data[$NodeID]["Shape"] = $Shape; - $this->Data[$NodeID]["FreeZone"] = $FreeZone; - if ( $Connections != NULL ) - { - if ( is_array($Connections ) ) - { - foreach($Connections as $Key => $Value) - $this->Data[$NodeID]["Connections"][] = $Value; - } - else - $this->Data[$NodeID]["Connections"][] = $Connections; - } - } - - /* Set color attribute for a list of nodes */ - function setNodesColor($Nodes,$Settings="") - { - if ( is_array($Nodes) ) - { - foreach ($Nodes as $Key => $NodeID) - { - if (isset($this->Data[$NodeID]) ) - { - if ( isset($Settings["R"]) ) { $this->Data[$NodeID]["R"] = $Settings["R"]; } - if ( isset($Settings["G"]) ) { $this->Data[$NodeID]["G"] = $Settings["G"]; } - if ( isset($Settings["B"]) ) { $this->Data[$NodeID]["B"] = $Settings["B"]; } - if ( isset($Settings["Alpha"]) ) { $this->Data[$NodeID]["Alpha"] = $Settings["Alpha"]; } - if ( isset($Settings["BorderR"]) ) { $this->Data[$NodeID]["BorderR"] = $Settings["BorderR"]; } - if ( isset($Settings["BorderG"]) ) { $this->Data[$NodeID]["BorderG"] = $Settings["BorderG"]; } - if ( isset($Settings["BorderB"]) ) { $this->Data[$NodeID]["BorderB"] = $Settings["BorderB"]; } - if ( isset($Settings["BorderAlpha"]) ) { $this->Data[$NodeID]["BorderAlpha"] = $Settings["BorderAlpha"]; } - if ( isset($Settings["Surrounding"]) ) { $this->Data[$NodeID]["BorderR"] = $this->Data[$NodeID]["R"] + $Settings["Surrounding"]; $this->Data[$NodeID]["BorderG"] = $this->Data[$NodeID]["G"] + $Settings["Surrounding"]; $this->Data[$NodeID]["BorderB"] = $this->Data[$NodeID]["B"] + $Settings["Surrounding"]; } - } - } - } - else - { - if ( isset($Settings["R"]) ) { $this->Data[$Nodes]["R"] = $Settings["R"]; } - if ( isset($Settings["G"]) ) { $this->Data[$Nodes]["G"] = $Settings["G"]; } - if ( isset($Settings["B"]) ) { $this->Data[$Nodes]["B"] = $Settings["B"]; } - if ( isset($Settings["Alpha"]) ) { $this->Data[$Nodes]["Alpha"] = $Settings["Alpha"]; } - if ( isset($Settings["BorderR"]) ) { $this->Data[$Nodes]["BorderR"] = $Settings["BorderR"]; } - if ( isset($Settings["BorderG"]) ) { $this->Data[$Nodes]["BorderG"] = $Settings["BorderG"]; } - if ( isset($Settings["BorderB"]) ) { $this->Data[$Nodes]["BorderB"] = $Settings["BorderB"]; } - if ( isset($Settings["BorderAlpha"]) ) { $this->Data[$Nodes]["BorderAlpha"] = $Settings["BorderAlpha"]; } - if ( isset($Settings["Surrounding"]) ) { $this->Data[$Nodes]["BorderR"] = $this->Data[$NodeID]["R"] + $Settings["Surrounding"]; $this->Data[$NodeID]["BorderG"] = $this->Data[$NodeID]["G"] + $Settings["Surrounding"]; $this->Data[$NodeID]["BorderB"] = $this->Data[$NodeID]["B"] + $Settings["Surrounding"]; } - } - } - - /* Returns all the nodes details */ - function dumpNodes() - { return($this->Data); } - - /* Check if a connection exists and create it if required */ - function checkConnection($SourceID, $TargetID) - { - if ( isset($this->Data[$SourceID]["Connections"]) ) - { - foreach ($this->Data[$SourceID]["Connections"] as $Key => $ConnectionID) - { if ( $TargetID == $ConnectionID ) { return(TRUE); } } - } - $this->Data[$SourceID]["Connections"][] = $TargetID; - } - /* Get the median linked nodes position */ - function getMedianOffset($Key,$X,$Y) - { - $Cpt = 1; - if ( isset($this->Data[$Key]["Connections"]) ) - { - foreach($this->Data[$Key]["Connections"] as $ID => $NodeID) - { - if ( isset($this->Data[$NodeID]["X"]) && isset($this->Data[$NodeID]["Y"]) ) - { - $X = $X + $this->Data[$NodeID]["X"]; - $Y = $Y + $this->Data[$NodeID]["Y"]; - $Cpt++; - } - } - } - return(array("X"=>$X/$Cpt,"Y"=>$Y/$Cpt)); - } - - /* Return the ID of the attached partner with the biggest weight */ - function getBiggestPartner($Key) - { - if ( !isset($this->Data[$Key]["Connections"]) ) { return(""); } - - $MaxWeight = 0; $Result = ""; - foreach($this->Data[$Key]["Connections"] as $Key => $PeerID) - { - if ( $this->Data[$PeerID]["Weight"] > $MaxWeight ) - { $MaxWeight = $this->Data[$PeerID]["Weight"]; $Result = $PeerID; } - } - return($Result); - } - - /* Do the initial node positions computing pass */ - function firstPass($Algorithm) - { - $CenterX = ($this->X2 - $this->X1) / 2 + $this->X1; - $CenterY = ($this->Y2 - $this->Y1) / 2 + $this->Y1; - - /* Check connections reciprocity */ - foreach($this->Data as $Key => $Settings) - { - if ( isset($Settings["Connections"]) ) - { - foreach($Settings["Connections"] as $ID => $ConnectionID) - $this->checkConnection($ConnectionID,$Key); - } - } - - if ( $this->AutoComputeFreeZone ) { $this->autoFreeZone(); } - - /* Get the max number of connections */ - $MaxConnections = 0; - foreach($this->Data as $Key => $Settings) - { if ( isset($Settings["Connections"]) ) { if ( $MaxConnections < count($Settings["Connections"] ) ) { $MaxConnections = count($Settings["Connections"]); } } } - - if ( $Algorithm == ALGORITHM_WEIGHTED ) - { - foreach($this->Data as $Key => $Settings) - { - if ( $Settings["Type"] == NODE_TYPE_CENTRAL ) { $this->Data[$Key]["X"] = $CenterX; $this->Data[$Key]["Y"] = $CenterY; } - if ( $Settings["Type"] == NODE_TYPE_FREE ) - { - if ( isset($Settings["Connections"]) ) - { $Connections = count($Settings["Connections"]); } - else - { $Connections = 0; } - - $Ring = $MaxConnections - $Connections; - $Angle = rand(0,360); - - $this->Data[$Key]["X"] = cos(deg2rad($Angle)) * ($Ring*$this->RingSize) + $CenterX; - $this->Data[$Key]["Y"] = sin(deg2rad($Angle)) * ($Ring*$this->RingSize) + $CenterY; - } - } - } - elseif ( $Algorithm == ALGORITHM_CENTRAL ) - { - /* Put a weight on each nodes */ - foreach($this->Data as $Key => $Settings) - { - if ( isset($Settings["Connections"]) ) - $this->Data[$Key]["Weight"] = count($Settings["Connections"]); - else - $this->Data[$Key]["Weight"] = 0; - } - - $MaxConnections = $MaxConnections + 1; - for($i=$MaxConnections;$i>=0;$i--) - { - foreach($this->Data as $Key => $Settings) - { - if ( $Settings["Type"] == NODE_TYPE_CENTRAL ) { $this->Data[$Key]["X"] = $CenterX; $this->Data[$Key]["Y"] = $CenterY; } - if ( $Settings["Type"] == NODE_TYPE_FREE ) - { - if ( isset($Settings["Connections"]) ) - { $Connections = count($Settings["Connections"]); } - else - { $Connections = 0; } - - if ( $Connections == $i ) - { - $BiggestPartner = $this->getBiggestPartner($Key); - if ( $BiggestPartner != "" ) - { - $Ring = $this->Data[$BiggestPartner]["FreeZone"]; - $Weight = $this->Data[$BiggestPartner]["Weight"]; - $AngleDivision = 360 / $this->Data[$BiggestPartner]["Weight"]; - $Done = FALSE; $Tries = 0; - while (!$Done && $Tries <= $Weight*2) - { - $Tries++; - $Angle = floor(rand(0,$Weight)*$AngleDivision); - if ( !isset($this->Data[$BiggestPartner]["Angular"][$Angle]) || !isset($this->Data[$BiggestPartner]["Angular"]) ) - { - $this->Data[$BiggestPartner]["Angular"][$Angle] = $Angle; - $Done = TRUE; - } - } - if ( !$Done ) - { $Angle = rand(0,360); $this->Data[$BiggestPartner]["Angular"][$Angle] = $Angle; } - - $X = cos(deg2rad($Angle)) * ($Ring) + $this->Data[$BiggestPartner]["X"]; - $Y = sin(deg2rad($Angle)) * ($Ring) + $this->Data[$BiggestPartner]["Y"]; - - $this->Data[$Key]["X"] = $X; - $this->Data[$Key]["Y"] = $Y; - } - } - } - } - } - } - elseif ( $Algorithm == ALGORITHM_CIRCULAR ) - { - $MaxConnections = $MaxConnections + 1; - for($i=$MaxConnections;$i>=0;$i--) - { - foreach($this->Data as $Key => $Settings) - { - if ( $Settings["Type"] == NODE_TYPE_CENTRAL ) { $this->Data[$Key]["X"] = $CenterX; $this->Data[$Key]["Y"] = $CenterY; } - if ( $Settings["Type"] == NODE_TYPE_FREE ) - { - if ( isset($Settings["Connections"]) ) - { $Connections = count($Settings["Connections"]); } - else - { $Connections = 0; } - - if ( $Connections == $i ) - { - $Ring = $MaxConnections - $Connections; - $Angle = rand(0,360); - - $X = cos(deg2rad($Angle)) * ($Ring*$this->RingSize) + $CenterX; - $Y = sin(deg2rad($Angle)) * ($Ring*$this->RingSize) + $CenterY; - - $MedianOffset = $this->getMedianOffset($Key,$X,$Y); - - $this->Data[$Key]["X"] = $MedianOffset["X"]; - $this->Data[$Key]["Y"] = $MedianOffset["Y"]; - } - } - } - } - } - elseif ( $Algorithm == ALGORITHM_RANDOM ) - { - foreach($this->Data as $Key => $Settings) - { - if ( $Settings["Type"] == NODE_TYPE_FREE ) - { - $this->Data[$Key]["X"] = $CenterX + rand(-20,20); - $this->Data[$Key]["Y"] = $CenterY + rand(-20,20); - } - if ( $Settings["Type"] == NODE_TYPE_CENTRAL ) { $this->Data[$Key]["X"] = $CenterX; $this->Data[$Key]["Y"] = $CenterY; } - } - } - } - - /* Compute one pass */ - function doPass() - { - /* Compute vectors */ - foreach($this->Data as $Key => $Settings) - { - if ( $Settings["Type"] != NODE_TYPE_CENTRAL ) - { - unset($this->Data[$Key]["Vectors"]); - - $X1 = $Settings["X"]; - $Y1 = $Settings["Y"]; - - /* Repulsion vectors */ - foreach($this->Data as $Key2 => $Settings2) - { - if ( $Key != $Key2 ) - { - $X2 = $this->Data[$Key2]["X"]; - $Y2 = $this->Data[$Key2]["Y"]; - $FreeZone = $this->Data[$Key2]["FreeZone"]; - - $Distance = $this->getDistance($X1,$Y1,$X2,$Y2); - $Angle = $this->getAngle($X1,$Y1,$X2,$Y2) + 180; - - /* Nodes too close, repulsion occurs */ - if ( $Distance < $FreeZone ) - { - $Force = log(pow(2,$FreeZone-$Distance)); - if ( $Force > 1 ) - { $this->Data[$Key]["Vectors"][] = array("Type"=>"R","Angle"=>$Angle % 360,"Force"=>$Force); } - } - } - } - - /* Attraction vectors */ - if ( isset($Settings["Connections"]) ) - { - foreach($Settings["Connections"] as $ID => $NodeID) - { - if ( isset($this->Data[$NodeID]) ) - { - $X2 = $this->Data[$NodeID]["X"]; - $Y2 = $this->Data[$NodeID]["Y"]; - $FreeZone = $this->Data[$Key2]["FreeZone"]; - - $Distance = $this->getDistance($X1,$Y1,$X2,$Y2); - $Angle = $this->getAngle($X1,$Y1,$X2,$Y2); - - if ( $Distance > $FreeZone ) - $Force = log(($Distance-$FreeZone)+1); - else - { $Force = log(($FreeZone-$Distance)+1); ($Angle = $Angle + 180); } - - if ( $Force > 1 ) - $this->Data[$Key]["Vectors"][] = array("Type"=>"A","Angle"=>$Angle % 360,"Force"=>$Force); - } - } - } - } - } - - /* Move the nodes accoding to the vectors */ - foreach($this->Data as $Key => $Settings) - { - $X = $Settings["X"]; - $Y = $Settings["Y"]; - - if ( isset($Settings["Vectors"]) && $Settings["Type"] != NODE_TYPE_CENTRAL ) - { - foreach($Settings["Vectors"] as $ID => $Vector) - { - $Type = $Vector["Type"]; - $Force = $Vector["Force"]; - $Angle = $Vector["Angle"]; - $Factor = $Type == "A" ? $this->MagneticForceA : $this->MagneticForceR; - - $X = cos(deg2rad($Angle)) * $Force * $Factor + $X; - $Y = sin(deg2rad($Angle)) * $Force * $Factor + $Y; - } - } - - $this->Data[$Key]["X"] = $X; - $this->Data[$Key]["Y"] = $Y; - } - } - - function lastPass() - { - /* Put everything inside the graph area */ - foreach($this->Data as $Key => $Settings) - { - $X = $Settings["X"]; - $Y = $Settings["Y"]; - - if ( $X < $this->X1 ) { $X = $this->X1; } - if ( $X > $this->X2 ) { $X = $this->X2; } - if ( $Y < $this->Y1 ) { $Y = $this->Y1; } - if ( $Y > $this->Y2 ) { $Y = $this->Y2; } - - $this->Data[$Key]["X"] = $X; - $this->Data[$Key]["Y"] = $Y; - } - - /* Dump all links */ - $Links = ""; - foreach($this->Data as $Key => $Settings) - { - $X1 = $Settings["X"]; - $Y1 = $Settings["Y"]; - - if ( isset($Settings["Connections"]) ) - { - foreach ($Settings["Connections"] as $ID => $NodeID) - { - if ( isset($this->Data[$NodeID]) ) - { - $X2 = $this->Data[$NodeID]["X"]; - $Y2 = $this->Data[$NodeID]["Y"]; - - $Links[] = array("X1"=>$X1,"Y1"=>$Y1,"X2"=>$X2,"Y2"=>$Y2,"Source"=>$Settings["Name"],"Destination"=>$this->Data[$NodeID]["Name"]); - } - } - } - } - - /* Check collisions */ - $Conflicts = 0; - foreach($this->Data as $Key => $Settings) - { - $X1 = $Settings["X"]; - $Y1 = $Settings["Y"]; - - if ( isset($Settings["Connections"]) ) - { - foreach ($Settings["Connections"] as $ID => $NodeID) - { - if ( isset($this->Data[$NodeID]) ) - { - $X2 = $this->Data[$NodeID]["X"]; - $Y2 = $this->Data[$NodeID]["Y"]; - - foreach($Links as $IDLinks => $Link) - { - $X3 = $Link["X1"]; $Y3 = $Link["Y1"]; $X4 = $Link["X2"]; $Y4 = $Link["Y2"]; - - if ( !($X1 == $X3 && $X2 == $X4 && $Y1 == $Y3 && $Y2 == $Y4 ) ) - { - if ( $this->intersect($X1,$Y1,$X2,$Y2,$X3,$Y3,$X4,$Y4) ) - { - if ( $Link["Source"] != $Settings["Name"] && $Link["Source"] != $this->Data[$NodeID]["Name"] && $Link["Destination"] != $Settings["Name"] && $Link["Destination"] != $this->Data[$NodeID]["Name"] ) - { $Conflicts++; } - } - } - } - } - } - } - } - return($Conflicts/2); - } - - /* Center the graph */ - function center() - { - /* Determine the real center */ - $TargetCenterX = ($this->X2 - $this->X1) / 2 + $this->X1; - $TargetCenterY = ($this->Y2 - $this->Y1) / 2 + $this->Y1; - - /* Get current boundaries */ - $XMin = $this->X2; $XMax = $this->X1; - $YMin = $this->Y2; $YMax = $this->Y1; - foreach($this->Data as $Key => $Settings) - { - $X = $Settings["X"]; - $Y = $Settings["Y"]; - - if ( $X < $XMin) { $XMin = $X; } - if ( $X > $XMax) { $XMax = $X; } - if ( $Y < $YMin) { $YMin = $Y; } - if ( $Y > $YMax) { $YMax = $Y; } - } - $CurrentCenterX = ($XMax - $XMin) / 2 + $XMin; - $CurrentCenterY = ($YMax - $YMin) / 2 + $YMin; - - /* Compute the offset to apply */ - $XOffset = $TargetCenterX - $CurrentCenterX; - $YOffset = $TargetCenterY - $CurrentCenterY; - - /* Correct the points position */ - foreach($this->Data as $Key => $Settings) - { - $this->Data[$Key]["X"] = $Settings["X"] + $XOffset; - $this->Data[$Key]["Y"] = $Settings["Y"] + $YOffset; - } - } - - /* Create the encoded string */ - function drawSpring($Object,$Settings="") - { - $this->pChartObject = $Object; - - $Pass = isset($Settings["Pass"]) ? $Settings["Pass"] : 50; - $Retries = isset($Settings["Retry"]) ? $Settings["Retry"] : 10; - $this->MagneticForceA = isset($Settings["MagneticForceA"]) ? $Settings["MagneticForceA"] : 1.5; - $this->MagneticForceR = isset($Settings["MagneticForceR"]) ? $Settings["MagneticForceR"] : 2; - $this->RingSize = isset($Settings["RingSize"]) ? $Settings["RingSize"] : 40; - $DrawVectors = isset($Settings["DrawVectors"]) ? $Settings["DrawVectors"] : FALSE; - $DrawQuietZone = isset($Settings["DrawQuietZone"]) ? $Settings["DrawQuietZone"] : FALSE; - $CenterGraph = isset($Settings["CenterGraph"]) ? $Settings["CenterGraph"] : TRUE; - $TextPadding = isset($Settings["TextPadding"]) ? $Settings["TextPadding"] : 4; - $Algorithm = isset($Settings["Algorithm"]) ? $Settings["Algorithm"] : ALGORITHM_WEIGHTED; - - $FontSize = $Object->FontSize; - $this->X1 = $Object->GraphAreaX1; - $this->Y1 = $Object->GraphAreaY1; - $this->X2 = $Object->GraphAreaX2; - $this->Y2 = $Object->GraphAreaY2; - - $Conflicts = 1; $Jobs = 0; $this->History["MinimumConflicts"] = -1; - while ($Conflicts != 0 && $Jobs < $Retries ) - { - $Jobs++; - - /* Compute the initial settings */ - $this->firstPass($Algorithm); - - /* Apply the vectors */ - if ( $Pass > 0 ) - { - for ($i=0; $i<=$Pass; $i++) { $this->doPass(); } - } - - $Conflicts = $this->lastPass(); - if ( $this->History["MinimumConflicts"] == -1 || $Conflicts < $this->History["MinimumConflicts"] ) - { $this->History["MinimumConflicts"] = $Conflicts; $this->History["Result"] = $this->Data; } - } - - $Conflicts = $this->History["MinimumConflicts"]; - $this->Data = $this->History["Result"]; - - if ( $CenterGraph ) { $this->center(); } - - /* Draw the connections */ - $Drawn = ""; - foreach($this->Data as $Key => $Settings) - { - $X = $Settings["X"]; - $Y = $Settings["Y"]; - - if ( isset($Settings["Connections"]) ) - { - foreach ($Settings["Connections"] as $ID => $NodeID) - { - if ( !isset($Drawn[$Key]) ) { $Drawn[$Key] = ""; } - if ( !isset($Drawn[$NodeID]) ) { $Drawn[$NodeID] = ""; } - - if ( isset($this->Data[$NodeID]) && !isset($Drawn[$Key][$NodeID]) && !isset($Drawn[$NodeID][$Key]) ) - { - $Color = array("R"=>$this->Default["LinkR"],"G"=>$this->Default["LinkG"],"B"=>$this->Default["LinkB"],"Alpha"=>$this->Default["Alpha"]); - - if ( $this->Links != "" ) - { - if ( isset($this->Links[$Key][$NodeID]["R"]) ) - { $Color = array("R"=>$this->Links[$Key][$NodeID]["R"],"G"=>$this->Links[$Key][$NodeID]["G"],"B"=>$this->Links[$Key][$NodeID]["B"],"Alpha"=>$this->Links[$Key][$NodeID]["Alpha"]); } - - if ( isset($this->Links[$Key][$NodeID]["Ticks"]) ) - { $Color["Ticks"] = $this->Links[$Key][$NodeID]["Ticks"]; } - } - - $X2 = $this->Data[$NodeID]["X"]; - $Y2 = $this->Data[$NodeID]["Y"]; - $this->pChartObject->drawLine($X,$Y,$X2,$Y2,$Color); - $Drawn[$Key][$NodeID] = TRUE; - - if ( isset($this->Links) && $this->Links != "" ) - { - if ( isset($this->Links[$Key][$NodeID]["Name"]) || isset($this->Links[$NodeID][$Key]["Name"]) ) - { - $Name = isset($this->Links[$Key][$NodeID]["Name"]) ? $this->Links[$Key][$NodeID]["Name"] : $this->Links[$NodeID][$Key]["Name"]; - $TxtX = ($X2 - $X)/2 + $X; - $TxtY = ($Y2 - $Y)/2 + $Y; - - if ( $X <= $X2 ) - $Angle = (360-$this->getAngle($X,$Y,$X2,$Y2)) % 360; - else - $Angle = (360-$this->getAngle($X2,$Y2,$X,$Y)) % 360; - - $Settings = $Color; - $Settings["Angle"] = $Angle; - $Settings["Align"] = TEXT_ALIGN_BOTTOMMIDDLE; - $this->pChartObject->drawText($TxtX,$TxtY,$Name,$Settings); - } - } - } - } - } - } - - /* Draw the quiet zones */ - if ( $DrawQuietZone ) - { - foreach($this->Data as $Key => $Settings) - { - $X = $Settings["X"]; - $Y = $Settings["Y"]; - $FreeZone = $Settings["FreeZone"]; - - $this->pChartObject->drawFilledCircle($X,$Y,$FreeZone,array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>2)); - } - } - - - /* Draw the nodes */ - foreach($this->Data as $Key => $Settings) - { - $X = $Settings["X"]; - $Y = $Settings["Y"]; - $Name = $Settings["Name"]; - $FreeZone = $Settings["FreeZone"]; - $Shape = $Settings["Shape"]; - $Size = $Settings["Size"]; - - $Color = array("R"=>$Settings["R"],"G"=>$Settings["G"],"B"=>$Settings["B"],"Alpha"=>$Settings["Alpha"],"BorderR"=>$Settings["BorderR"],"BorderG"=>$Settings["BorderG"],"BorderB"=>$Settings["BorderB"],"BorderApha"=>$Settings["BorderAlpha"]); - - if ( $Shape == NODE_SHAPE_CIRCLE ) - { - $this->pChartObject->drawFilledCircle($X,$Y,$Size,$Color); - } - elseif ( $Shape == NODE_SHAPE_TRIANGLE ) - { - $Points = ""; - $Points[] = cos(deg2rad(270)) * $Size + $X; $Points[] = sin(deg2rad(270)) * $Size + $Y; - $Points[] = cos(deg2rad(45)) * $Size + $X; $Points[] = sin(deg2rad(45)) * $Size + $Y; - $Points[] = cos(deg2rad(135)) * $Size + $X; $Points[] = sin(deg2rad(135)) * $Size + $Y; - $this->pChartObject->drawPolygon($Points,$Color); - } - elseif ( $Shape == NODE_SHAPE_SQUARE ) - { - $Offset = $Size/2; $Size = $Size / 2; - $this->pChartObject->drawFilledRectangle($X-$Offset,$Y-$Offset,$X+$Offset,$Y+$Offset,$Color); - } - - if ( $Name != "" ) - { - $LabelOptions = array("R"=>$this->Labels["R"],"G"=>$this->Labels["G"],"B"=>$this->Labels["B"],"Alpha"=>$this->Labels["Alpha"]); - - if ( $this->Labels["Type"] == LABEL_LIGHT ) - { - $LabelOptions["Align"] = TEXT_ALIGN_BOTTOMLEFT; - $this->pChartObject->drawText($X,$Y,$Name,$LabelOptions); - } - elseif ( $this->Labels["Type"] == LABEL_CLASSIC ) - { - $LabelOptions["Align"] = TEXT_ALIGN_TOPMIDDLE; - $LabelOptions["DrawBox"] = TRUE; - $LabelOptions["BoxAlpha"] = 50; - $LabelOptions["BorderOffset"] = 4; - $LabelOptions["RoundedRadius"] = 3; - $LabelOptions["BoxRounded"] = TRUE; - $LabelOptions["NoShadow"] = TRUE; - - $this->pChartObject->drawText($X,$Y+$Size+$TextPadding,$Name,$LabelOptions); - } - } - } - - /* Draw the vectors */ - if ( $DrawVectors ) - { - foreach($this->Data as $Key => $Settings) - { - $X1 = $Settings["X"]; - $Y1 = $Settings["Y"]; - - if ( isset($Settings["Vectors"]) && $Settings["Type"] != NODE_TYPE_CENTRAL ) - { - foreach($Settings["Vectors"] as $ID => $Vector) - { - $Type = $Vector["Type"]; - $Force = $Vector["Force"]; - $Angle = $Vector["Angle"]; - $Factor = $Type == "A" ? $this->MagneticForceA : $this->MagneticForceR; - $Color = $Type == "A" ? array("FillR"=>255,"FillG"=>0,"FillB"=>0) : array("FillR"=>0,"FillG"=>255,"FillB"=>0); - - $X2 = cos(deg2rad($Angle)) * $Force * $Factor + $X1; - $Y2 = sin(deg2rad($Angle)) * $Force * $Factor + $Y1; - - $this->pChartObject->drawArrow($X1,$Y1,$X2,$Y2,$Color); - } - } - } - } - - return(array("Pass"=>$Jobs,"Conflicts"=>$Conflicts)); - } - - /* Return the distance between two points */ - function getDistance($X1,$Y1,$X2,$Y2) - { return (sqrt(($X2-$X1)*($X2-$X1)+($Y2-$Y1)*($Y2-$Y1))); } - - /* Return the angle made by a line and the X axis */ - function getAngle($X1,$Y1,$X2,$Y2) - { - $Opposite = $Y2 - $Y1; $Adjacent = $X2 - $X1;$Angle = rad2deg(atan2($Opposite,$Adjacent)); - if ($Angle > 0) { return($Angle); } else { return(360-abs($Angle)); } - } - - function intersect($X1,$Y1,$X2,$Y2,$X3,$Y3,$X4,$Y4) - { - $A = (($X3 * $Y4 - $X4 * $Y3) * ($X1 - $X2) - ($X1 * $Y2 - $X2 * $Y1) * ($X3 - $X4)); - $B = (($Y1 - $Y2) * ($X3 - $X4) - ($Y3 - $Y4) * ($X1 - $X2)); - - if ( $B == 0 ) { return(FALSE); } - $Xi = $A / $B; - - $C = ($X1 - $X2); - if ( $C == 0 ) { return(FALSE); } - $Yi = $Xi * (($Y1 - $Y2)/$C) + (($X1 * $Y2 - $X2 * $Y1)/$C); - - if ( $Xi >= min($X1,$X2) && $Xi >= min($X3,$X4) && $Xi <= max($X1,$X2) && $Xi <= max($X3,$X4)) - { - if ( $Yi >= min($Y1,$Y2) && $Yi >= min($Y3,$Y4) && $Yi <= max($Y1,$Y2) && $Yi <= max($Y3,$Y4)) - { return(TRUE); } - } - - return(FALSE); - } - } +Data = ""; + $this->Links = ""; + + /* Set nodes defaults */ + $this->Default["R"] = 255; + $this->Default["G"] = 255; + $this->Default["B"] = 255; + $this->Default["Alpha"] = 100; + $this->Default["BorderR"] = 0; + $this->Default["BorderG"] = 0; + $this->Default["BorderB"] = 0; + $this->Default["BorderAlpha"] = 100; + $this->Default["Surrounding"] = NULL; + $this->Default["BackgroundR"] = 255; + $this->Default["BackgroundG"] = 255; + $this->Default["BackgroundB"] = 255; + $this->Default["BackgroundAlpha"] = 0; + $this->Default["Force"] = 1; + $this->Default["NodeType"] = NODE_TYPE_FREE; + $this->Default["Size"] = 5; + $this->Default["Shape"] = NODE_SHAPE_CIRCLE; + $this->Default["FreeZone"] = 40; + $this->Default["LinkR"] = 0; + $this->Default["LinkG"] = 0; + $this->Default["LinkB"] = 0; + $this->Default["LinkAlpha"] = 0; + + $this->Labels["Type"] = LABEL_CLASSIC; + $this->Labels["R"] = 0; + $this->Labels["G"] = 0; + $this->Labels["B"] = 0; + $this->Labels["Alpha"] = 100; + + $this->AutoComputeFreeZone = FALSE; + } + + /* Set default links options */ + function setLinkDefaults($Settings="") + { + if ( isset($Settings["R"]) ) { $this->Default["LinkR"] = $Settings["R"]; } + if ( isset($Settings["G"]) ) { $this->Default["LinkG"] = $Settings["G"]; } + if ( isset($Settings["B"]) ) { $this->Default["LinkB"] = $Settings["B"]; } + if ( isset($Settings["Alpha"]) ) { $this->Default["LinkAlpha"] = $Settings["Alpha"]; } + } + + /* Set default links options */ + function setLabelsSettings($Settings="") + { + if ( isset($Settings["Type"]) ) { $this->Labels["Type"] = $Settings["Type"]; } + if ( isset($Settings["R"]) ) { $this->Labels["R"] = $Settings["R"]; } + if ( isset($Settings["G"]) ) { $this->Labels["G"] = $Settings["G"]; } + if ( isset($Settings["B"]) ) { $this->Labels["B"] = $Settings["B"]; } + if ( isset($Settings["Alpha"]) ) { $this->Labels["Alpha"] = $Settings["Alpha"]; } + } + + /* Auto compute the FreeZone size based on the number of connections */ + function autoFreeZone() + { + /* Check connections reciprocity */ + foreach($this->Data as $Key => $Settings) + { + if ( isset($Settings["Connections"]) ) + { $this->Data[$Key]["FreeZone"] = count($Settings["Connections"])*10 + 20; } + else + { $this->Data[$Key]["FreeZone"] = 20; } + } + + } + + /* Set link properties */ + function linkProperties($FromNode,$ToNode,$Settings) + { + if ( !isset($this->Data[$FromNode]) ) { return(0); } + if ( !isset($this->Data[$ToNode]) ) { return(0); } + + $R = isset($Settings["R"]) ? $Settings["R"] : 0; + $G = isset($Settings["G"]) ? $Settings["G"] : 0; + $B = isset($Settings["B"]) ? $Settings["B"] : 0; + $Alpha = isset($Settings["Alpha"]) ? $Settings["Alpha"] : 100; + $Name = isset($Settings["Name"]) ? $Settings["Name"] : NULL; + $Ticks = isset($Settings["Ticks"]) ? $Settings["Ticks"] : NULL; + + $this->Links[$FromNode][$ToNode]["R"] = $R; $this->Links[$ToNode][$FromNode]["R"] = $R; + $this->Links[$FromNode][$ToNode]["G"] = $G; $this->Links[$ToNode][$FromNode]["G"] = $G; + $this->Links[$FromNode][$ToNode]["B"] = $B; $this->Links[$ToNode][$FromNode]["B"] = $B; + $this->Links[$FromNode][$ToNode]["Alpha"] = $Alpha; $this->Links[$ToNode][$FromNode]["Alpha"] = $Alpha; + $this->Links[$FromNode][$ToNode]["Name"] = $Name; $this->Links[$ToNode][$FromNode]["Name"] = $Name; + $this->Links[$FromNode][$ToNode]["Ticks"] = $Ticks; $this->Links[$ToNode][$FromNode]["Ticks"] = $Ticks; + } + + function setNodeDefaults($Settings="") + { + if ( isset($Settings["R"]) ) { $this->Default["R"] = $Settings["R"]; } + if ( isset($Settings["G"]) ) { $this->Default["G"] = $Settings["G"]; } + if ( isset($Settings["B"]) ) { $this->Default["B"] = $Settings["B"]; } + if ( isset($Settings["Alpha"]) ) { $this->Default["Alpha"] = $Settings["Alpha"]; } + if ( isset($Settings["BorderR"]) ) { $this->Default["BorderR"] = $Settings["BorderR"]; } + if ( isset($Settings["BorderG"]) ) { $this->Default["BorderG"] = $Settings["BorderG"]; } + if ( isset($Settings["BorderB"]) ) { $this->Default["BorderB"] = $Settings["BorderB"]; } + if ( isset($Settings["BorderAlpha"]) ) { $this->Default["BorderAlpha"] = $Settings["BorderAlpha"]; } + if ( isset($Settings["Surrounding"]) ) { $this->Default["Surrounding"] = $Settings["Surrounding"]; } + if ( isset($Settings["BackgroundR"]) ) { $this->Default["BackgroundR"] = $Settings["BackgroundR"]; } + if ( isset($Settings["BackgroundG"]) ) { $this->Default["BackgroundG"] = $Settings["BackgroundG"]; } + if ( isset($Settings["BackgroundB"]) ) { $this->Default["BackgroundB"] = $Settings["BackgroundB"]; } + if ( isset($Settings["BackgroundAlpha"]) ) { $this->Default["BackgroundAlpha"] = $Settings["BackgroundAlpha"]; } + if ( isset($Settings["NodeType"]) ) { $this->Default["NodeType"] = $Settings["NodeType"]; } + if ( isset($Settings["Size"]) ) { $this->Default["Size"] = $Settings["Size"]; } + if ( isset($Settings["Shape"]) ) { $this->Default["Shape"] = $Settings["Shape"]; } + if ( isset($Settings["FreeZone"]) ) { $this->Default["FreeZone"] = $Settings["FreeZone"]; } + } + + /* Add a node */ + function addNode($NodeID,$Settings="") + { + /* if the node already exists, ignore */ + if (isset($this->Data[$NodeID])) { return(0); } + + $Name = isset($Settings["Name"]) ? $Settings["Name"] : "Node ".$NodeID; + $Connections = isset($Settings["Connections"]) ? $Settings["Connections"] : NULL; + + $R = isset($Settings["R"]) ? $Settings["R"] : $this->Default["R"]; + $G = isset($Settings["G"]) ? $Settings["G"] : $this->Default["G"]; + $B = isset($Settings["B"]) ? $Settings["B"] : $this->Default["B"]; + $Alpha = isset($Settings["Alpha"]) ? $Settings["Alpha"] : $this->Default["Alpha"]; + $BorderR = isset($Settings["BorderR"]) ? $Settings["BorderR"] : $this->Default["BorderR"]; + $BorderG = isset($Settings["BorderG"]) ? $Settings["BorderG"] : $this->Default["BorderG"]; + $BorderB = isset($Settings["BorderB"]) ? $Settings["BorderB"] : $this->Default["BorderB"]; + $BorderAlpha = isset($Settings["BorderAlpha"]) ? $Settings["BorderAlpha"] : $this->Default["BorderAlpha"]; + $Surrounding = isset($Settings["Surrounding"]) ? $Settings["Surrounding"] : $this->Default["Surrounding"]; + $BackgroundR = isset($Settings["BackgroundR"]) ? $Settings["BackgroundR"] : $this->Default["BackgroundR"]; + $BackgroundG = isset($Settings["BackgroundG"]) ? $Settings["BackgroundG"] : $this->Default["BackgroundG"]; + $BackgroundB = isset($Settings["BackgroundB"]) ? $Settings["BackgroundB"] : $this->Default["BackgroundB"]; + $BackgroundAlpha = isset($Settings["BackgroundAlpha"]) ? $Settings["BackgroundAlpha"] : $this->Default["BackgroundAlpha"]; + $Force = isset($Settings["Force"]) ? $Settings["Force"] : $this->Default["Force"]; + $NodeType = isset($Settings["NodeType"]) ? $Settings["NodeType"] : $this->Default["NodeType"]; + $Size = isset($Settings["Size"]) ? $Settings["Size"] : $this->Default["Size"]; + $Shape = isset($Settings["Shape"]) ? $Settings["Shape"] : $this->Default["Shape"]; + $FreeZone = isset($Settings["FreeZone"]) ? $Settings["FreeZone"] : $this->Default["FreeZone"]; + + if ( $Surrounding != NULL ) { $BorderR = $R + $Surrounding; $BorderG = $G + $Surrounding; $BorderB = $B + $Surrounding; } + + $this->Data[$NodeID]["R"] = $R; $this->Data[$NodeID]["G"] = $G; $this->Data[$NodeID]["B"] = $B; $this->Data[$NodeID]["Alpha"] = $Alpha; + $this->Data[$NodeID]["BorderR"] = $BorderR; $this->Data[$NodeID]["BorderG"] = $BorderG; $this->Data[$NodeID]["BorderB"] = $BorderB; $this->Data[$NodeID]["BorderAlpha"] = $BorderAlpha; + $this->Data[$NodeID]["BackgroundR"] = $BackgroundR; $this->Data[$NodeID]["BackgroundG"] = $BackgroundG; $this->Data[$NodeID]["BackgroundB"] = $BackgroundB; $this->Data[$NodeID]["BackgroundAlpha"] = $BackgroundAlpha; + $this->Data[$NodeID]["Name"] = $Name; + $this->Data[$NodeID]["Force"] = $Force; + $this->Data[$NodeID]["Type"] = $NodeType; + $this->Data[$NodeID]["Size"] = $Size; + $this->Data[$NodeID]["Shape"] = $Shape; + $this->Data[$NodeID]["FreeZone"] = $FreeZone; + if ( $Connections != NULL ) + { + if ( is_array($Connections ) ) + { + foreach($Connections as $Key => $Value) + $this->Data[$NodeID]["Connections"][] = $Value; + } + else + $this->Data[$NodeID]["Connections"][] = $Connections; + } + } + + /* Set color attribute for a list of nodes */ + function setNodesColor($Nodes,$Settings="") + { + if ( is_array($Nodes) ) + { + foreach ($Nodes as $Key => $NodeID) + { + if (isset($this->Data[$NodeID]) ) + { + if ( isset($Settings["R"]) ) { $this->Data[$NodeID]["R"] = $Settings["R"]; } + if ( isset($Settings["G"]) ) { $this->Data[$NodeID]["G"] = $Settings["G"]; } + if ( isset($Settings["B"]) ) { $this->Data[$NodeID]["B"] = $Settings["B"]; } + if ( isset($Settings["Alpha"]) ) { $this->Data[$NodeID]["Alpha"] = $Settings["Alpha"]; } + if ( isset($Settings["BorderR"]) ) { $this->Data[$NodeID]["BorderR"] = $Settings["BorderR"]; } + if ( isset($Settings["BorderG"]) ) { $this->Data[$NodeID]["BorderG"] = $Settings["BorderG"]; } + if ( isset($Settings["BorderB"]) ) { $this->Data[$NodeID]["BorderB"] = $Settings["BorderB"]; } + if ( isset($Settings["BorderAlpha"]) ) { $this->Data[$NodeID]["BorderAlpha"] = $Settings["BorderAlpha"]; } + if ( isset($Settings["Surrounding"]) ) { $this->Data[$NodeID]["BorderR"] = $this->Data[$NodeID]["R"] + $Settings["Surrounding"]; $this->Data[$NodeID]["BorderG"] = $this->Data[$NodeID]["G"] + $Settings["Surrounding"]; $this->Data[$NodeID]["BorderB"] = $this->Data[$NodeID]["B"] + $Settings["Surrounding"]; } + } + } + } + else + { + if ( isset($Settings["R"]) ) { $this->Data[$Nodes]["R"] = $Settings["R"]; } + if ( isset($Settings["G"]) ) { $this->Data[$Nodes]["G"] = $Settings["G"]; } + if ( isset($Settings["B"]) ) { $this->Data[$Nodes]["B"] = $Settings["B"]; } + if ( isset($Settings["Alpha"]) ) { $this->Data[$Nodes]["Alpha"] = $Settings["Alpha"]; } + if ( isset($Settings["BorderR"]) ) { $this->Data[$Nodes]["BorderR"] = $Settings["BorderR"]; } + if ( isset($Settings["BorderG"]) ) { $this->Data[$Nodes]["BorderG"] = $Settings["BorderG"]; } + if ( isset($Settings["BorderB"]) ) { $this->Data[$Nodes]["BorderB"] = $Settings["BorderB"]; } + if ( isset($Settings["BorderAlpha"]) ) { $this->Data[$Nodes]["BorderAlpha"] = $Settings["BorderAlpha"]; } + if ( isset($Settings["Surrounding"]) ) { $this->Data[$Nodes]["BorderR"] = $this->Data[$NodeID]["R"] + $Settings["Surrounding"]; $this->Data[$NodeID]["BorderG"] = $this->Data[$NodeID]["G"] + $Settings["Surrounding"]; $this->Data[$NodeID]["BorderB"] = $this->Data[$NodeID]["B"] + $Settings["Surrounding"]; } + } + } + + /* Returns all the nodes details */ + function dumpNodes() + { return($this->Data); } + + /* Check if a connection exists and create it if required */ + function checkConnection($SourceID, $TargetID) + { + if ( isset($this->Data[$SourceID]["Connections"]) ) + { + foreach ($this->Data[$SourceID]["Connections"] as $Key => $ConnectionID) + { if ( $TargetID == $ConnectionID ) { return(TRUE); } } + } + $this->Data[$SourceID]["Connections"][] = $TargetID; + } + /* Get the median linked nodes position */ + function getMedianOffset($Key,$X,$Y) + { + $Cpt = 1; + if ( isset($this->Data[$Key]["Connections"]) ) + { + foreach($this->Data[$Key]["Connections"] as $ID => $NodeID) + { + if ( isset($this->Data[$NodeID]["X"]) && isset($this->Data[$NodeID]["Y"]) ) + { + $X = $X + $this->Data[$NodeID]["X"]; + $Y = $Y + $this->Data[$NodeID]["Y"]; + $Cpt++; + } + } + } + return(array("X"=>$X/$Cpt,"Y"=>$Y/$Cpt)); + } + + /* Return the ID of the attached partner with the biggest weight */ + function getBiggestPartner($Key) + { + if ( !isset($this->Data[$Key]["Connections"]) ) { return(""); } + + $MaxWeight = 0; $Result = ""; + foreach($this->Data[$Key]["Connections"] as $Key => $PeerID) + { + if ( $this->Data[$PeerID]["Weight"] > $MaxWeight ) + { $MaxWeight = $this->Data[$PeerID]["Weight"]; $Result = $PeerID; } + } + return($Result); + } + + /* Do the initial node positions computing pass */ + function firstPass($Algorithm) + { + $CenterX = ($this->X2 - $this->X1) / 2 + $this->X1; + $CenterY = ($this->Y2 - $this->Y1) / 2 + $this->Y1; + + /* Check connections reciprocity */ + foreach($this->Data as $Key => $Settings) + { + if ( isset($Settings["Connections"]) ) + { + foreach($Settings["Connections"] as $ID => $ConnectionID) + $this->checkConnection($ConnectionID,$Key); + } + } + + if ( $this->AutoComputeFreeZone ) { $this->autoFreeZone(); } + + /* Get the max number of connections */ + $MaxConnections = 0; + foreach($this->Data as $Key => $Settings) + { if ( isset($Settings["Connections"]) ) { if ( $MaxConnections < count($Settings["Connections"] ) ) { $MaxConnections = count($Settings["Connections"]); } } } + + if ( $Algorithm == ALGORITHM_WEIGHTED ) + { + foreach($this->Data as $Key => $Settings) + { + if ( $Settings["Type"] == NODE_TYPE_CENTRAL ) { $this->Data[$Key]["X"] = $CenterX; $this->Data[$Key]["Y"] = $CenterY; } + if ( $Settings["Type"] == NODE_TYPE_FREE ) + { + if ( isset($Settings["Connections"]) ) + { $Connections = count($Settings["Connections"]); } + else + { $Connections = 0; } + + $Ring = $MaxConnections - $Connections; + $Angle = rand(0,360); + + $this->Data[$Key]["X"] = cos(deg2rad($Angle)) * ($Ring*$this->RingSize) + $CenterX; + $this->Data[$Key]["Y"] = sin(deg2rad($Angle)) * ($Ring*$this->RingSize) + $CenterY; + } + } + } + elseif ( $Algorithm == ALGORITHM_CENTRAL ) + { + /* Put a weight on each nodes */ + foreach($this->Data as $Key => $Settings) + { + if ( isset($Settings["Connections"]) ) + $this->Data[$Key]["Weight"] = count($Settings["Connections"]); + else + $this->Data[$Key]["Weight"] = 0; + } + + $MaxConnections = $MaxConnections + 1; + for($i=$MaxConnections;$i>=0;$i--) + { + foreach($this->Data as $Key => $Settings) + { + if ( $Settings["Type"] == NODE_TYPE_CENTRAL ) { $this->Data[$Key]["X"] = $CenterX; $this->Data[$Key]["Y"] = $CenterY; } + if ( $Settings["Type"] == NODE_TYPE_FREE ) + { + if ( isset($Settings["Connections"]) ) + { $Connections = count($Settings["Connections"]); } + else + { $Connections = 0; } + + if ( $Connections == $i ) + { + $BiggestPartner = $this->getBiggestPartner($Key); + if ( $BiggestPartner != "" ) + { + $Ring = $this->Data[$BiggestPartner]["FreeZone"]; + $Weight = $this->Data[$BiggestPartner]["Weight"]; + $AngleDivision = 360 / $this->Data[$BiggestPartner]["Weight"]; + $Done = FALSE; $Tries = 0; + while (!$Done && $Tries <= $Weight*2) + { + $Tries++; + $Angle = floor(rand(0,$Weight)*$AngleDivision); + if ( !isset($this->Data[$BiggestPartner]["Angular"][$Angle]) || !isset($this->Data[$BiggestPartner]["Angular"]) ) + { + $this->Data[$BiggestPartner]["Angular"][$Angle] = $Angle; + $Done = TRUE; + } + } + if ( !$Done ) + { $Angle = rand(0,360); $this->Data[$BiggestPartner]["Angular"][$Angle] = $Angle; } + + $X = cos(deg2rad($Angle)) * ($Ring) + $this->Data[$BiggestPartner]["X"]; + $Y = sin(deg2rad($Angle)) * ($Ring) + $this->Data[$BiggestPartner]["Y"]; + + $this->Data[$Key]["X"] = $X; + $this->Data[$Key]["Y"] = $Y; + } + } + } + } + } + } + elseif ( $Algorithm == ALGORITHM_CIRCULAR ) + { + $MaxConnections = $MaxConnections + 1; + for($i=$MaxConnections;$i>=0;$i--) + { + foreach($this->Data as $Key => $Settings) + { + if ( $Settings["Type"] == NODE_TYPE_CENTRAL ) { $this->Data[$Key]["X"] = $CenterX; $this->Data[$Key]["Y"] = $CenterY; } + if ( $Settings["Type"] == NODE_TYPE_FREE ) + { + if ( isset($Settings["Connections"]) ) + { $Connections = count($Settings["Connections"]); } + else + { $Connections = 0; } + + if ( $Connections == $i ) + { + $Ring = $MaxConnections - $Connections; + $Angle = rand(0,360); + + $X = cos(deg2rad($Angle)) * ($Ring*$this->RingSize) + $CenterX; + $Y = sin(deg2rad($Angle)) * ($Ring*$this->RingSize) + $CenterY; + + $MedianOffset = $this->getMedianOffset($Key,$X,$Y); + + $this->Data[$Key]["X"] = $MedianOffset["X"]; + $this->Data[$Key]["Y"] = $MedianOffset["Y"]; + } + } + } + } + } + elseif ( $Algorithm == ALGORITHM_RANDOM ) + { + foreach($this->Data as $Key => $Settings) + { + if ( $Settings["Type"] == NODE_TYPE_FREE ) + { + $this->Data[$Key]["X"] = $CenterX + rand(-20,20); + $this->Data[$Key]["Y"] = $CenterY + rand(-20,20); + } + if ( $Settings["Type"] == NODE_TYPE_CENTRAL ) { $this->Data[$Key]["X"] = $CenterX; $this->Data[$Key]["Y"] = $CenterY; } + } + } + } + + /* Compute one pass */ + function doPass() + { + /* Compute vectors */ + foreach($this->Data as $Key => $Settings) + { + if ( $Settings["Type"] != NODE_TYPE_CENTRAL ) + { + unset($this->Data[$Key]["Vectors"]); + + $X1 = $Settings["X"]; + $Y1 = $Settings["Y"]; + + /* Repulsion vectors */ + foreach($this->Data as $Key2 => $Settings2) + { + if ( $Key != $Key2 ) + { + $X2 = $this->Data[$Key2]["X"]; + $Y2 = $this->Data[$Key2]["Y"]; + $FreeZone = $this->Data[$Key2]["FreeZone"]; + + $Distance = $this->getDistance($X1,$Y1,$X2,$Y2); + $Angle = $this->getAngle($X1,$Y1,$X2,$Y2) + 180; + + /* Nodes too close, repulsion occurs */ + if ( $Distance < $FreeZone ) + { + $Force = log(pow(2,$FreeZone-$Distance)); + if ( $Force > 1 ) + { $this->Data[$Key]["Vectors"][] = array("Type"=>"R","Angle"=>$Angle % 360,"Force"=>$Force); } + } + } + } + + /* Attraction vectors */ + if ( isset($Settings["Connections"]) ) + { + foreach($Settings["Connections"] as $ID => $NodeID) + { + if ( isset($this->Data[$NodeID]) ) + { + $X2 = $this->Data[$NodeID]["X"]; + $Y2 = $this->Data[$NodeID]["Y"]; + $FreeZone = $this->Data[$Key2]["FreeZone"]; + + $Distance = $this->getDistance($X1,$Y1,$X2,$Y2); + $Angle = $this->getAngle($X1,$Y1,$X2,$Y2); + + if ( $Distance > $FreeZone ) + $Force = log(($Distance-$FreeZone)+1); + else + { $Force = log(($FreeZone-$Distance)+1); ($Angle = $Angle + 180); } + + if ( $Force > 1 ) + $this->Data[$Key]["Vectors"][] = array("Type"=>"A","Angle"=>$Angle % 360,"Force"=>$Force); + } + } + } + } + } + + /* Move the nodes accoding to the vectors */ + foreach($this->Data as $Key => $Settings) + { + $X = $Settings["X"]; + $Y = $Settings["Y"]; + + if ( isset($Settings["Vectors"]) && $Settings["Type"] != NODE_TYPE_CENTRAL ) + { + foreach($Settings["Vectors"] as $ID => $Vector) + { + $Type = $Vector["Type"]; + $Force = $Vector["Force"]; + $Angle = $Vector["Angle"]; + $Factor = $Type == "A" ? $this->MagneticForceA : $this->MagneticForceR; + + $X = cos(deg2rad($Angle)) * $Force * $Factor + $X; + $Y = sin(deg2rad($Angle)) * $Force * $Factor + $Y; + } + } + + $this->Data[$Key]["X"] = $X; + $this->Data[$Key]["Y"] = $Y; + } + } + + function lastPass() + { + /* Put everything inside the graph area */ + foreach($this->Data as $Key => $Settings) + { + $X = $Settings["X"]; + $Y = $Settings["Y"]; + + if ( $X < $this->X1 ) { $X = $this->X1; } + if ( $X > $this->X2 ) { $X = $this->X2; } + if ( $Y < $this->Y1 ) { $Y = $this->Y1; } + if ( $Y > $this->Y2 ) { $Y = $this->Y2; } + + $this->Data[$Key]["X"] = $X; + $this->Data[$Key]["Y"] = $Y; + } + + /* Dump all links */ + $Links = ""; + foreach($this->Data as $Key => $Settings) + { + $X1 = $Settings["X"]; + $Y1 = $Settings["Y"]; + + if ( isset($Settings["Connections"]) ) + { + foreach ($Settings["Connections"] as $ID => $NodeID) + { + if ( isset($this->Data[$NodeID]) ) + { + $X2 = $this->Data[$NodeID]["X"]; + $Y2 = $this->Data[$NodeID]["Y"]; + + $Links[] = array("X1"=>$X1,"Y1"=>$Y1,"X2"=>$X2,"Y2"=>$Y2,"Source"=>$Settings["Name"],"Destination"=>$this->Data[$NodeID]["Name"]); + } + } + } + } + + /* Check collisions */ + $Conflicts = 0; + foreach($this->Data as $Key => $Settings) + { + $X1 = $Settings["X"]; + $Y1 = $Settings["Y"]; + + if ( isset($Settings["Connections"]) ) + { + foreach ($Settings["Connections"] as $ID => $NodeID) + { + if ( isset($this->Data[$NodeID]) ) + { + $X2 = $this->Data[$NodeID]["X"]; + $Y2 = $this->Data[$NodeID]["Y"]; + + foreach($Links as $IDLinks => $Link) + { + $X3 = $Link["X1"]; $Y3 = $Link["Y1"]; $X4 = $Link["X2"]; $Y4 = $Link["Y2"]; + + if ( !($X1 == $X3 && $X2 == $X4 && $Y1 == $Y3 && $Y2 == $Y4 ) ) + { + if ( $this->intersect($X1,$Y1,$X2,$Y2,$X3,$Y3,$X4,$Y4) ) + { + if ( $Link["Source"] != $Settings["Name"] && $Link["Source"] != $this->Data[$NodeID]["Name"] && $Link["Destination"] != $Settings["Name"] && $Link["Destination"] != $this->Data[$NodeID]["Name"] ) + { $Conflicts++; } + } + } + } + } + } + } + } + return($Conflicts/2); + } + + /* Center the graph */ + function center() + { + /* Determine the real center */ + $TargetCenterX = ($this->X2 - $this->X1) / 2 + $this->X1; + $TargetCenterY = ($this->Y2 - $this->Y1) / 2 + $this->Y1; + + /* Get current boundaries */ + $XMin = $this->X2; $XMax = $this->X1; + $YMin = $this->Y2; $YMax = $this->Y1; + foreach($this->Data as $Key => $Settings) + { + $X = $Settings["X"]; + $Y = $Settings["Y"]; + + if ( $X < $XMin) { $XMin = $X; } + if ( $X > $XMax) { $XMax = $X; } + if ( $Y < $YMin) { $YMin = $Y; } + if ( $Y > $YMax) { $YMax = $Y; } + } + $CurrentCenterX = ($XMax - $XMin) / 2 + $XMin; + $CurrentCenterY = ($YMax - $YMin) / 2 + $YMin; + + /* Compute the offset to apply */ + $XOffset = $TargetCenterX - $CurrentCenterX; + $YOffset = $TargetCenterY - $CurrentCenterY; + + /* Correct the points position */ + foreach($this->Data as $Key => $Settings) + { + $this->Data[$Key]["X"] = $Settings["X"] + $XOffset; + $this->Data[$Key]["Y"] = $Settings["Y"] + $YOffset; + } + } + + /* Create the encoded string */ + function drawSpring($Object,$Settings="") + { + $this->pChartObject = $Object; + + $Pass = isset($Settings["Pass"]) ? $Settings["Pass"] : 50; + $Retries = isset($Settings["Retry"]) ? $Settings["Retry"] : 10; + $this->MagneticForceA = isset($Settings["MagneticForceA"]) ? $Settings["MagneticForceA"] : 1.5; + $this->MagneticForceR = isset($Settings["MagneticForceR"]) ? $Settings["MagneticForceR"] : 2; + $this->RingSize = isset($Settings["RingSize"]) ? $Settings["RingSize"] : 40; + $DrawVectors = isset($Settings["DrawVectors"]) ? $Settings["DrawVectors"] : FALSE; + $DrawQuietZone = isset($Settings["DrawQuietZone"]) ? $Settings["DrawQuietZone"] : FALSE; + $CenterGraph = isset($Settings["CenterGraph"]) ? $Settings["CenterGraph"] : TRUE; + $TextPadding = isset($Settings["TextPadding"]) ? $Settings["TextPadding"] : 4; + $Algorithm = isset($Settings["Algorithm"]) ? $Settings["Algorithm"] : ALGORITHM_WEIGHTED; + + $FontSize = $Object->FontSize; + $this->X1 = $Object->GraphAreaX1; + $this->Y1 = $Object->GraphAreaY1; + $this->X2 = $Object->GraphAreaX2; + $this->Y2 = $Object->GraphAreaY2; + + $Conflicts = 1; $Jobs = 0; $this->History["MinimumConflicts"] = -1; + while ($Conflicts != 0 && $Jobs < $Retries ) + { + $Jobs++; + + /* Compute the initial settings */ + $this->firstPass($Algorithm); + + /* Apply the vectors */ + if ( $Pass > 0 ) + { + for ($i=0; $i<=$Pass; $i++) { $this->doPass(); } + } + + $Conflicts = $this->lastPass(); + if ( $this->History["MinimumConflicts"] == -1 || $Conflicts < $this->History["MinimumConflicts"] ) + { $this->History["MinimumConflicts"] = $Conflicts; $this->History["Result"] = $this->Data; } + } + + $Conflicts = $this->History["MinimumConflicts"]; + $this->Data = $this->History["Result"]; + + if ( $CenterGraph ) { $this->center(); } + + /* Draw the connections */ + $Drawn = ""; + foreach($this->Data as $Key => $Settings) + { + $X = $Settings["X"]; + $Y = $Settings["Y"]; + + if ( isset($Settings["Connections"]) ) + { + foreach ($Settings["Connections"] as $ID => $NodeID) + { + if ( !isset($Drawn[$Key]) ) { $Drawn[$Key] = ""; } + if ( !isset($Drawn[$NodeID]) ) { $Drawn[$NodeID] = ""; } + + if ( isset($this->Data[$NodeID]) && !isset($Drawn[$Key][$NodeID]) && !isset($Drawn[$NodeID][$Key]) ) + { + $Color = array("R"=>$this->Default["LinkR"],"G"=>$this->Default["LinkG"],"B"=>$this->Default["LinkB"],"Alpha"=>$this->Default["Alpha"]); + + if ( $this->Links != "" ) + { + if ( isset($this->Links[$Key][$NodeID]["R"]) ) + { $Color = array("R"=>$this->Links[$Key][$NodeID]["R"],"G"=>$this->Links[$Key][$NodeID]["G"],"B"=>$this->Links[$Key][$NodeID]["B"],"Alpha"=>$this->Links[$Key][$NodeID]["Alpha"]); } + + if ( isset($this->Links[$Key][$NodeID]["Ticks"]) ) + { $Color["Ticks"] = $this->Links[$Key][$NodeID]["Ticks"]; } + } + + $X2 = $this->Data[$NodeID]["X"]; + $Y2 = $this->Data[$NodeID]["Y"]; + $this->pChartObject->drawLine($X,$Y,$X2,$Y2,$Color); + $Drawn[$Key][$NodeID] = TRUE; + + if ( isset($this->Links) && $this->Links != "" ) + { + if ( isset($this->Links[$Key][$NodeID]["Name"]) || isset($this->Links[$NodeID][$Key]["Name"]) ) + { + $Name = isset($this->Links[$Key][$NodeID]["Name"]) ? $this->Links[$Key][$NodeID]["Name"] : $this->Links[$NodeID][$Key]["Name"]; + $TxtX = ($X2 - $X)/2 + $X; + $TxtY = ($Y2 - $Y)/2 + $Y; + + if ( $X <= $X2 ) + $Angle = (360-$this->getAngle($X,$Y,$X2,$Y2)) % 360; + else + $Angle = (360-$this->getAngle($X2,$Y2,$X,$Y)) % 360; + + $Settings = $Color; + $Settings["Angle"] = $Angle; + $Settings["Align"] = TEXT_ALIGN_BOTTOMMIDDLE; + $this->pChartObject->drawText($TxtX,$TxtY,$Name,$Settings); + } + } + } + } + } + } + + /* Draw the quiet zones */ + if ( $DrawQuietZone ) + { + foreach($this->Data as $Key => $Settings) + { + $X = $Settings["X"]; + $Y = $Settings["Y"]; + $FreeZone = $Settings["FreeZone"]; + + $this->pChartObject->drawFilledCircle($X,$Y,$FreeZone,array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>2)); + } + } + + + /* Draw the nodes */ + foreach($this->Data as $Key => $Settings) + { + $X = $Settings["X"]; + $Y = $Settings["Y"]; + $Name = $Settings["Name"]; + $FreeZone = $Settings["FreeZone"]; + $Shape = $Settings["Shape"]; + $Size = $Settings["Size"]; + + $Color = array("R"=>$Settings["R"],"G"=>$Settings["G"],"B"=>$Settings["B"],"Alpha"=>$Settings["Alpha"],"BorderR"=>$Settings["BorderR"],"BorderG"=>$Settings["BorderG"],"BorderB"=>$Settings["BorderB"],"BorderApha"=>$Settings["BorderAlpha"]); + + if ( $Shape == NODE_SHAPE_CIRCLE ) + { + $this->pChartObject->drawFilledCircle($X,$Y,$Size,$Color); + } + elseif ( $Shape == NODE_SHAPE_TRIANGLE ) + { + $Points = ""; + $Points[] = cos(deg2rad(270)) * $Size + $X; $Points[] = sin(deg2rad(270)) * $Size + $Y; + $Points[] = cos(deg2rad(45)) * $Size + $X; $Points[] = sin(deg2rad(45)) * $Size + $Y; + $Points[] = cos(deg2rad(135)) * $Size + $X; $Points[] = sin(deg2rad(135)) * $Size + $Y; + $this->pChartObject->drawPolygon($Points,$Color); + } + elseif ( $Shape == NODE_SHAPE_SQUARE ) + { + $Offset = $Size/2; $Size = $Size / 2; + $this->pChartObject->drawFilledRectangle($X-$Offset,$Y-$Offset,$X+$Offset,$Y+$Offset,$Color); + } + + if ( $Name != "" ) + { + $LabelOptions = array("R"=>$this->Labels["R"],"G"=>$this->Labels["G"],"B"=>$this->Labels["B"],"Alpha"=>$this->Labels["Alpha"]); + + if ( $this->Labels["Type"] == LABEL_LIGHT ) + { + $LabelOptions["Align"] = TEXT_ALIGN_BOTTOMLEFT; + $this->pChartObject->drawText($X,$Y,$Name,$LabelOptions); + } + elseif ( $this->Labels["Type"] == LABEL_CLASSIC ) + { + $LabelOptions["Align"] = TEXT_ALIGN_TOPMIDDLE; + $LabelOptions["DrawBox"] = TRUE; + $LabelOptions["BoxAlpha"] = 50; + $LabelOptions["BorderOffset"] = 4; + $LabelOptions["RoundedRadius"] = 3; + $LabelOptions["BoxRounded"] = TRUE; + $LabelOptions["NoShadow"] = TRUE; + + $this->pChartObject->drawText($X,$Y+$Size+$TextPadding,$Name,$LabelOptions); + } + } + } + + /* Draw the vectors */ + if ( $DrawVectors ) + { + foreach($this->Data as $Key => $Settings) + { + $X1 = $Settings["X"]; + $Y1 = $Settings["Y"]; + + if ( isset($Settings["Vectors"]) && $Settings["Type"] != NODE_TYPE_CENTRAL ) + { + foreach($Settings["Vectors"] as $ID => $Vector) + { + $Type = $Vector["Type"]; + $Force = $Vector["Force"]; + $Angle = $Vector["Angle"]; + $Factor = $Type == "A" ? $this->MagneticForceA : $this->MagneticForceR; + $Color = $Type == "A" ? array("FillR"=>255,"FillG"=>0,"FillB"=>0) : array("FillR"=>0,"FillG"=>255,"FillB"=>0); + + $X2 = cos(deg2rad($Angle)) * $Force * $Factor + $X1; + $Y2 = sin(deg2rad($Angle)) * $Force * $Factor + $Y1; + + $this->pChartObject->drawArrow($X1,$Y1,$X2,$Y2,$Color); + } + } + } + } + + return(array("Pass"=>$Jobs,"Conflicts"=>$Conflicts)); + } + + /* Return the distance between two points */ + function getDistance($X1,$Y1,$X2,$Y2) + { return (sqrt(($X2-$X1)*($X2-$X1)+($Y2-$Y1)*($Y2-$Y1))); } + + /* Return the angle made by a line and the X axis */ + function getAngle($X1,$Y1,$X2,$Y2) + { + $Opposite = $Y2 - $Y1; $Adjacent = $X2 - $X1;$Angle = rad2deg(atan2($Opposite,$Adjacent)); + if ($Angle > 0) { return($Angle); } else { return(360-abs($Angle)); } + } + + function intersect($X1,$Y1,$X2,$Y2,$X3,$Y3,$X4,$Y4) + { + $A = (($X3 * $Y4 - $X4 * $Y3) * ($X1 - $X2) - ($X1 * $Y2 - $X2 * $Y1) * ($X3 - $X4)); + $B = (($Y1 - $Y2) * ($X3 - $X4) - ($Y3 - $Y4) * ($X1 - $X2)); + + if ( $B == 0 ) { return(FALSE); } + $Xi = $A / $B; + + $C = ($X1 - $X2); + if ( $C == 0 ) { return(FALSE); } + $Yi = $Xi * (($Y1 - $Y2)/$C) + (($X1 * $Y2 - $X2 * $Y1)/$C); + + if ( $Xi >= min($X1,$X2) && $Xi >= min($X3,$X4) && $Xi <= max($X1,$X2) && $Xi <= max($X3,$X4)) + { + if ( $Yi >= min($Y1,$Y2) && $Yi >= min($Y3,$Y4) && $Yi <= max($Y1,$Y2) && $Yi <= max($Y3,$Y4)) + { return(TRUE); } + } + + return(FALSE); + } + } ?> \ No newline at end of file diff --git a/www/libs/pChart2.1.3/class/pStock.class.php b/www/libs/pChart2.1.4/class/pStock.class.php similarity index 97% rename from www/libs/pChart2.1.3/class/pStock.class.php rename to www/libs/pChart2.1.4/class/pStock.class.php index 9b594886..b31c3e7d 100644 --- a/www/libs/pChart2.1.3/class/pStock.class.php +++ b/www/libs/pChart2.1.4/class/pStock.class.php @@ -1,216 +1,216 @@ -pChartObject = $pChartObject; - $this->pDataObject = $pDataObject; - } - - /* Draw a stock chart */ - function drawStockChart($Format="") - { - $SerieOpen = isset($Format["SerieOpen"]) ? $Format["SerieOpen"] : "Open"; - $SerieClose = isset($Format["SerieClose"]) ? $Format["SerieClose"] : "Close"; - $SerieMin = isset($Format["SerieMin"]) ? $Format["SerieMin"] : "Min"; - $SerieMax = isset($Format["SerieMax"]) ? $Format["SerieMax"] : "Max"; - $SerieMedian = isset($Format["SerieMedian"]) ? $Format["SerieMedian"] : NULL; - $LineWidth = isset($Format["LineWidth"]) ? $Format["LineWidth"] : 1; - $LineR = isset($Format["LineR"]) ? $Format["LineR"] : 0; - $LineG = isset($Format["LineG"]) ? $Format["LineG"] : 0; - $LineB = isset($Format["LineB"]) ? $Format["LineB"] : 0; - $LineAlpha = isset($Format["LineAlpha"]) ? $Format["LineAlpha"] : 100; - $ExtremityWidth = isset($Format["ExtremityWidth"]) ? $Format["ExtremityWidth"] : 1; - $ExtremityLength = isset($Format["ExtremityLength"]) ? $Format["ExtremityLength"] : 3; - $ExtremityR = isset($Format["ExtremityR"]) ? $Format["ExtremityR"] : 0; - $ExtremityG = isset($Format["ExtremityG"]) ? $Format["ExtremityG"] : 0; - $ExtremityB = isset($Format["ExtremityB"]) ? $Format["ExtremityB"] : 0; - $ExtremityAlpha = isset($Format["ExtremityAlpha"]) ? $Format["ExtremityAlpha"] : 100; - $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 8; - $BoxUpR = isset($Format["BoxUpR"]) ? $Format["BoxUpR"] : 188; - $BoxUpG = isset($Format["BoxUpG"]) ? $Format["BoxUpG"] : 224; - $BoxUpB = isset($Format["BoxUpB"]) ? $Format["BoxUpB"] : 46; - $BoxUpAlpha = isset($Format["BoxUpAlpha"]) ? $Format["BoxUpAlpha"] : 100; - $BoxUpSurrounding = isset($Format["BoxUpSurrounding"]) ? $Format["BoxUpSurrounding"] : NULL; - $BoxUpBorderR = isset($Format["BoxUpBorderR"]) ? $Format["BoxUpBorderR"] : $BoxUpR-20; - $BoxUpBorderG = isset($Format["BoxUpBorderG"]) ? $Format["BoxUpBorderG"] : $BoxUpG-20; - $BoxUpBorderB = isset($Format["BoxUpBorderB"]) ? $Format["BoxUpBorderB"] : $BoxUpB-20; - $BoxUpBorderAlpha = isset($Format["BoxUpBorderAlpha"]) ? $Format["BoxUpBorderAlpha"] : 100; - $BoxDownR = isset($Format["BoxDownR"]) ? $Format["BoxDownR"] : 224; - $BoxDownG = isset($Format["BoxDownG"]) ? $Format["BoxDownG"] : 100; - $BoxDownB = isset($Format["BoxDownB"]) ? $Format["BoxDownB"] : 46; - $BoxDownAlpha = isset($Format["BoxDownAlpha"]) ? $Format["BoxDownAlpha"] : 100; - $BoxDownSurrounding= isset($Format["BoxDownSurrounding"]) ? $Format["BoxDownSurrounding"] : NULL; - $BoxDownBorderR = isset($Format["BoxDownBorderR"]) ? $Format["BoxDownBorderR"] : $BoxDownR-20; - $BoxDownBorderG = isset($Format["BoxDownBorderG"]) ? $Format["BoxDownBorderG"] : $BoxDownG-20; - $BoxDownBorderB = isset($Format["BoxDownBorderB"]) ? $Format["BoxDownBorderB"] : $BoxDownB-20; - $BoxDownBorderAlpha= isset($Format["BoxDownBorderAlpha"]) ? $Format["BoxDownBorderAlpha"] : 100; - $ShadowOnBoxesOnly = isset($Format["ShadowOnBoxesOnly"]) ? $Format["ShadowOnBoxesOnly"] : TRUE; - $MedianR = isset($Format["MedianR"]) ? $Format["MedianR"] : 255; - $MedianG = isset($Format["MedianG"]) ? $Format["MedianG"] : 0; - $MedianB = isset($Format["MedianB"]) ? $Format["MedianB"] : 0; - $MedianAlpha = isset($Format["MedianAlpha"]) ? $Format["MedianAlpha"] : 100; - $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; - $ImageMapTitle = isset($Format["ImageMapTitle"]) ? $Format["ImageMapTitle"] : "Stock Chart"; - - - /* Data Processing */ - $Data = $this->pDataObject->getData(); - $Palette = $this->pDataObject->getPalette(); - - if ( $BoxUpSurrounding != NULL ) { $BoxUpBorderR = $BoxUpR + $BoxUpSurrounding; $BoxUpBorderG = $BoxUpG + $BoxUpSurrounding; $BoxUpBorderB = $BoxUpB + $BoxUpSurrounding; } - if ( $BoxDownSurrounding != NULL ) { $BoxDownBorderR = $BoxDownR + $BoxDownSurrounding; $BoxDownBorderG = $BoxDownG + $BoxDownSurrounding; $BoxDownBorderB = $BoxDownB + $BoxDownSurrounding; } - - if ( $LineWidth != 1 ) { $LineOffset = $LineWidth / 2; } - $BoxOffset = $BoxWidth / 2; - - $Data = $this->pChartObject->DataSet->getData(); - list($XMargin,$XDivs) = $this->pChartObject->scaleGetXSettings(); - - if ( !isset($Data["Series"][$SerieOpen]) || !isset($Data["Series"][$SerieClose]) || !isset($Data["Series"][$SerieMin]) || !isset($Data["Series"][$SerieMax]) ) - return(STOCK_MISSING_SERIE); - - $Plots = ""; - foreach($Data["Series"][$SerieOpen]["Data"] as $Key => $Value) - { - $Point = ""; - if ( isset($Data["Series"][$SerieClose]["Data"][$Key]) || isset($Data["Series"][$SerieMin]["Data"][$Key]) || isset($Data["Series"][$SerieMax]["Data"][$Key]) ) - $Point = array($Value,$Data["Series"][$SerieClose]["Data"][$Key],$Data["Series"][$SerieMin]["Data"][$Key],$Data["Series"][$SerieMax]["Data"][$Key]); - if ( $SerieMedian != NULL && isset($Data["Series"][$SerieMedian]["Data"][$Key]) ) - $Point[] = $Data["Series"][$SerieMedian]["Data"][$Key]; - - $Plots[] = $Point; - } - - $AxisID = $Data["Series"][$SerieOpen]["Axis"]; - $Mode = $Data["Axis"][$AxisID]["Display"]; - $Format = $Data["Axis"][$AxisID]["Format"]; - $Unit = $Data["Axis"][$AxisID]["Unit"]; - - $YZero = $this->pChartObject->scaleComputeY(0,array("AxisID"=>$AxisID)); - $XStep = ($this->pChartObject->GraphAreaX2-$this->pChartObject->GraphAreaX1-$XMargin*2)/$XDivs; - - $X = $this->pChartObject->GraphAreaX1 + $XMargin; - $Y = $this->pChartObject->GraphAreaY1 + $XMargin; - - $LineSettings = array("R"=>$LineR,"G"=>$LineG,"B"=>$LineB,"Alpha"=>$LineAlpha); - $ExtremitySettings = array("R"=>$ExtremityR,"G"=>$ExtremityG,"B"=>$ExtremityB,"Alpha"=>$ExtremityAlpha); - $BoxUpSettings = array("R"=>$BoxUpR,"G"=>$BoxUpG,"B"=>$BoxUpB,"Alpha"=>$BoxUpAlpha,"BorderR"=>$BoxUpBorderR,"BorderG"=>$BoxUpBorderG,"BorderB"=>$BoxUpBorderB,"BorderAlpha"=>$BoxUpBorderAlpha); - $BoxDownSettings = array("R"=>$BoxDownR,"G"=>$BoxDownG,"B"=>$BoxDownB,"Alpha"=>$BoxDownAlpha,"BorderR"=>$BoxDownBorderR,"BorderG"=>$BoxDownBorderG,"BorderB"=>$BoxDownBorderB,"BorderAlpha"=>$BoxDownBorderAlpha); - $MedianSettings = array("R"=>$MedianR,"G"=>$MedianG,"B"=>$MedianB,"Alpha"=>$MedianAlpha); - - foreach($Plots as $Key =>$Points) - { - $PosArray = $this->pChartObject->scaleComputeY($Points,array("AxisID"=>$AxisID)); - - $Values = "Open :".$Data["Series"][$SerieOpen]["Data"][$Key]."
Close : ".$Data["Series"][$SerieClose]["Data"][$Key]."
Min : ".$Data["Series"][$SerieMin]["Data"][$Key]."
Max : ".$Data["Series"][$SerieMax]["Data"][$Key]."
"; - if ( $SerieMedian != NULL ) { $Values = $Values."Median : ".$Data["Series"][$SerieMedian]["Data"][$Key]."
"; } - if ( $PosArray[0] > $PosArray[1] ) { $ImageMapColor = $this->pChartObject->toHTMLColor($BoxUpR,$BoxUpG,$BoxUpB); } else { $ImageMapColor = $this->pChartObject->toHTMLColor($BoxDownR,$BoxDownG,$BoxDownB); } - - if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) - { - if ( $YZero > $this->pChartObject->GraphAreaY2-1 ) { $YZero = $this->pChartObject->GraphAreaY2-1; } - if ( $YZero < $this->pChartObject->GraphAreaY1+1 ) { $YZero = $this->pChartObject->GraphAreaY1+1; } - - if ( $XDivs == 0 ) { $XStep = 0; } else { $XStep = ($this->pChartObject->GraphAreaX2-$this->pChartObject->GraphAreaX1-$XMargin*2)/$XDivs; } - - if ( $ShadowOnBoxesOnly ) { $RestoreShadow = $this->pChartObject->Shadow; $this->pChartObject->Shadow = FALSE; } - - if ( $LineWidth == 1 ) - $this->pChartObject->drawLine($X,$PosArray[2],$X,$PosArray[3],$LineSettings); - else - $this->pChartObject->drawFilledRectangle($X-$LineOffset,$PosArray[2],$X+$LineOffset,$PosArray[3],$LineSettings); - - if ( $ExtremityWidth == 1 ) - { - $this->pChartObject->drawLine($X-$ExtremityLength,$PosArray[2],$X+$ExtremityLength,$PosArray[2],$ExtremitySettings); - $this->pChartObject->drawLine($X-$ExtremityLength,$PosArray[3],$X+$ExtremityLength,$PosArray[3],$ExtremitySettings); - - if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("RECT",floor($X-$ExtremityLength).",".floor($PosArray[2]).",".floor($X+$ExtremityLength).",".floor($PosArray[3]),$ImageMapColor,$ImageMapTitle,$Values); } - } - else - { - $this->pChartObject->drawFilledRectangle($X-$ExtremityLength,$PosArray[2],$X+$ExtremityLength,$PosArray[2]-$ExtremityWidth,$ExtremitySettings); - $this->pChartObject->drawFilledRectangle($X-$ExtremityLength,$PosArray[3],$X+$ExtremityLength,$PosArray[3]+$ExtremityWidth,$ExtremitySettings); - - if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("RECT",floor($X-$ExtremityLength).",".floor($PosArray[2]-$ExtremityWidth).",".floor($X+$ExtremityLength).",".floor($PosArray[3]+$ExtremityWidth),$ImageMapColor,$ImageMapTitle,$Values); } - } - - if ( $ShadowOnBoxesOnly ) { $this->pChartObject->Shadow = $RestoreShadow; } - - if ( $PosArray[0] > $PosArray[1] ) - $this->pChartObject->drawFilledRectangle($X-$BoxOffset,$PosArray[0],$X+$BoxOffset,$PosArray[1],$BoxUpSettings); - else - $this->pChartObject->drawFilledRectangle($X-$BoxOffset,$PosArray[0],$X+$BoxOffset,$PosArray[1],$BoxDownSettings); - - if ( isset($PosArray[4]) ) - $this->pChartObject->drawLine($X-$ExtremityLength,$PosArray[4],$X+$ExtremityLength,$PosArray[4],$MedianSettings); - - $X = $X + $XStep; - } - elseif ( $Data["Orientation"] == SCALE_POS_TOPBOTTOM ) - { - if ( $YZero > $this->pChartObject->GraphAreaX2-1 ) { $YZero = $this->pChartObject->GraphAreaX2-1; } - if ( $YZero < $this->pChartObject->GraphAreaX1+1 ) { $YZero = $this->pChartObject->GraphAreaX1+1; } - - if ( $XDivs == 0 ) { $XStep = 0; } else { $XStep = ($this->pChartObject->GraphAreaY2-$this->pChartObject->GraphAreaY1-$XMargin*2)/$XDivs; } - - if ( $LineWidth == 1 ) - $this->pChartObject->drawLine($PosArray[2],$Y,$PosArray[3],$Y,$LineSettings); - else - $this->pChartObject->drawFilledRectangle($PosArray[2],$Y-$LineOffset,$PosArray[3],$Y+$LineOffset,$LineSettings); - - if ( $ShadowOnBoxesOnly ) { $RestoreShadow = $this->pChartObject->Shadow; $this->pChartObject->Shadow = FALSE; } - - if ( $ExtremityWidth == 1 ) - { - $this->pChartObject->drawLine($PosArray[2],$Y-$ExtremityLength,$PosArray[2],$Y+$ExtremityLength,$ExtremitySettings); - $this->pChartObject->drawLine($PosArray[3],$Y-$ExtremityLength,$PosArray[3],$Y+$ExtremityLength,$ExtremitySettings); - - if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("RECT",floor($PosArray[2]).",".floor($Y-$ExtremityLength).",".floor($PosArray[3]).",".floor($Y+$ExtremityLength),$ImageMapColor,$ImageMapTitle,$Values); } - } - else - { - $this->pChartObject->drawFilledRectangle($PosArray[2],$Y-$ExtremityLength,$PosArray[2]-$ExtremityWidth,$Y+$ExtremityLength,$ExtremitySettings); - $this->pChartObject->drawFilledRectangle($PosArray[3],$Y-$ExtremityLength,$PosArray[3]+$ExtremityWidth,$Y+$ExtremityLength,$ExtremitySettings); - - if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("RECT",floor($PosArray[2]-$ExtremityWidth).",".floor($Y-$ExtremityLength).",".floor($PosArray[3]+$ExtremityWidth).",".floor($Y+$ExtremityLength),$ImageMapColor,$ImageMapTitle,$Values); } - } - - if ( $ShadowOnBoxesOnly ) { $this->pChartObject->Shadow = $RestoreShadow; } - - if ( $PosArray[0] < $PosArray[1] ) - $this->pChartObject->drawFilledRectangle($PosArray[0],$Y-$BoxOffset,$PosArray[1],$Y+$BoxOffset,$BoxUpSettings); - else - $this->pChartObject->drawFilledRectangle($PosArray[0],$Y-$BoxOffset,$PosArray[1],$Y+$BoxOffset,$BoxDownSettings); - - if ( isset($PosArray[4]) ) - $this->pChartObject->drawLine($PosArray[4],$Y-$ExtremityLength,$PosArray[4],$Y+$ExtremityLength,$MedianSettings); - - $Y = $Y + $XStep; - } - } - } - } +pChartObject = $pChartObject; + $this->pDataObject = $pDataObject; + } + + /* Draw a stock chart */ + function drawStockChart($Format="") + { + $SerieOpen = isset($Format["SerieOpen"]) ? $Format["SerieOpen"] : "Open"; + $SerieClose = isset($Format["SerieClose"]) ? $Format["SerieClose"] : "Close"; + $SerieMin = isset($Format["SerieMin"]) ? $Format["SerieMin"] : "Min"; + $SerieMax = isset($Format["SerieMax"]) ? $Format["SerieMax"] : "Max"; + $SerieMedian = isset($Format["SerieMedian"]) ? $Format["SerieMedian"] : NULL; + $LineWidth = isset($Format["LineWidth"]) ? $Format["LineWidth"] : 1; + $LineR = isset($Format["LineR"]) ? $Format["LineR"] : 0; + $LineG = isset($Format["LineG"]) ? $Format["LineG"] : 0; + $LineB = isset($Format["LineB"]) ? $Format["LineB"] : 0; + $LineAlpha = isset($Format["LineAlpha"]) ? $Format["LineAlpha"] : 100; + $ExtremityWidth = isset($Format["ExtremityWidth"]) ? $Format["ExtremityWidth"] : 1; + $ExtremityLength = isset($Format["ExtremityLength"]) ? $Format["ExtremityLength"] : 3; + $ExtremityR = isset($Format["ExtremityR"]) ? $Format["ExtremityR"] : 0; + $ExtremityG = isset($Format["ExtremityG"]) ? $Format["ExtremityG"] : 0; + $ExtremityB = isset($Format["ExtremityB"]) ? $Format["ExtremityB"] : 0; + $ExtremityAlpha = isset($Format["ExtremityAlpha"]) ? $Format["ExtremityAlpha"] : 100; + $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 8; + $BoxUpR = isset($Format["BoxUpR"]) ? $Format["BoxUpR"] : 188; + $BoxUpG = isset($Format["BoxUpG"]) ? $Format["BoxUpG"] : 224; + $BoxUpB = isset($Format["BoxUpB"]) ? $Format["BoxUpB"] : 46; + $BoxUpAlpha = isset($Format["BoxUpAlpha"]) ? $Format["BoxUpAlpha"] : 100; + $BoxUpSurrounding = isset($Format["BoxUpSurrounding"]) ? $Format["BoxUpSurrounding"] : NULL; + $BoxUpBorderR = isset($Format["BoxUpBorderR"]) ? $Format["BoxUpBorderR"] : $BoxUpR-20; + $BoxUpBorderG = isset($Format["BoxUpBorderG"]) ? $Format["BoxUpBorderG"] : $BoxUpG-20; + $BoxUpBorderB = isset($Format["BoxUpBorderB"]) ? $Format["BoxUpBorderB"] : $BoxUpB-20; + $BoxUpBorderAlpha = isset($Format["BoxUpBorderAlpha"]) ? $Format["BoxUpBorderAlpha"] : 100; + $BoxDownR = isset($Format["BoxDownR"]) ? $Format["BoxDownR"] : 224; + $BoxDownG = isset($Format["BoxDownG"]) ? $Format["BoxDownG"] : 100; + $BoxDownB = isset($Format["BoxDownB"]) ? $Format["BoxDownB"] : 46; + $BoxDownAlpha = isset($Format["BoxDownAlpha"]) ? $Format["BoxDownAlpha"] : 100; + $BoxDownSurrounding= isset($Format["BoxDownSurrounding"]) ? $Format["BoxDownSurrounding"] : NULL; + $BoxDownBorderR = isset($Format["BoxDownBorderR"]) ? $Format["BoxDownBorderR"] : $BoxDownR-20; + $BoxDownBorderG = isset($Format["BoxDownBorderG"]) ? $Format["BoxDownBorderG"] : $BoxDownG-20; + $BoxDownBorderB = isset($Format["BoxDownBorderB"]) ? $Format["BoxDownBorderB"] : $BoxDownB-20; + $BoxDownBorderAlpha= isset($Format["BoxDownBorderAlpha"]) ? $Format["BoxDownBorderAlpha"] : 100; + $ShadowOnBoxesOnly = isset($Format["ShadowOnBoxesOnly"]) ? $Format["ShadowOnBoxesOnly"] : TRUE; + $MedianR = isset($Format["MedianR"]) ? $Format["MedianR"] : 255; + $MedianG = isset($Format["MedianG"]) ? $Format["MedianG"] : 0; + $MedianB = isset($Format["MedianB"]) ? $Format["MedianB"] : 0; + $MedianAlpha = isset($Format["MedianAlpha"]) ? $Format["MedianAlpha"] : 100; + $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE; + $ImageMapTitle = isset($Format["ImageMapTitle"]) ? $Format["ImageMapTitle"] : "Stock Chart"; + + + /* Data Processing */ + $Data = $this->pDataObject->getData(); + $Palette = $this->pDataObject->getPalette(); + + if ( $BoxUpSurrounding != NULL ) { $BoxUpBorderR = $BoxUpR + $BoxUpSurrounding; $BoxUpBorderG = $BoxUpG + $BoxUpSurrounding; $BoxUpBorderB = $BoxUpB + $BoxUpSurrounding; } + if ( $BoxDownSurrounding != NULL ) { $BoxDownBorderR = $BoxDownR + $BoxDownSurrounding; $BoxDownBorderG = $BoxDownG + $BoxDownSurrounding; $BoxDownBorderB = $BoxDownB + $BoxDownSurrounding; } + + if ( $LineWidth != 1 ) { $LineOffset = $LineWidth / 2; } + $BoxOffset = $BoxWidth / 2; + + $Data = $this->pChartObject->DataSet->getData(); + list($XMargin,$XDivs) = $this->pChartObject->scaleGetXSettings(); + + if ( !isset($Data["Series"][$SerieOpen]) || !isset($Data["Series"][$SerieClose]) || !isset($Data["Series"][$SerieMin]) || !isset($Data["Series"][$SerieMax]) ) + return(STOCK_MISSING_SERIE); + + $Plots = ""; + foreach($Data["Series"][$SerieOpen]["Data"] as $Key => $Value) + { + $Point = ""; + if ( isset($Data["Series"][$SerieClose]["Data"][$Key]) || isset($Data["Series"][$SerieMin]["Data"][$Key]) || isset($Data["Series"][$SerieMax]["Data"][$Key]) ) + $Point = array($Value,$Data["Series"][$SerieClose]["Data"][$Key],$Data["Series"][$SerieMin]["Data"][$Key],$Data["Series"][$SerieMax]["Data"][$Key]); + if ( $SerieMedian != NULL && isset($Data["Series"][$SerieMedian]["Data"][$Key]) ) + $Point[] = $Data["Series"][$SerieMedian]["Data"][$Key]; + + $Plots[] = $Point; + } + + $AxisID = $Data["Series"][$SerieOpen]["Axis"]; + $Mode = $Data["Axis"][$AxisID]["Display"]; + $Format = $Data["Axis"][$AxisID]["Format"]; + $Unit = $Data["Axis"][$AxisID]["Unit"]; + + $YZero = $this->pChartObject->scaleComputeY(0,array("AxisID"=>$AxisID)); + $XStep = ($this->pChartObject->GraphAreaX2-$this->pChartObject->GraphAreaX1-$XMargin*2)/$XDivs; + + $X = $this->pChartObject->GraphAreaX1 + $XMargin; + $Y = $this->pChartObject->GraphAreaY1 + $XMargin; + + $LineSettings = array("R"=>$LineR,"G"=>$LineG,"B"=>$LineB,"Alpha"=>$LineAlpha); + $ExtremitySettings = array("R"=>$ExtremityR,"G"=>$ExtremityG,"B"=>$ExtremityB,"Alpha"=>$ExtremityAlpha); + $BoxUpSettings = array("R"=>$BoxUpR,"G"=>$BoxUpG,"B"=>$BoxUpB,"Alpha"=>$BoxUpAlpha,"BorderR"=>$BoxUpBorderR,"BorderG"=>$BoxUpBorderG,"BorderB"=>$BoxUpBorderB,"BorderAlpha"=>$BoxUpBorderAlpha); + $BoxDownSettings = array("R"=>$BoxDownR,"G"=>$BoxDownG,"B"=>$BoxDownB,"Alpha"=>$BoxDownAlpha,"BorderR"=>$BoxDownBorderR,"BorderG"=>$BoxDownBorderG,"BorderB"=>$BoxDownBorderB,"BorderAlpha"=>$BoxDownBorderAlpha); + $MedianSettings = array("R"=>$MedianR,"G"=>$MedianG,"B"=>$MedianB,"Alpha"=>$MedianAlpha); + + foreach($Plots as $Key =>$Points) + { + $PosArray = $this->pChartObject->scaleComputeY($Points,array("AxisID"=>$AxisID)); + + $Values = "Open :".$Data["Series"][$SerieOpen]["Data"][$Key]."
Close : ".$Data["Series"][$SerieClose]["Data"][$Key]."
Min : ".$Data["Series"][$SerieMin]["Data"][$Key]."
Max : ".$Data["Series"][$SerieMax]["Data"][$Key]."
"; + if ( $SerieMedian != NULL ) { $Values = $Values."Median : ".$Data["Series"][$SerieMedian]["Data"][$Key]."
"; } + if ( $PosArray[0] > $PosArray[1] ) { $ImageMapColor = $this->pChartObject->toHTMLColor($BoxUpR,$BoxUpG,$BoxUpB); } else { $ImageMapColor = $this->pChartObject->toHTMLColor($BoxDownR,$BoxDownG,$BoxDownB); } + + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) + { + if ( $YZero > $this->pChartObject->GraphAreaY2-1 ) { $YZero = $this->pChartObject->GraphAreaY2-1; } + if ( $YZero < $this->pChartObject->GraphAreaY1+1 ) { $YZero = $this->pChartObject->GraphAreaY1+1; } + + if ( $XDivs == 0 ) { $XStep = 0; } else { $XStep = ($this->pChartObject->GraphAreaX2-$this->pChartObject->GraphAreaX1-$XMargin*2)/$XDivs; } + + if ( $ShadowOnBoxesOnly ) { $RestoreShadow = $this->pChartObject->Shadow; $this->pChartObject->Shadow = FALSE; } + + if ( $LineWidth == 1 ) + $this->pChartObject->drawLine($X,$PosArray[2],$X,$PosArray[3],$LineSettings); + else + $this->pChartObject->drawFilledRectangle($X-$LineOffset,$PosArray[2],$X+$LineOffset,$PosArray[3],$LineSettings); + + if ( $ExtremityWidth == 1 ) + { + $this->pChartObject->drawLine($X-$ExtremityLength,$PosArray[2],$X+$ExtremityLength,$PosArray[2],$ExtremitySettings); + $this->pChartObject->drawLine($X-$ExtremityLength,$PosArray[3],$X+$ExtremityLength,$PosArray[3],$ExtremitySettings); + + if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("RECT",floor($X-$ExtremityLength).",".floor($PosArray[2]).",".floor($X+$ExtremityLength).",".floor($PosArray[3]),$ImageMapColor,$ImageMapTitle,$Values); } + } + else + { + $this->pChartObject->drawFilledRectangle($X-$ExtremityLength,$PosArray[2],$X+$ExtremityLength,$PosArray[2]-$ExtremityWidth,$ExtremitySettings); + $this->pChartObject->drawFilledRectangle($X-$ExtremityLength,$PosArray[3],$X+$ExtremityLength,$PosArray[3]+$ExtremityWidth,$ExtremitySettings); + + if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("RECT",floor($X-$ExtremityLength).",".floor($PosArray[2]-$ExtremityWidth).",".floor($X+$ExtremityLength).",".floor($PosArray[3]+$ExtremityWidth),$ImageMapColor,$ImageMapTitle,$Values); } + } + + if ( $ShadowOnBoxesOnly ) { $this->pChartObject->Shadow = $RestoreShadow; } + + if ( $PosArray[0] > $PosArray[1] ) + $this->pChartObject->drawFilledRectangle($X-$BoxOffset,$PosArray[0],$X+$BoxOffset,$PosArray[1],$BoxUpSettings); + else + $this->pChartObject->drawFilledRectangle($X-$BoxOffset,$PosArray[0],$X+$BoxOffset,$PosArray[1],$BoxDownSettings); + + if ( isset($PosArray[4]) ) + $this->pChartObject->drawLine($X-$ExtremityLength,$PosArray[4],$X+$ExtremityLength,$PosArray[4],$MedianSettings); + + $X = $X + $XStep; + } + elseif ( $Data["Orientation"] == SCALE_POS_TOPBOTTOM ) + { + if ( $YZero > $this->pChartObject->GraphAreaX2-1 ) { $YZero = $this->pChartObject->GraphAreaX2-1; } + if ( $YZero < $this->pChartObject->GraphAreaX1+1 ) { $YZero = $this->pChartObject->GraphAreaX1+1; } + + if ( $XDivs == 0 ) { $XStep = 0; } else { $XStep = ($this->pChartObject->GraphAreaY2-$this->pChartObject->GraphAreaY1-$XMargin*2)/$XDivs; } + + if ( $LineWidth == 1 ) + $this->pChartObject->drawLine($PosArray[2],$Y,$PosArray[3],$Y,$LineSettings); + else + $this->pChartObject->drawFilledRectangle($PosArray[2],$Y-$LineOffset,$PosArray[3],$Y+$LineOffset,$LineSettings); + + if ( $ShadowOnBoxesOnly ) { $RestoreShadow = $this->pChartObject->Shadow; $this->pChartObject->Shadow = FALSE; } + + if ( $ExtremityWidth == 1 ) + { + $this->pChartObject->drawLine($PosArray[2],$Y-$ExtremityLength,$PosArray[2],$Y+$ExtremityLength,$ExtremitySettings); + $this->pChartObject->drawLine($PosArray[3],$Y-$ExtremityLength,$PosArray[3],$Y+$ExtremityLength,$ExtremitySettings); + + if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("RECT",floor($PosArray[2]).",".floor($Y-$ExtremityLength).",".floor($PosArray[3]).",".floor($Y+$ExtremityLength),$ImageMapColor,$ImageMapTitle,$Values); } + } + else + { + $this->pChartObject->drawFilledRectangle($PosArray[2],$Y-$ExtremityLength,$PosArray[2]-$ExtremityWidth,$Y+$ExtremityLength,$ExtremitySettings); + $this->pChartObject->drawFilledRectangle($PosArray[3],$Y-$ExtremityLength,$PosArray[3]+$ExtremityWidth,$Y+$ExtremityLength,$ExtremitySettings); + + if ( $RecordImageMap ) { $this->pChartObject->addToImageMap("RECT",floor($PosArray[2]-$ExtremityWidth).",".floor($Y-$ExtremityLength).",".floor($PosArray[3]+$ExtremityWidth).",".floor($Y+$ExtremityLength),$ImageMapColor,$ImageMapTitle,$Values); } + } + + if ( $ShadowOnBoxesOnly ) { $this->pChartObject->Shadow = $RestoreShadow; } + + if ( $PosArray[0] < $PosArray[1] ) + $this->pChartObject->drawFilledRectangle($PosArray[0],$Y-$BoxOffset,$PosArray[1],$Y+$BoxOffset,$BoxUpSettings); + else + $this->pChartObject->drawFilledRectangle($PosArray[0],$Y-$BoxOffset,$PosArray[1],$Y+$BoxOffset,$BoxDownSettings); + + if ( isset($PosArray[4]) ) + $this->pChartObject->drawLine($PosArray[4],$Y-$ExtremityLength,$PosArray[4],$Y+$ExtremityLength,$MedianSettings); + + $Y = $Y + $XStep; + } + } + } + } ?> \ No newline at end of file diff --git a/www/libs/pChart2.1.3/class/pSurface.class.php b/www/libs/pChart2.1.4/class/pSurface.class.php similarity index 97% rename from www/libs/pChart2.1.3/class/pSurface.class.php rename to www/libs/pChart2.1.4/class/pSurface.class.php index 0aeb9b97..bace49a4 100644 --- a/www/libs/pChart2.1.3/class/pSurface.class.php +++ b/www/libs/pChart2.1.4/class/pSurface.class.php @@ -1,315 +1,315 @@ -pChartObject = $pChartObject; - $this->GridSize = 10; - $this->Points = ""; - } - - /* Define the grid size and initialise the 2D matrix */ - function setGrid($XSize=10,$YSize=10) - { - for($X=0; $X<=$XSize; $X++) { for($Y=0; $Y<=$YSize; $Y++) { $this->Points[$X][$Y]=UNKNOWN; } } - - $this->GridSizeX = $XSize; - $this->GridSizeY = $YSize; - } - - /* Add a point on the grid */ - function addPoint($X,$Y,$Value,$Force=TRUE) - { - if ( $X < 0 || $X >$this->GridSizeX ) { return(0); } - if ( $Y < 0 || $Y >$this->GridSizeY ) { return(0); } - - if ( $this->Points[$X][$Y] == UNKNOWN || $Force ) - $this->Points[$X][$Y] = $Value; - elseif ( $this->Points[$X][$Y] == UNKNOWN ) - $this->Points[$X][$Y] = $Value; - else - $this->Points[$X][$Y] = ($this->Points[$X][$Y] + $Value)/2; - } - - /* Write the X labels */ - function writeXLabels($Format="") - { - $R = isset($Format["R"]) ? $Format["R"] : $this->pChartObject->FontColorR; - $G = isset($Format["G"]) ? $Format["G"] : $this->pChartObject->FontColorG; - $B = isset($Format["B"]) ? $Format["B"] : $this->pChartObject->FontColorB; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : $this->pChartObject->FontColorA; - $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0; - $Padding = isset($Format["Padding"]) ? $Format["Padding"] : 5; - $Position = isset($Format["Position"]) ? $Format["Position"] : LABEL_POSITION_TOP; - $Labels = isset($Format["Labels"]) ? $Format["Labels"] : NULL; - $CountOffset = isset($Format["CountOffset"]) ? $Format["CountOffset"] : 0; - - if ( $Labels != NULL && !is_array($Labels) ) { $Label = $Labels; $Labels = ""; $Labels[] = $Label; } - - $X0 = $this->pChartObject->GraphAreaX1; - $XSize = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1) / ($this->GridSizeX+1); - - $Settings = array("Angle"=>$Angle,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); - if ( $Position == LABEL_POSITION_TOP ) - { - $YPos = $this->pChartObject->GraphAreaY1 - $Padding; - if ($Angle == 0 ) { $Settings["Align"] = TEXT_ALIGN_BOTTOMMIDDLE; } - if ($Angle != 0 ) { $Settings["Align"] = TEXT_ALIGN_MIDDLELEFT; } - } - elseif ( $Position == LABEL_POSITION_BOTTOM ) - { - $YPos = $this->pChartObject->GraphAreaY2 + $Padding; - if ($Angle == 0 ) { $Settings["Align"] = TEXT_ALIGN_TOPMIDDLE; } - if ($Angle != 0 ) { $Settings["Align"] = TEXT_ALIGN_MIDDLERIGHT; } - } - else - return(-1); - - for($X=0;$X<=$this->GridSizeX;$X++) - { - $XPos = floor($X0+$X*$XSize + $XSize/2); - - if( $Labels == NULL || !isset($Labels[$X]) ) - $Value = $X+$CountOffset; - else - $Value = $Labels[$X]; - - $this->pChartObject->drawText($XPos,$YPos,$Value,$Settings); - } - } - - /* Write the Y labels */ - function writeYLabels($Format="") - { - $R = isset($Format["R"]) ? $Format["R"] : $this->pChartObject->FontColorR; - $G = isset($Format["G"]) ? $Format["G"] : $this->pChartObject->FontColorG; - $B = isset($Format["B"]) ? $Format["B"] : $this->pChartObject->FontColorB; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : $this->pChartObject->FontColorA; - $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0; - $Padding = isset($Format["Padding"]) ? $Format["Padding"] : 5; - $Position = isset($Format["Position"]) ? $Format["Position"] : LABEL_POSITION_LEFT; - $Labels = isset($Format["Labels"]) ? $Format["Labels"] : NULL; - $CountOffset = isset($Format["CountOffset"]) ? $Format["CountOffset"] : 0; - - if ( $Labels != NULL && !is_array($Labels) ) { $Label = $Labels; $Labels = ""; $Labels[] = $Label; } - - $Y0 = $this->pChartObject->GraphAreaY1; - $YSize = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1) / ($this->GridSizeY+1); - - $Settings = array("Angle"=>$Angle,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); - if ( $Position == LABEL_POSITION_LEFT ) - { $XPos = $this->pChartObject->GraphAreaX1 - $Padding; $Settings["Align"] = TEXT_ALIGN_MIDDLERIGHT; } - elseif ( $Position == LABEL_POSITION_RIGHT ) - { $XPos = $this->pChartObject->GraphAreaX2 + $Padding; $Settings["Align"] = TEXT_ALIGN_MIDDLELEFT; } - else - return(-1); - - for($Y=0;$Y<=$this->GridSizeY;$Y++) - { - $YPos = floor($Y0+$Y*$YSize + $YSize/2); - - if( $Labels == NULL || !isset($Labels[$Y]) ) - $Value = $Y+$CountOffset; - else - $Value = $Labels[$Y]; - - $this->pChartObject->drawText($XPos,$YPos,$Value,$Settings); - } - } - - /* Draw the area arround the specified Threshold */ - function drawContour($Threshold,$Format="") - { - $R = isset($Format["R"]) ? $Format["R"] : 0; - $G = isset($Format["G"]) ? $Format["G"] : 0; - $B = isset($Format["B"]) ? $Format["B"] : 0; - $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; - $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : 3; - $Padding = isset($Format["Padding"]) ? $Format["Padding"] : 0; - - $X0 = $this->pChartObject->GraphAreaX1; - $Y0 = $this->pChartObject->GraphAreaY1; - $XSize = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1) / ($this->GridSizeX+1); - $YSize = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1) / ($this->GridSizeY+1); - - $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks); - - for($X=0;$X<=$this->GridSizeX;$X++) - { - for($Y=0;$Y<=$this->GridSizeY;$Y++) - { - $Value = $this->Points[$X][$Y]; - - if ( $Value != UNKNOWN && $Value != IGNORED && $Value >= $Threshold) - { - $X1 = floor($X0+$X*$XSize)+$Padding; - $Y1 = floor($Y0+$Y*$YSize)+$Padding; - $X2 = floor($X0+$X*$XSize+$XSize); - $Y2 = floor($Y0+$Y*$YSize+$YSize); - - if ( $X > 0 && $this->Points[$X-1][$Y] != UNKNOWN && $this->Points[$X-1][$Y] != IGNORED && $this->Points[$X-1][$Y] < $Threshold) - $this->pChartObject->drawLine($X1,$Y1,$X1,$Y2,$Color); - if ( $Y > 0 && $this->Points[$X][$Y-1] != UNKNOWN && $this->Points[$X][$Y-1] != IGNORED && $this->Points[$X][$Y-1] < $Threshold) - $this->pChartObject->drawLine($X1,$Y1,$X2,$Y1,$Color); - if ( $X < $this->GridSizeX && $this->Points[$X+1][$Y] != UNKNOWN && $this->Points[$X+1][$Y] != IGNORED && $this->Points[$X+1][$Y] < $Threshold) - $this->pChartObject->drawLine($X2,$Y1,$X2,$Y2,$Color); - if ( $Y < $this->GridSizeY && $this->Points[$X][$Y+1] != UNKNOWN && $this->Points[$X][$Y+1] != IGNORED && $this->Points[$X][$Y+1] < $Threshold) - $this->pChartObject->drawLine($X1,$Y2,$X2,$Y2,$Color); - } - } - } - } - - /* Draw the surface chart */ - function drawSurface($Format="") - { - $Palette = isset($Format["Palette"]) ? $Format["Palette"] : NULL; - $ShadeR1 = isset($Format["ShadeR1"]) ? $Format["ShadeR1"] : 77; - $ShadeG1 = isset($Format["ShadeG1"]) ? $Format["ShadeG1"] : 205; - $ShadeB1 = isset($Format["ShadeB1"]) ? $Format["ShadeB1"] : 21; - $ShadeA1 = isset($Format["ShadeA1"]) ? $Format["ShadeA1"] : 40; - $ShadeR2 = isset($Format["ShadeR2"]) ? $Format["ShadeR2"] : 227; - $ShadeG2 = isset($Format["ShadeG2"]) ? $Format["ShadeG2"] : 135; - $ShadeB2 = isset($Format["ShadeB2"]) ? $Format["ShadeB2"] : 61; - $ShadeA2 = isset($Format["ShadeA2"]) ? $Format["ShadeA2"] : 100; - $Border = isset($Format["Border"]) ? $Format["Border"] : FALSE; - $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 0; - $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 0; - $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 0; - $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : -1; - $Padding = isset($Format["Padding"]) ? $Format["Padding"] : 1; - - $X0 = $this->pChartObject->GraphAreaX1; - $Y0 = $this->pChartObject->GraphAreaY1; - $XSize = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1) / ($this->GridSizeX+1); - $YSize = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1) / ($this->GridSizeY+1); - - for($X=0;$X<=$this->GridSizeX;$X++) - { - for($Y=0;$Y<=$this->GridSizeY;$Y++) - { - $Value = $this->Points[$X][$Y]; - - if ( $Value != UNKNOWN && $Value != IGNORED ) - { - $X1 = floor($X0+$X*$XSize)+$Padding; - $Y1 = floor($Y0+$Y*$YSize)+$Padding; - $X2 = floor($X0+$X*$XSize+$XSize); - $Y2 = floor($Y0+$Y*$YSize+$YSize); - - if ( $Palette != NULL ) - { - if ( isset($Palette[$Value]) && isset($Palette[$Value]["R"]) ) { $R = $Palette[$Value]["R"]; } else { $R = 0; } - if ( isset($Palette[$Value]) && isset($Palette[$Value]["G"]) ) { $G = $Palette[$Value]["G"]; } else { $G = 0; } - if ( isset($Palette[$Value]) && isset($Palette[$Value]["B"]) ) { $B = $Palette[$Value]["B"]; } else { $B = 0; } - if ( isset($Palette[$Value]) && isset($Palette[$Value]["Alpha"]) ) { $Alpha = $Palette[$Value]["Alpha"]; } else { $Alpha = 1000; } - } - else - { - $R = (($ShadeR2-$ShadeR1)/100)*$Value + $ShadeR1; - $G = (($ShadeG2-$ShadeG1)/100)*$Value + $ShadeG1; - $B = (($ShadeB2-$ShadeB1)/100)*$Value + $ShadeB1; - $Alpha = (($ShadeA2-$ShadeA1)/100)*$Value + $ShadeA1; - } - - $Settings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); - if ( $Border ) { $Settings["BorderR"] = $BorderR; $Settings["BorderG"] = $BorderG; $Settings["BorderB"] = $BorderB; } - if ( $Surrounding != -1 ) { $Settings["BorderR"] = $R+$Surrounding; $Settings["BorderG"] = $G+$Surrounding; $Settings["BorderB"] = $B+$Surrounding; } - - $this->pChartObject->drawFilledRectangle($X1,$Y1,$X2-1,$Y2-1,$Settings); - } - } - } - } - - /* Compute the missing points */ - function computeMissing() - { - $Missing = ""; - for($X=0;$X<=$this->GridSizeX;$X++) - { - for($Y=0;$Y<=$this->GridSizeY;$Y++) - { - if ( $this->Points[$X][$Y] == UNKNOWN ) - $Missing[] = $X.",".$Y; - } - } - shuffle($Missing); - - foreach($Missing as $Key => $Pos) - { - $Pos = preg_split("/,/",$Pos); - $X = $Pos[0]; - $Y = $Pos[1]; - - if ( $this->Points[$X][$Y] == UNKNOWN ) - { - $NearestNeighbor = $this->getNearestNeighbor($X,$Y); - - $Value = 0; $Points = 0; - for($Xi=$X-$NearestNeighbor;$Xi<=$X+$NearestNeighbor;$Xi++) - { - for($Yi=$Y-$NearestNeighbor;$Yi<=$Y+$NearestNeighbor;$Yi++) - { - if ($Xi >=0 && $Yi >= 0 && $Xi <= $this->GridSizeX && $Yi <= $this->GridSizeY && $this->Points[$Xi][$Yi] != UNKNOWN && $this->Points[$Xi][$Yi] != IGNORED) - { - $Value = $Value + $this->Points[$Xi][$Yi]; $Points++; - } - } - } - - if ( $Points != 0 ) { $this->Points[$X][$Y] = $Value / $Points; } - } - } - } - - /* Return the nearest Neighbor distance of a point */ - function getNearestNeighbor($Xp,$Yp) - { - $Nearest = UNKNOWN; - for($X=0;$X<=$this->GridSizeX;$X++) - { - for($Y=0;$Y<=$this->GridSizeY;$Y++) - { - if ( $this->Points[$X][$Y] != UNKNOWN && $this->Points[$X][$Y] != IGNORED ) - { - $DistanceX = max($Xp,$X)-min($Xp,$X); - $DistanceY = max($Yp,$Y)-min($Yp,$Y); - $Distance = max($DistanceX,$DistanceY); - if ( $Distance < $Nearest || $Nearest == UNKNOWN ) { $Nearest = $Distance; } - } - } - } - return($Nearest); - } - } +pChartObject = $pChartObject; + $this->GridSize = 10; + $this->Points = ""; + } + + /* Define the grid size and initialise the 2D matrix */ + function setGrid($XSize=10,$YSize=10) + { + for($X=0; $X<=$XSize; $X++) { for($Y=0; $Y<=$YSize; $Y++) { $this->Points[$X][$Y]=UNKNOWN; } } + + $this->GridSizeX = $XSize; + $this->GridSizeY = $YSize; + } + + /* Add a point on the grid */ + function addPoint($X,$Y,$Value,$Force=TRUE) + { + if ( $X < 0 || $X >$this->GridSizeX ) { return(0); } + if ( $Y < 0 || $Y >$this->GridSizeY ) { return(0); } + + if ( $this->Points[$X][$Y] == UNKNOWN || $Force ) + $this->Points[$X][$Y] = $Value; + elseif ( $this->Points[$X][$Y] == UNKNOWN ) + $this->Points[$X][$Y] = $Value; + else + $this->Points[$X][$Y] = ($this->Points[$X][$Y] + $Value)/2; + } + + /* Write the X labels */ + function writeXLabels($Format="") + { + $R = isset($Format["R"]) ? $Format["R"] : $this->pChartObject->FontColorR; + $G = isset($Format["G"]) ? $Format["G"] : $this->pChartObject->FontColorG; + $B = isset($Format["B"]) ? $Format["B"] : $this->pChartObject->FontColorB; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : $this->pChartObject->FontColorA; + $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0; + $Padding = isset($Format["Padding"]) ? $Format["Padding"] : 5; + $Position = isset($Format["Position"]) ? $Format["Position"] : LABEL_POSITION_TOP; + $Labels = isset($Format["Labels"]) ? $Format["Labels"] : NULL; + $CountOffset = isset($Format["CountOffset"]) ? $Format["CountOffset"] : 0; + + if ( $Labels != NULL && !is_array($Labels) ) { $Label = $Labels; $Labels = ""; $Labels[] = $Label; } + + $X0 = $this->pChartObject->GraphAreaX1; + $XSize = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1) / ($this->GridSizeX+1); + + $Settings = array("Angle"=>$Angle,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); + if ( $Position == LABEL_POSITION_TOP ) + { + $YPos = $this->pChartObject->GraphAreaY1 - $Padding; + if ($Angle == 0 ) { $Settings["Align"] = TEXT_ALIGN_BOTTOMMIDDLE; } + if ($Angle != 0 ) { $Settings["Align"] = TEXT_ALIGN_MIDDLELEFT; } + } + elseif ( $Position == LABEL_POSITION_BOTTOM ) + { + $YPos = $this->pChartObject->GraphAreaY2 + $Padding; + if ($Angle == 0 ) { $Settings["Align"] = TEXT_ALIGN_TOPMIDDLE; } + if ($Angle != 0 ) { $Settings["Align"] = TEXT_ALIGN_MIDDLERIGHT; } + } + else + return(-1); + + for($X=0;$X<=$this->GridSizeX;$X++) + { + $XPos = floor($X0+$X*$XSize + $XSize/2); + + if( $Labels == NULL || !isset($Labels[$X]) ) + $Value = $X+$CountOffset; + else + $Value = $Labels[$X]; + + $this->pChartObject->drawText($XPos,$YPos,$Value,$Settings); + } + } + + /* Write the Y labels */ + function writeYLabels($Format="") + { + $R = isset($Format["R"]) ? $Format["R"] : $this->pChartObject->FontColorR; + $G = isset($Format["G"]) ? $Format["G"] : $this->pChartObject->FontColorG; + $B = isset($Format["B"]) ? $Format["B"] : $this->pChartObject->FontColorB; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : $this->pChartObject->FontColorA; + $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0; + $Padding = isset($Format["Padding"]) ? $Format["Padding"] : 5; + $Position = isset($Format["Position"]) ? $Format["Position"] : LABEL_POSITION_LEFT; + $Labels = isset($Format["Labels"]) ? $Format["Labels"] : NULL; + $CountOffset = isset($Format["CountOffset"]) ? $Format["CountOffset"] : 0; + + if ( $Labels != NULL && !is_array($Labels) ) { $Label = $Labels; $Labels = ""; $Labels[] = $Label; } + + $Y0 = $this->pChartObject->GraphAreaY1; + $YSize = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1) / ($this->GridSizeY+1); + + $Settings = array("Angle"=>$Angle,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); + if ( $Position == LABEL_POSITION_LEFT ) + { $XPos = $this->pChartObject->GraphAreaX1 - $Padding; $Settings["Align"] = TEXT_ALIGN_MIDDLERIGHT; } + elseif ( $Position == LABEL_POSITION_RIGHT ) + { $XPos = $this->pChartObject->GraphAreaX2 + $Padding; $Settings["Align"] = TEXT_ALIGN_MIDDLELEFT; } + else + return(-1); + + for($Y=0;$Y<=$this->GridSizeY;$Y++) + { + $YPos = floor($Y0+$Y*$YSize + $YSize/2); + + if( $Labels == NULL || !isset($Labels[$Y]) ) + $Value = $Y+$CountOffset; + else + $Value = $Labels[$Y]; + + $this->pChartObject->drawText($XPos,$YPos,$Value,$Settings); + } + } + + /* Draw the area arround the specified Threshold */ + function drawContour($Threshold,$Format="") + { + $R = isset($Format["R"]) ? $Format["R"] : 0; + $G = isset($Format["G"]) ? $Format["G"] : 0; + $B = isset($Format["B"]) ? $Format["B"] : 0; + $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100; + $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : 3; + $Padding = isset($Format["Padding"]) ? $Format["Padding"] : 0; + + $X0 = $this->pChartObject->GraphAreaX1; + $Y0 = $this->pChartObject->GraphAreaY1; + $XSize = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1) / ($this->GridSizeX+1); + $YSize = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1) / ($this->GridSizeY+1); + + $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks); + + for($X=0;$X<=$this->GridSizeX;$X++) + { + for($Y=0;$Y<=$this->GridSizeY;$Y++) + { + $Value = $this->Points[$X][$Y]; + + if ( $Value != UNKNOWN && $Value != IGNORED && $Value >= $Threshold) + { + $X1 = floor($X0+$X*$XSize)+$Padding; + $Y1 = floor($Y0+$Y*$YSize)+$Padding; + $X2 = floor($X0+$X*$XSize+$XSize); + $Y2 = floor($Y0+$Y*$YSize+$YSize); + + if ( $X > 0 && $this->Points[$X-1][$Y] != UNKNOWN && $this->Points[$X-1][$Y] != IGNORED && $this->Points[$X-1][$Y] < $Threshold) + $this->pChartObject->drawLine($X1,$Y1,$X1,$Y2,$Color); + if ( $Y > 0 && $this->Points[$X][$Y-1] != UNKNOWN && $this->Points[$X][$Y-1] != IGNORED && $this->Points[$X][$Y-1] < $Threshold) + $this->pChartObject->drawLine($X1,$Y1,$X2,$Y1,$Color); + if ( $X < $this->GridSizeX && $this->Points[$X+1][$Y] != UNKNOWN && $this->Points[$X+1][$Y] != IGNORED && $this->Points[$X+1][$Y] < $Threshold) + $this->pChartObject->drawLine($X2,$Y1,$X2,$Y2,$Color); + if ( $Y < $this->GridSizeY && $this->Points[$X][$Y+1] != UNKNOWN && $this->Points[$X][$Y+1] != IGNORED && $this->Points[$X][$Y+1] < $Threshold) + $this->pChartObject->drawLine($X1,$Y2,$X2,$Y2,$Color); + } + } + } + } + + /* Draw the surface chart */ + function drawSurface($Format="") + { + $Palette = isset($Format["Palette"]) ? $Format["Palette"] : NULL; + $ShadeR1 = isset($Format["ShadeR1"]) ? $Format["ShadeR1"] : 77; + $ShadeG1 = isset($Format["ShadeG1"]) ? $Format["ShadeG1"] : 205; + $ShadeB1 = isset($Format["ShadeB1"]) ? $Format["ShadeB1"] : 21; + $ShadeA1 = isset($Format["ShadeA1"]) ? $Format["ShadeA1"] : 40; + $ShadeR2 = isset($Format["ShadeR2"]) ? $Format["ShadeR2"] : 227; + $ShadeG2 = isset($Format["ShadeG2"]) ? $Format["ShadeG2"] : 135; + $ShadeB2 = isset($Format["ShadeB2"]) ? $Format["ShadeB2"] : 61; + $ShadeA2 = isset($Format["ShadeA2"]) ? $Format["ShadeA2"] : 100; + $Border = isset($Format["Border"]) ? $Format["Border"] : FALSE; + $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 0; + $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 0; + $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 0; + $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : -1; + $Padding = isset($Format["Padding"]) ? $Format["Padding"] : 1; + + $X0 = $this->pChartObject->GraphAreaX1; + $Y0 = $this->pChartObject->GraphAreaY1; + $XSize = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1) / ($this->GridSizeX+1); + $YSize = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1) / ($this->GridSizeY+1); + + for($X=0;$X<=$this->GridSizeX;$X++) + { + for($Y=0;$Y<=$this->GridSizeY;$Y++) + { + $Value = $this->Points[$X][$Y]; + + if ( $Value != UNKNOWN && $Value != IGNORED ) + { + $X1 = floor($X0+$X*$XSize)+$Padding; + $Y1 = floor($Y0+$Y*$YSize)+$Padding; + $X2 = floor($X0+$X*$XSize+$XSize); + $Y2 = floor($Y0+$Y*$YSize+$YSize); + + if ( $Palette != NULL ) + { + if ( isset($Palette[$Value]) && isset($Palette[$Value]["R"]) ) { $R = $Palette[$Value]["R"]; } else { $R = 0; } + if ( isset($Palette[$Value]) && isset($Palette[$Value]["G"]) ) { $G = $Palette[$Value]["G"]; } else { $G = 0; } + if ( isset($Palette[$Value]) && isset($Palette[$Value]["B"]) ) { $B = $Palette[$Value]["B"]; } else { $B = 0; } + if ( isset($Palette[$Value]) && isset($Palette[$Value]["Alpha"]) ) { $Alpha = $Palette[$Value]["Alpha"]; } else { $Alpha = 1000; } + } + else + { + $R = (($ShadeR2-$ShadeR1)/100)*$Value + $ShadeR1; + $G = (($ShadeG2-$ShadeG1)/100)*$Value + $ShadeG1; + $B = (($ShadeB2-$ShadeB1)/100)*$Value + $ShadeB1; + $Alpha = (($ShadeA2-$ShadeA1)/100)*$Value + $ShadeA1; + } + + $Settings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha); + if ( $Border ) { $Settings["BorderR"] = $BorderR; $Settings["BorderG"] = $BorderG; $Settings["BorderB"] = $BorderB; } + if ( $Surrounding != -1 ) { $Settings["BorderR"] = $R+$Surrounding; $Settings["BorderG"] = $G+$Surrounding; $Settings["BorderB"] = $B+$Surrounding; } + + $this->pChartObject->drawFilledRectangle($X1,$Y1,$X2-1,$Y2-1,$Settings); + } + } + } + } + + /* Compute the missing points */ + function computeMissing() + { + $Missing = ""; + for($X=0;$X<=$this->GridSizeX;$X++) + { + for($Y=0;$Y<=$this->GridSizeY;$Y++) + { + if ( $this->Points[$X][$Y] == UNKNOWN ) + $Missing[] = $X.",".$Y; + } + } + shuffle($Missing); + + foreach($Missing as $Key => $Pos) + { + $Pos = preg_split("/,/",$Pos); + $X = $Pos[0]; + $Y = $Pos[1]; + + if ( $this->Points[$X][$Y] == UNKNOWN ) + { + $NearestNeighbor = $this->getNearestNeighbor($X,$Y); + + $Value = 0; $Points = 0; + for($Xi=$X-$NearestNeighbor;$Xi<=$X+$NearestNeighbor;$Xi++) + { + for($Yi=$Y-$NearestNeighbor;$Yi<=$Y+$NearestNeighbor;$Yi++) + { + if ($Xi >=0 && $Yi >= 0 && $Xi <= $this->GridSizeX && $Yi <= $this->GridSizeY && $this->Points[$Xi][$Yi] != UNKNOWN && $this->Points[$Xi][$Yi] != IGNORED) + { + $Value = $Value + $this->Points[$Xi][$Yi]; $Points++; + } + } + } + + if ( $Points != 0 ) { $this->Points[$X][$Y] = $Value / $Points; } + } + } + } + + /* Return the nearest Neighbor distance of a point */ + function getNearestNeighbor($Xp,$Yp) + { + $Nearest = UNKNOWN; + for($X=0;$X<=$this->GridSizeX;$X++) + { + for($Y=0;$Y<=$this->GridSizeY;$Y++) + { + if ( $this->Points[$X][$Y] != UNKNOWN && $this->Points[$X][$Y] != IGNORED ) + { + $DistanceX = max($Xp,$X)-min($Xp,$X); + $DistanceY = max($Yp,$Y)-min($Yp,$Y); + $Distance = max($DistanceX,$DistanceY); + if ( $Distance < $Nearest || $Nearest == UNKNOWN ) { $Nearest = $Distance; } + } + } + } + return($Nearest); + } + } ?> \ No newline at end of file diff --git a/www/libs/pChart2.1.3/data/128B.db b/www/libs/pChart2.1.4/data/128B.db similarity index 100% rename from www/libs/pChart2.1.3/data/128B.db rename to www/libs/pChart2.1.4/data/128B.db diff --git a/www/libs/pChart2.1.3/data/39.db b/www/libs/pChart2.1.4/data/39.db similarity index 100% rename from www/libs/pChart2.1.3/data/39.db rename to www/libs/pChart2.1.4/data/39.db diff --git a/www/libs/pChart2.1.3/fonts/Bedizen.ttf b/www/libs/pChart2.1.4/fonts/Bedizen.ttf similarity index 100% rename from www/libs/pChart2.1.3/fonts/Bedizen.ttf rename to www/libs/pChart2.1.4/fonts/Bedizen.ttf diff --git a/www/libs/pChart2.1.3/fonts/Forgotte.ttf b/www/libs/pChart2.1.4/fonts/Forgotte.ttf similarity index 100% rename from www/libs/pChart2.1.3/fonts/Forgotte.ttf rename to www/libs/pChart2.1.4/fonts/Forgotte.ttf diff --git a/www/libs/pChart2.1.3/fonts/GeosansLight.ttf b/www/libs/pChart2.1.4/fonts/GeosansLight.ttf similarity index 100% rename from www/libs/pChart2.1.3/fonts/GeosansLight.ttf rename to www/libs/pChart2.1.4/fonts/GeosansLight.ttf diff --git a/www/libs/pChart2.1.3/fonts/MankSans.ttf b/www/libs/pChart2.1.4/fonts/MankSans.ttf similarity index 100% rename from www/libs/pChart2.1.3/fonts/MankSans.ttf rename to www/libs/pChart2.1.4/fonts/MankSans.ttf diff --git a/www/libs/pChart2.1.3/fonts/Silkscreen.ttf b/www/libs/pChart2.1.4/fonts/Silkscreen.ttf similarity index 100% rename from www/libs/pChart2.1.3/fonts/Silkscreen.ttf rename to www/libs/pChart2.1.4/fonts/Silkscreen.ttf diff --git a/www/libs/pChart2.1.3/fonts/advent_light.ttf b/www/libs/pChart2.1.4/fonts/advent_light.ttf similarity index 100% rename from www/libs/pChart2.1.3/fonts/advent_light.ttf rename to www/libs/pChart2.1.4/fonts/advent_light.ttf diff --git a/www/libs/pChart2.1.3/fonts/calibri.ttf b/www/libs/pChart2.1.4/fonts/calibri.ttf similarity index 100% rename from www/libs/pChart2.1.3/fonts/calibri.ttf rename to www/libs/pChart2.1.4/fonts/calibri.ttf diff --git a/www/libs/pChart2.1.3/fonts/pf_arma_five.ttf b/www/libs/pChart2.1.4/fonts/pf_arma_five.ttf similarity index 100% rename from www/libs/pChart2.1.3/fonts/pf_arma_five.ttf rename to www/libs/pChart2.1.4/fonts/pf_arma_five.ttf diff --git a/www/libs/pChart2.1.3/fonts/verdana.ttf b/www/libs/pChart2.1.4/fonts/verdana.ttf similarity index 100% rename from www/libs/pChart2.1.3/fonts/verdana.ttf rename to www/libs/pChart2.1.4/fonts/verdana.ttf diff --git a/www/libs/pChart2.1.3/palettes/autumn.color b/www/libs/pChart2.1.4/palettes/autumn.color similarity index 100% rename from www/libs/pChart2.1.3/palettes/autumn.color rename to www/libs/pChart2.1.4/palettes/autumn.color diff --git a/www/libs/pChart2.1.3/palettes/blind.color b/www/libs/pChart2.1.4/palettes/blind.color similarity index 100% rename from www/libs/pChart2.1.3/palettes/blind.color rename to www/libs/pChart2.1.4/palettes/blind.color diff --git a/www/libs/pChart2.1.3/palettes/evening.color b/www/libs/pChart2.1.4/palettes/evening.color similarity index 100% rename from www/libs/pChart2.1.3/palettes/evening.color rename to www/libs/pChart2.1.4/palettes/evening.color diff --git a/www/libs/pChart2.1.3/palettes/kitchen.color b/www/libs/pChart2.1.4/palettes/kitchen.color similarity index 100% rename from www/libs/pChart2.1.3/palettes/kitchen.color rename to www/libs/pChart2.1.4/palettes/kitchen.color diff --git a/www/libs/pChart2.1.3/palettes/light.color b/www/libs/pChart2.1.4/palettes/light.color similarity index 100% rename from www/libs/pChart2.1.3/palettes/light.color rename to www/libs/pChart2.1.4/palettes/light.color diff --git a/www/libs/pChart2.1.3/palettes/navy.color b/www/libs/pChart2.1.4/palettes/navy.color similarity index 100% rename from www/libs/pChart2.1.3/palettes/navy.color rename to www/libs/pChart2.1.4/palettes/navy.color diff --git a/www/libs/pChart2.1.3/palettes/shade.color b/www/libs/pChart2.1.4/palettes/shade.color similarity index 100% rename from www/libs/pChart2.1.3/palettes/shade.color rename to www/libs/pChart2.1.4/palettes/shade.color diff --git a/www/libs/pChart2.1.3/palettes/spring.color b/www/libs/pChart2.1.4/palettes/spring.color similarity index 100% rename from www/libs/pChart2.1.3/palettes/spring.color rename to www/libs/pChart2.1.4/palettes/spring.color diff --git a/www/libs/pChart2.1.3/palettes/summer.color b/www/libs/pChart2.1.4/palettes/summer.color similarity index 100% rename from www/libs/pChart2.1.3/palettes/summer.color rename to www/libs/pChart2.1.4/palettes/summer.color