From 5e2659f7244da909ef9d5646d6fac18ec209ae33 Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Sat, 25 Dec 2021 11:51:56 +0100 Subject: [PATCH] libs: Update QCustomPlot to version 2.1.0 --- libs/qcustomplot-source/GPL.txt | 1348 ++-- libs/qcustomplot-source/changelog.txt | 34 + libs/qcustomplot-source/qcustomplot.cpp | 8950 ++++++++++++++++++----- libs/qcustomplot-source/qcustomplot.h | 1468 +++- 4 files changed, 9090 insertions(+), 2710 deletions(-) mode change 100644 => 100755 libs/qcustomplot-source/GPL.txt diff --git a/libs/qcustomplot-source/GPL.txt b/libs/qcustomplot-source/GPL.txt old mode 100644 new mode 100755 index 94a9ed024..818433ecc --- a/libs/qcustomplot-source/GPL.txt +++ b/libs/qcustomplot-source/GPL.txt @@ -1,674 +1,674 @@ - 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 -. + 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/libs/qcustomplot-source/changelog.txt b/libs/qcustomplot-source/changelog.txt index 545d8bf75..cc0fede3f 100644 --- a/libs/qcustomplot-source/changelog.txt +++ b/libs/qcustomplot-source/changelog.txt @@ -1,3 +1,37 @@ +#### Version 2.1.0 released on 29.03.21 #### + +Added features: + - Compatibility up to Qt 6.0 + - Tech Preview: Radial Plots (see setupPolarPlotDemo in examples project) + - QCPAxisTickerDateTime can now be configured with a QTimeZone for adjusting the label display to arbitrary time zones + - QCPColorGradient (and thus also QCPColorMap) now has explicit configurable NaN handling (see QCPColorGradient::setNanHandling) + - added timing/benchmarking method QCustomPlot::replotTime(bool average) which returns the milliseconds per replot + - QCustomPlot::plottableAt has an optional output parameter dataIndex, providing the index of the data point at the probed position + - QCustomPlot::plottableAt template method allows limiting the search to the specified QCPAbstractPlottable subclass T + - QCustomPlot::itemAt template method allows limiting the search to the specified QCPAbstractItem subclass T + - Added Interaction flag QCP::iSelectPlottablesBeyondAxisRect, allows selection of data points very close to (and beyond of) the axes + - QCPAxisTickerDateTime::dateTimeToKey(QDate &) now also takes a TimeSpec to specify the interpretation of the start-of-day + - QCPAxisTickerLog now switches to linear ticks if zoomed in beyond where logarithmic ticks are reasonable + - Added QCustomPlot::afterLayout signal, for user code that crucially depends on layout sizes/positions, right before the draw step during a replot + +Bugfixes: + - Fixed bug where QCPLayer::replot wouldn't issue full replot even though invalidated buffers existed + - Fixed QCPCurve bug causing rendering artifacts when using keys/values smaller than about 1e-12 in some constellations + - Fixed getValueRange when used with explicit keyRange, now doesn't use key range expanded by one point to each side anymore + - Fixed bug of QCPAxis tick label font size change only propagating to the layout after second call to replot + - Fixed bug of QCPTextElement not respecting the configured text alignment flag (setTextFlags) + - Various documentation typos and improvements + +Other: + - QCP Now requires C++11. However, Qt4.6 compatibility is maintained in the QCP 2.x release series + - QCPColorScale is now initialized with gpCold gradient preset, which prevents color maps turning black when linking them to a default-created color scale without explicitly setting a gradient + - QCPLegend::clearItems is now faster in case of many legend items (>1000) + - Modernized expressions and thereby avoided some warnings (e.g. nullptr and casts) + - Switched to foreach (Qt macro) where possible (in preparation for switch to range-based for (C++11), soonest at next major release) + - Work around Qt bug, drawing lines with pen width 1 as slow as with pen widths > 1 (polyfill instead of line algorithm, also on Normal-DPI), by using pen width 0 in such cases. + - Added QCP::Interaction flag iNone=0x000 to allow explicitly specifying no interaction (Avoids QFlags::zero, which was deprecated in Qt5.14) + - QCP is now compatible with defines QT_USE_QSTRINGBUILDER, QT_USE_FAST_CONCATENATION (Qt<4.8), QT_USE_FAST_OPERATOR_PLUS (Qt<4.8) + #### Version 2.0.1 released on 25.06.18 #### Bugfixes: diff --git a/libs/qcustomplot-source/qcustomplot.cpp b/libs/qcustomplot-source/qcustomplot.cpp index 51722e248..04f3147dd 100644 --- a/libs/qcustomplot-source/qcustomplot.cpp +++ b/libs/qcustomplot-source/qcustomplot.cpp @@ -1,7 +1,7 @@ /*************************************************************************** ** ** ** QCustomPlot, an easy to use, modern plotting widget for Qt ** -** Copyright (C) 2011-2018 Emanuel Eichhammer ** +** Copyright (C) 2011-2021 Emanuel Eichhammer ** ** ** ** 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 ** @@ -19,15 +19,15 @@ **************************************************************************** ** Author: Emanuel Eichhammer ** ** Website/Contact: http://www.qcustomplot.com/ ** -** Date: 25.06.18 ** -** Version: 2.0.1 ** +** Date: 29.03.21 ** +** Version: 2.1.0 ** ****************************************************************************/ #include "qcustomplot.h" -/* including file 'src/vector2d.cpp', size 7340 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/vector2d.cpp' */ +/* modified 2021-03-29T02:30:44, size 7973 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPVector2D @@ -72,6 +72,13 @@ \see length */ +/*! \fn double QCPVector2D::angle() const + + Returns the angle of the vector in radians. The angle is measured between the positive x line and + the vector, counter-clockwise in a mathematical coordinate system (y axis upwards positive). In + screen/widget coordinates where the y axis is inverted, the angle appears clockwise. +*/ + /*! \fn QPoint QCPVector2D::toPoint() const Returns a QPoint which has the x and y coordinates of this vector, truncating any floating point @@ -147,25 +154,30 @@ QCPVector2D::QCPVector2D(const QPointF &point) : /*! Normalizes this vector. After this operation, the length of the vector is equal to 1. + If the vector has both entries set to zero, this method does nothing. + \see normalized, length, lengthSquared */ void QCPVector2D::normalize() { - double len = length(); - mX /= len; - mY /= len; + if (mX == 0.0 && mY == 0.0) return; + const double lenInv = 1.0/length(); + mX *= lenInv; + mY *= lenInv; } /*! Returns a normalized version of this vector. The length of the returned vector is equal to 1. + If the vector has both entries set to zero, this method returns the vector unmodified. + \see normalize, length, lengthSquared */ QCPVector2D QCPVector2D::normalized() const { - QCPVector2D result(mX, mY); - result.normalize(); - return result; + if (mX == 0.0 && mY == 0.0) return *this; + const double lenInv = 1.0/length(); + return QCPVector2D(mX*lenInv, mY*lenInv); } /*! \overload @@ -177,11 +189,11 @@ QCPVector2D QCPVector2D::normalized() const */ double QCPVector2D::distanceSquaredToLine(const QCPVector2D &start, const QCPVector2D &end) const { - QCPVector2D v(end-start); - double vLengthSqr = v.lengthSquared(); + const QCPVector2D v(end-start); + const double vLengthSqr = v.lengthSquared(); if (!qFuzzyIsNull(vLengthSqr)) { - double mu = v.dot(*this-start)/vLengthSqr; + const double mu = v.dot(*this-start)/vLengthSqr; if (mu < 0) return (*this-start).lengthSquared(); else if (mu > 1) @@ -259,8 +271,8 @@ QCPVector2D &QCPVector2D::operator-=(const QCPVector2D &vector) /* end of 'src/vector2d.cpp' */ -/* including file 'src/painter.cpp', size 8670 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/painter.cpp' */ +/* modified 2021-03-29T02:30:44, size 8656 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPPainter @@ -283,7 +295,6 @@ QCPVector2D &QCPVector2D::operator-=(const QCPVector2D &vector) Creates a new QCPPainter instance and sets default values */ QCPPainter::QCPPainter() : - QPainter(), mModes(pmDefault), mIsAntialiasing(false) { @@ -477,8 +488,8 @@ void QCPPainter::makeNonCosmetic() /* end of 'src/painter.cpp' */ -/* including file 'src/paintbuffer.cpp', size 18502 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/paintbuffer.cpp' */ +/* modified 2021-03-29T02:30:44, size 18915 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAbstractPaintBuffer @@ -616,7 +627,7 @@ void QCPAbstractPaintBuffer::setInvalidated(bool invalidated) } /*! - Sets the the device pixel ratio to \a ratio. This is useful to render on high-DPI output devices. + Sets the device pixel ratio to \a ratio. This is useful to render on high-DPI output devices. The ratio is automatically set to the device pixel ratio used by the parent QCustomPlot instance. The buffer is reallocated (by calling \ref reallocateBuffer), so any painters that were obtained @@ -667,7 +678,9 @@ QCPPaintBufferPixmap::~QCPPaintBufferPixmap() QCPPainter *QCPPaintBufferPixmap::startPainting() { QCPPainter *result = new QCPPainter(&mBuffer); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) result->setRenderHint(QPainter::HighQualityAntialiasing); +#endif return result; } @@ -845,22 +858,31 @@ QCPPaintBufferGlFbo::~QCPPaintBufferGlFbo() /* inherits documentation from base class */ QCPPainter *QCPPaintBufferGlFbo::startPainting() { - if (mGlPaintDevice.isNull()) + QSharedPointer paintDevice = mGlPaintDevice.toStrongRef(); + QSharedPointer context = mGlContext.toStrongRef(); + if (!paintDevice) { qDebug() << Q_FUNC_INFO << "OpenGL paint device doesn't exist"; return 0; } + if (!context) + { + qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist"; + return 0; + } if (!mGlFrameBuffer) { qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?"; return 0; } - if (QOpenGLContext::currentContext() != mGlContext.data()) - mGlContext.data()->makeCurrent(mGlContext.data()->surface()); + if (QOpenGLContext::currentContext() != context.data()) + context->makeCurrent(context->surface()); mGlFrameBuffer->bind(); - QCPPainter *result = new QCPPainter(mGlPaintDevice.data()); + QCPPainter *result = new QCPPainter(paintDevice.data()); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) result->setRenderHint(QPainter::HighQualityAntialiasing); +#endif return result; } @@ -892,7 +914,8 @@ void QCPPaintBufferGlFbo::draw(QCPPainter *painter) const /* inherits documentation from base class */ void QCPPaintBufferGlFbo::clear(const QColor &color) { - if (mGlContext.isNull()) + QSharedPointer context = mGlContext.toStrongRef(); + if (!context) { qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist"; return; @@ -903,8 +926,8 @@ void QCPPaintBufferGlFbo::clear(const QColor &color) return; } - if (QOpenGLContext::currentContext() != mGlContext.data()) - mGlContext.data()->makeCurrent(mGlContext.data()->surface()); + if (QOpenGLContext::currentContext() != context.data()) + context->makeCurrent(context->surface()); mGlFrameBuffer->bind(); glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF()); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -923,35 +946,37 @@ void QCPPaintBufferGlFbo::reallocateBuffer() mGlFrameBuffer = 0; } - if (mGlContext.isNull()) + QSharedPointer paintDevice = mGlPaintDevice.toStrongRef(); + QSharedPointer context = mGlContext.toStrongRef(); + if (!paintDevice) { - qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist"; + qDebug() << Q_FUNC_INFO << "OpenGL paint device doesn't exist"; return; } - if (mGlPaintDevice.isNull()) + if (!context) { - qDebug() << Q_FUNC_INFO << "OpenGL paint device doesn't exist"; + qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist"; return; } // create new fbo with appropriate size: - mGlContext.data()->makeCurrent(mGlContext.data()->surface()); + context->makeCurrent(context->surface()); QOpenGLFramebufferObjectFormat frameBufferFormat; - frameBufferFormat.setSamples(mGlContext.data()->format().samples()); + frameBufferFormat.setSamples(context->format().samples()); frameBufferFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); mGlFrameBuffer = new QOpenGLFramebufferObject(mSize*mDevicePixelRatio, frameBufferFormat); - if (mGlPaintDevice.data()->size() != mSize*mDevicePixelRatio) - mGlPaintDevice.data()->setSize(mSize*mDevicePixelRatio); + if (paintDevice->size() != mSize*mDevicePixelRatio) + paintDevice->setSize(mSize*mDevicePixelRatio); #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED - mGlPaintDevice.data()->setDevicePixelRatio(mDevicePixelRatio); + paintDevice->setDevicePixelRatio(mDevicePixelRatio); #endif } #endif // QCP_OPENGL_FBO /* end of 'src/paintbuffer.cpp' */ -/* including file 'src/layer.cpp', size 37304 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/layer.cpp' */ +/* modified 2021-03-29T02:30:44, size 37615 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPLayer @@ -1059,10 +1084,10 @@ QCPLayer::~QCPLayer() // call QCustomPlot::removeLayer, which moves all layerables off this layer before deleting it.) while (!mChildren.isEmpty()) - mChildren.last()->setLayer(0); // removes itself from mChildren via removeChild() + mChildren.last()->setLayer(nullptr); // removes itself from mChildren via removeChild() if (mParentPlot->currentLayer() == this) - qDebug() << Q_FUNC_INFO << "The parent plot's mCurrentLayer will be a dangling pointer. Should have been set to a valid layer or 0 beforehand."; + qDebug() << Q_FUNC_INFO << "The parent plot's mCurrentLayer will be a dangling pointer. Should have been set to a valid layer or nullptr beforehand."; } /*! @@ -1104,8 +1129,8 @@ void QCPLayer::setMode(QCPLayer::LayerMode mode) if (mMode != mode) { mMode = mode; - if (!mPaintBuffer.isNull()) - mPaintBuffer.data()->setInvalidated(); + if (QSharedPointer pb = mPaintBuffer.toStrongRef()) + pb->setInvalidated(); } } @@ -1140,18 +1165,18 @@ void QCPLayer::draw(QCPPainter *painter) */ void QCPLayer::drawToPaintBuffer() { - if (!mPaintBuffer.isNull()) + if (QSharedPointer pb = mPaintBuffer.toStrongRef()) { - if (QCPPainter *painter = mPaintBuffer.data()->startPainting()) + if (QCPPainter *painter = pb->startPainting()) { if (painter->isActive()) draw(painter); else qDebug() << Q_FUNC_INFO << "paint buffer returned inactive painter"; delete painter; - mPaintBuffer.data()->donePainting(); + pb->donePainting(); } else - qDebug() << Q_FUNC_INFO << "paint buffer returned zero painter"; + qDebug() << Q_FUNC_INFO << "paint buffer returned nullptr painter"; } else qDebug() << Q_FUNC_INFO << "no valid paint buffer associated with this layer"; } @@ -1161,27 +1186,28 @@ void QCPLayer::drawToPaintBuffer() the layerables on this specific layer, without the need to replot all other layers (as a call to \ref QCustomPlot::replot would do). + QCustomPlot also makes sure to replot all layers instead of only this one, if the layer ordering + or any layerable-layer-association has changed since the last full replot and any other paint + buffers were thus invalidated. + If the layer mode is \ref lmLogical however, this method simply calls \ref QCustomPlot::replot on the parent QCustomPlot instance. - QCustomPlot also makes sure to replot all layers instead of only this one, if the layer ordering - has changed since the last full replot and the other paint buffers were thus invalidated. - \see draw */ void QCPLayer::replot() { if (mMode == lmBuffered && !mParentPlot->hasInvalidatedPaintBuffers()) { - if (!mPaintBuffer.isNull()) + if (QSharedPointer pb = mPaintBuffer.toStrongRef()) { - mPaintBuffer.data()->clear(Qt::transparent); + pb->clear(Qt::transparent); drawToPaintBuffer(); - mPaintBuffer.data()->setInvalidated(false); + pb->setInvalidated(false); // since layer is lmBuffered, we know only this layer is on buffer and we can reset invalidated flag mParentPlot->update(); } else qDebug() << Q_FUNC_INFO << "no valid paint buffer associated with this layer"; - } else if (mMode == lmLogical) + } else mParentPlot->replot(); } @@ -1203,8 +1229,8 @@ void QCPLayer::addChild(QCPLayerable *layerable, bool prepend) mChildren.prepend(layerable); else mChildren.append(layerable); - if (!mPaintBuffer.isNull()) - mPaintBuffer.data()->setInvalidated(); + if (QSharedPointer pb = mPaintBuffer.toStrongRef()) + pb->setInvalidated(); } else qDebug() << Q_FUNC_INFO << "layerable is already child of this layer" << reinterpret_cast(layerable); } @@ -1222,8 +1248,8 @@ void QCPLayer::removeChild(QCPLayerable *layerable) { if (mChildren.removeOne(layerable)) { - if (!mPaintBuffer.isNull()) - mPaintBuffer.data()->setInvalidated(); + if (QSharedPointer pb = mPaintBuffer.toStrongRef()) + pb->setInvalidated(); } else qDebug() << Q_FUNC_INFO << "layerable is not child of this layer" << reinterpret_cast(layerable); } @@ -1254,7 +1280,8 @@ void QCPLayer::removeChild(QCPLayerable *layerable) only get drawn if their parent layerables are visible, too. Note that a parent layerable is not necessarily also the QObject parent for memory management. - Further, a layerable doesn't always have a parent layerable, so this function may return 0. + Further, a layerable doesn't always have a parent layerable, so this function may return \c + nullptr. A parent layerable is set implicitly when placed inside layout elements and doesn't need to be set manually by the user. @@ -1326,8 +1353,8 @@ void QCPLayer::removeChild(QCPLayerable *layerable) targetLayer is an empty string, it places itself on the current layer of the plot (see \ref QCustomPlot::setCurrentLayer). - It is possible to provide 0 as \a plot. In that case, you should assign a parent plot at a later - time with \ref initializeParentPlot. + It is possible to provide \c nullptr as \a plot. In that case, you should assign a parent plot at + a later time with \ref initializeParentPlot. The layerable's parent layerable is set to \a parentLayerable, if provided. Direct layerable parents are mainly used to control visibility in a hierarchy of layerables. This means a @@ -1341,7 +1368,7 @@ QCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer, QCPLayerable mVisible(true), mParentPlot(plot), mParentLayerable(parentLayerable), - mLayer(0), + mLayer(nullptr), mAntialiased(true) { if (mParentPlot) @@ -1358,7 +1385,7 @@ QCPLayerable::~QCPLayerable() if (mLayer) { mLayer->removeChild(this); - mLayer = 0; + mLayer = nullptr; } } @@ -1467,7 +1494,8 @@ bool QCPLayerable::realVisibility() const In the case of 1D Plottables (\ref QCPAbstractPlottable1D, like \ref QCPGraph or \ref QCPBars) \a details will be set to a \ref QCPDataSelection, describing the closest data point to \a pos. - You may pass 0 as \a details to indicate that you are not interested in those selection details. + You may pass \c nullptr as \a details to indicate that you are not interested in those selection + details. \see selectEvent, deselectEvent, mousePressEvent, wheelEvent, QCustomPlot::setInteractions, QCPAbstractPlottable1D::selectTestRect @@ -1483,11 +1511,11 @@ double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVarian /*! \internal Sets the parent plot of this layerable. Use this function once to set the parent plot if you have - passed 0 in the constructor. It can not be used to move a layerable from one QCustomPlot to - another one. + passed \c nullptr in the constructor. It can not be used to move a layerable from one QCustomPlot + to another one. - Note that, unlike when passing a non-null parent plot in the constructor, this function does not - make \a parentPlot the QObject-parent of this layerable. If you want this, call + Note that, unlike when passing a non \c nullptr parent plot in the constructor, this function + does not make \a parentPlot the QObject-parent of this layerable. If you want this, call QObject::setParent(\a parentPlot) in addition to this function. Further, you will probably want to set a layer (\ref setLayer) after calling this function, to @@ -1580,8 +1608,8 @@ void QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialia /*! \internal This function is called by \ref initializeParentPlot, to allow subclasses to react on the setting - of a parent plot. This is the case when 0 was passed as parent plot in the constructor, and the - parent plot is set at a later time. + of a parent plot. This is the case when \c nullptr was passed as parent plot in the constructor, + and the parent plot is set at a later time. For example, QCPLayoutElement/QCPLayout hierarchies may be created independently of any QCustomPlot at first. When they are then added to a layout inside the QCustomPlot, the top level @@ -1628,7 +1656,7 @@ QRect QCPLayerable::clipRect() const if (mParentPlot) return mParentPlot->viewport(); else - return QRect(); + return {}; } /*! \internal @@ -1721,7 +1749,7 @@ void QCPLayerable::mousePressEvent(QMouseEvent *event, const QVariant &details) The current pixel position of the cursor on the QCustomPlot widget is accessible via \c event->pos(). The parameter \a startPos indicates the position where the initial \ref - mousePressEvent occured, that started the mouse interaction. + mousePressEvent occurred, that started the mouse interaction. The default implementation does nothing. @@ -1739,7 +1767,7 @@ void QCPLayerable::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) The current pixel position of the cursor on the QCustomPlot widget is accessible via \c event->pos(). The parameter \a startPos indicates the position where the initial \ref - mousePressEvent occured, that started the mouse interaction. + mousePressEvent occurred, that started the mouse interaction. The default implementation does nothing. @@ -1791,10 +1819,10 @@ void QCPLayerable::mouseDoubleClickEvent(QMouseEvent *event, const QVariant &det The current pixel position of the cursor on the QCustomPlot widget is accessible via \c event->pos(). - The \c event->delta() indicates how far the mouse wheel was turned, which is usually +/- 120 for - single rotation steps. However, if the mouse wheel is turned rapidly, multiple steps may - accumulate to one event, making \c event->delta() larger. On the other hand, if the wheel has - very smooth steps or none at all, the delta may be smaller. + The \c event->angleDelta() indicates how far the mouse wheel was turned, which is usually +/- 120 + for single rotation steps. However, if the mouse wheel is turned rapidly, multiple steps may + accumulate to one event, making the delta larger. On the other hand, if the wheel has very smooth + steps or none at all, the delta may be smaller. The default implementation does nothing. @@ -1807,8 +1835,8 @@ void QCPLayerable::wheelEvent(QWheelEvent *event) /* end of 'src/layer.cpp' */ -/* including file 'src/axis/range.cpp', size 12221 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/axis/range.cpp' */ +/* modified 2021-03-29T02:30:44, size 12221 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPRange @@ -2129,8 +2157,8 @@ bool QCPRange::validRange(const QCPRange &range) /* end of 'src/axis/range.cpp' */ -/* including file 'src/selection.cpp', size 21941 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/selection.cpp' */ +/* modified 2021-03-29T02:30:44, size 21837 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPDataRange @@ -2140,10 +2168,9 @@ bool QCPRange::validRange(const QCPRange &range) \brief Describes a data range given by begin and end index QCPDataRange holds two integers describing the begin (\ref setBegin) and end (\ref setEnd) index - of a contiguous set of data points. The end index points to the data point just after the last - data point that's part of the data range, similarly to the nomenclature used in standard - iterators. - + of a contiguous set of data points. The \a end index corresponds to the data point just after the + last data point of the data range, like in standard iterators. + Data Ranges are not bound to a certain plottable, thus they can be freely exchanged, created and modified. If a non-contiguous data set shall be described, the class \ref QCPDataSelection is used, which holds and manages multiple instances of \ref QCPDataRange. In most situations, \ref @@ -2271,7 +2298,7 @@ QCPDataRange QCPDataRange::bounded(const QCPDataRange &other) const */ QCPDataRange QCPDataRange::expanded(const QCPDataRange &other) const { - return QCPDataRange(qMin(mBegin, other.mBegin), qMax(mEnd, other.mEnd)); + return {qMin(mBegin, other.mBegin), qMax(mEnd, other.mEnd)}; } /*! @@ -2290,7 +2317,7 @@ QCPDataRange QCPDataRange::intersection(const QCPDataRange &other) const if (result.isValid()) return result; else - return QCPDataRange(); + return {}; } /*! @@ -2501,8 +2528,8 @@ QCPDataSelection &QCPDataSelection::operator-=(const QCPDataRange &other) int QCPDataSelection::dataPointCount() const { int result = 0; - for (int i=0; iorientation() == Qt::Horizontal) - return QCPRange(axis->pixelToCoord(mRect.left()), axis->pixelToCoord(mRect.left()+mRect.width())); + return {axis->pixelToCoord(mRect.left()), axis->pixelToCoord(mRect.left()+mRect.width())}; else - return QCPRange(axis->pixelToCoord(mRect.top()+mRect.height()), axis->pixelToCoord(mRect.top())); + return {axis->pixelToCoord(mRect.top()+mRect.height()), axis->pixelToCoord(mRect.top())}; } else { qDebug() << Q_FUNC_INFO << "called with axis zero"; - return QCPRange(); + return {}; } } @@ -2878,7 +2905,7 @@ void QCPSelectionRect::cancel() if (mActive) { mActive = false; - emit canceled(mRect, 0); + emit canceled(mRect, nullptr); } } @@ -2960,8 +2987,8 @@ void QCPSelectionRect::draw(QCPPainter *painter) /* end of 'src/selectionrect.cpp' */ -/* including file 'src/layout.cpp', size 79139 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/layout.cpp' */ +/* modified 2021-03-29T02:30:44, size 78863 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPMarginGroup @@ -3055,7 +3082,7 @@ void QCPMarginGroup::clear() it.next(); const QList elements = it.value(); for (int i=elements.size()-1; i>=0; --i) - elements.at(i)->setMarginGroup(it.key(), 0); // removes itself from mChildren via removeChild + elements.at(i)->setMarginGroup(it.key(), nullptr); // removes itself from mChildren via removeChild } } @@ -3073,12 +3100,11 @@ int QCPMarginGroup::commonMargin(QCP::MarginSide side) const { // query all automatic margins of the layout elements in this margin group side and find maximum: int result = 0; - const QList elements = mChildren.value(side); - for (int i=0; iautoMargins().testFlag(side)) + if (!el->autoMargins().testFlag(side)) continue; - int m = qMax(elements.at(i)->calculateAutoMargin(side), QCP::getMarginValue(elements.at(i)->minimumMargins(), side)); + int m = qMax(el->calculateAutoMargin(side), QCP::getMarginValue(el->minimumMargins(), side)); if (m > result) result = m; } @@ -3180,7 +3206,7 @@ void QCPMarginGroup::removeChild(QCP::MarginSide side, QCPLayoutElement *element */ QCPLayoutElement::QCPLayoutElement(QCustomPlot *parentPlot) : QCPLayerable(parentPlot), // parenthood is changed as soon as layout element gets inserted into a layout (except for top level layout) - mParentLayout(0), + mParentLayout(nullptr), mMinimumSize(), mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX), mSizeConstraintRect(scrInnerRect), @@ -3194,7 +3220,7 @@ QCPLayoutElement::QCPLayoutElement(QCustomPlot *parentPlot) : QCPLayoutElement::~QCPLayoutElement() { - setMarginGroup(QCP::msAll, 0); // unregister at margin groups, if there are any + setMarginGroup(QCP::msAll, nullptr); // unregister at margin groups, if there are any // unregister at layout: if (qobject_cast(mParentLayout)) // the qobject_cast is just a safeguard in case the layout forgets to call clear() in its dtor and this dtor is called by QObject dtor mParentLayout->take(this); @@ -3360,7 +3386,7 @@ void QCPLayoutElement::setSizeConstraintRect(SizeConstraintRect constraintRect) Margin groups allow synchronizing specified margins across layout elements, see the documentation of \ref QCPMarginGroup. - To unset the margin group of \a sides, set \a group to 0. + To unset the margin group of \a sides, set \a group to \c nullptr. Note that margin groups only work for margin sides that are set to automatic (\ref setAutoMargins). @@ -3375,9 +3401,8 @@ void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *gr if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop); if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom); - for (int i=0; i allMarginSides = QList() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom; + const QList allMarginSides = QList() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom; foreach (QCP::MarginSide side, allMarginSides) { if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically @@ -3451,7 +3476,7 @@ void QCPLayoutElement::update(UpdatePhase phase) */ QSize QCPLayoutElement::minimumOuterSizeHint() const { - return QSize(mMargins.left()+mMargins.right(), mMargins.top()+mMargins.bottom()); + return {mMargins.left()+mMargins.right(), mMargins.top()+mMargins.bottom()}; } /*! @@ -3470,15 +3495,15 @@ QSize QCPLayoutElement::minimumOuterSizeHint() const */ QSize QCPLayoutElement::maximumOuterSizeHint() const { - return QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + return {QWIDGETSIZE_MAX, QWIDGETSIZE_MAX}; } /*! Returns a list of all child elements in this layout element. If \a recursive is true, all sub-child elements are included in the list, too. - \warning There may be entries with value 0 in the returned list. (For example, QCPLayoutGrid may have - empty cells which yield 0 at the respective index.) + \warning There may be \c nullptr entries in the returned list. For example, QCPLayoutGrid may + have empty cells which yield \c nullptr at the respective index. */ QList QCPLayoutElement::elements(bool recursive) const { @@ -3524,7 +3549,7 @@ double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable, QVa */ void QCPLayoutElement::parentPlotInitialized(QCustomPlot *parentPlot) { - foreach (QCPLayoutElement* el, elements(false)) + foreach (QCPLayoutElement *el, elements(false)) { if (!el->parentPlot()) el->initializeParentPlot(parentPlot); @@ -3598,11 +3623,12 @@ void QCPLayoutElement::layoutChanged() /*! \fn virtual QCPLayoutElement* QCPLayout::elementAt(int index) const = 0 - Returns the element in the cell with the given \a index. If \a index is invalid, returns 0. + Returns the element in the cell with the given \a index. If \a index is invalid, returns \c + nullptr. Note that even if \a index is valid, the respective cell may be empty in some layouts (e.g. - QCPLayoutGrid), so this function may return 0 in those cases. You may use this function to check - whether a cell is empty or not. + QCPLayoutGrid), so this function may return \c nullptr in those cases. You may use this function + to check whether a cell is empty or not. \see elements, elementCount, takeAt */ @@ -3611,7 +3637,7 @@ void QCPLayoutElement::layoutChanged() Removes the element with the given \a index from the layout and returns it. - If the \a index is invalid or the cell with that index is empty, returns 0. + If the \a index is invalid or the cell with that index is empty, returns \c nullptr. Note that some layouts don't remove the respective cell right away but leave an empty cell after successful removal of the layout element. To collapse empty cells, use \ref simplify. @@ -3830,8 +3856,8 @@ void QCPLayout::releaseElement(QCPLayoutElement *el) { if (el) { - el->mParentLayout = 0; - el->setParentLayerable(0); + el->mParentLayout = nullptr; + el->setParentLayerable(nullptr); el->setParent(mParentPlot); // Note: Don't initializeParentPlot(0) here, because layout element will stay in same parent plot } else @@ -3909,9 +3935,8 @@ QVector QCPLayout::getSectionSizes(QVector maxSizes, QVector minS // find section that hits its maximum next: int nextId = -1; double nextMax = 1e12; - for (int i=0; i QCPLayout::getSectionSizes(QVector maxSizes, QVector minS // check if that maximum is actually within the bounds of the total size (i.e. can we stretch all remaining sections so far that the found section // actually hits its maximum, without exceeding the total size when we add up all sections) double stretchFactorSum = 0; - for (int i=0; i QCPLayout::getSectionSizes(QVector maxSizes, QVector minS freeSize -= sectionSizes.at(i); // remove size of minimum locked sections from available space in next round } // reset all section sizes to zero that are in unfinished sections (all others have been set to their minimum): - for (int i=0; i 0 && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect) minOuter.rheight() += el->margins().top() + el->margins().bottom(); - return QSize(minOuter.width() > 0 ? minOuter.width() : minOuterHint.width(), - minOuter.height() > 0 ? minOuter.height() : minOuterHint.height());; + return {minOuter.width() > 0 ? minOuter.width() : minOuterHint.width(), + minOuter.height() > 0 ? minOuter.height() : minOuterHint.height()}; } /*! \internal @@ -4026,8 +4051,8 @@ QSize QCPLayout::getFinalMaximumOuterSize(const QCPLayoutElement *el) if (maxOuter.height() < QWIDGETSIZE_MAX && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect) maxOuter.rheight() += el->margins().top() + el->margins().bottom(); - return QSize(maxOuter.width() < QWIDGETSIZE_MAX ? maxOuter.width() : maxOuterHint.width(), - maxOuter.height() < QWIDGETSIZE_MAX ? maxOuter.height() : maxOuterHint.height()); + return {maxOuter.width() < QWIDGETSIZE_MAX ? maxOuter.width() : maxOuterHint.width(), + maxOuter.height() < QWIDGETSIZE_MAX ? maxOuter.height() : maxOuterHint.height()}; } @@ -4094,8 +4119,8 @@ QCPLayoutGrid::~QCPLayoutGrid() /*! Returns the element in the cell in \a row and \a column. - Returns 0 if either the row/column is invalid or if the cell is empty. In those cases, a qDebug - message is printed. To check whether a cell exists and isn't empty, use \ref hasElement. + Returns \c nullptr if either the row/column is invalid or if the cell is empty. In those cases, a + qDebug message is printed. To check whether a cell exists and isn't empty, use \ref hasElement. \see addElement, hasElement */ @@ -4113,7 +4138,7 @@ QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row << "Column:" << column; } else qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row << "Column:" << column; - return 0; + return nullptr; } @@ -4394,8 +4419,8 @@ void QCPLayoutGrid::setFillOrder(FillOrder order, bool rearrange) // if rearranging, re-insert via linear index according to new fill order: if (rearrange) { - for (int i=0; i newRow; for (int col=0; col minColWidths, minRowHeights; getMinimumRowColSizes(&minColWidths, &minRowHeights); QSize result(0, 0); - for (int i=0; isetOuterRect(insetRect); } @@ -5012,7 +5041,7 @@ QCPLayoutElement *QCPLayoutInset::elementAt(int index) const if (index >= 0 && index < mElements.size()) return mElements.at(index); else - return 0; + return nullptr; } /* inherits documentation from base class */ @@ -5029,7 +5058,7 @@ QCPLayoutElement *QCPLayoutInset::takeAt(int index) } else { qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index; - return 0; + return nullptr; } } @@ -5048,7 +5077,7 @@ bool QCPLayoutInset::take(QCPLayoutElement *element) } qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take"; } else - qDebug() << Q_FUNC_INFO << "Can't take null element"; + qDebug() << Q_FUNC_INFO << "Can't take nullptr element"; return false; } @@ -5067,11 +5096,11 @@ double QCPLayoutInset::selectTest(const QPointF &pos, bool onlySelectable, QVari if (onlySelectable) return -1; - for (int i=0; irealVisibility() && mElements.at(i)->selectTest(pos, onlySelectable) >= 0) + if (el->realVisibility() && el->selectTest(pos, onlySelectable) >= 0) return mParentPlot->selectionTolerance()*0.99; } return -1; @@ -5100,7 +5129,7 @@ void QCPLayoutInset::addElement(QCPLayoutElement *element, Qt::Alignment alignme mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4)); adoptElement(element); } else - qDebug() << Q_FUNC_INFO << "Can't add null element"; + qDebug() << Q_FUNC_INFO << "Can't add nullptr element"; } /*! @@ -5126,13 +5155,13 @@ void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect) mInsetRect.append(rect); adoptElement(element); } else - qDebug() << Q_FUNC_INFO << "Can't add null element"; + qDebug() << Q_FUNC_INFO << "Can't add nullptr element"; } /* end of 'src/layout.cpp' */ -/* including file 'src/lineending.cpp', size 11536 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/lineending.cpp' */ +/* modified 2021-03-29T02:30:44, size 11189 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPLineEnding @@ -5402,17 +5431,12 @@ void QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos, const QCPV } case esSkewedBar: { - if (qFuzzyIsNull(painter->pen().widthF()) && !painter->modes().testFlag(QCPPainter::pmNonCosmetic)) - { - // if drawing with cosmetic pen (perfectly thin stroke, happens only in vector exports), draw bar exactly on tip of line - painter->drawLine((pos+widthVec+lengthVec*0.2*(mInverted?-1:1)).toPointF(), - (pos-widthVec-lengthVec*0.2*(mInverted?-1:1)).toPointF()); - } else - { - // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly - painter->drawLine((pos+widthVec+lengthVec*0.2*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF(), - (pos-widthVec-lengthVec*0.2*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF()); - } + QCPVector2D shift; + if (!qFuzzyIsNull(painter->pen().widthF()) || painter->modes().testFlag(QCPPainter::pmNonCosmetic)) + shift = dir.normalized()*qMax(qreal(1.0), painter->pen().widthF())*qreal(0.5); + // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly + painter->drawLine((pos+widthVec+lengthVec*0.2*(mInverted?-1:1)+shift).toPointF(), + (pos-widthVec-lengthVec*0.2*(mInverted?-1:1)+shift).toPointF()); break; } } @@ -5430,906 +5454,1231 @@ void QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos, double ang /* end of 'src/lineending.cpp' */ -/* including file 'src/axis/axisticker.cpp', size 18664 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/axis/labelpainter.cpp' */ +/* modified 2021-03-29T02:30:44, size 27296 */ + //////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////// QCPAxisTicker +//////////////////// QCPLabelPainterPrivate //////////////////////////////////////////////////////////////////////////////////////////////////// -/*! \class QCPAxisTicker - \brief The base class tick generator used by QCPAxis to create tick positions and tick labels - - Each QCPAxis has an internal QCPAxisTicker (or a subclass) in order to generate tick positions - and tick labels for the current axis range. The ticker of an axis can be set via \ref - QCPAxis::setTicker. Since that method takes a QSharedPointer, multiple - axes can share the same ticker instance. - - This base class generates normal tick coordinates and numeric labels for linear axes. It picks a - reasonable tick step (the separation between ticks) which results in readable tick labels. The - number of ticks that should be approximately generated can be set via \ref setTickCount. - Depending on the current tick step strategy (\ref setTickStepStrategy), the algorithm either - sacrifices readability to better match the specified tick count (\ref - QCPAxisTicker::tssMeetTickCount) or relaxes the tick count in favor of better tick steps (\ref - QCPAxisTicker::tssReadability), which is the default. - - The following more specialized axis ticker subclasses are available, see details in the - respective class documentation: - -
- - - - - - - -
QCPAxisTickerFixed\image html axisticker-fixed.png
QCPAxisTickerLog\image html axisticker-log.png
QCPAxisTickerPi\image html axisticker-pi.png
QCPAxisTickerText\image html axisticker-text.png
QCPAxisTickerDateTime\image html axisticker-datetime.png
QCPAxisTickerTime\image html axisticker-time.png - \image html axisticker-time2.png
-
- - \section axisticker-subclassing Creating own axis tickers - - Creating own axis tickers can be achieved very easily by sublassing QCPAxisTicker and - reimplementing some or all of the available virtual methods. - In the simplest case you might wish to just generate different tick steps than the other tickers, - so you only reimplement the method \ref getTickStep. If you additionally want control over the - string that will be shown as tick label, reimplement \ref getTickLabel. - - If you wish to have complete control, you can generate the tick vectors and tick label vectors - yourself by reimplementing \ref createTickVector and \ref createLabelVector. The default - implementations use the previously mentioned virtual methods \ref getTickStep and \ref - getTickLabel, but your reimplementations don't necessarily need to do so. For example in the case - of unequal tick steps, the method \ref getTickStep loses its usefulness and can be ignored. +/*! \class QCPLabelPainterPrivate + + \internal + \brief (Private) - The sub tick count between major ticks can be controlled with \ref getSubTickCount. Full sub tick - placement control is obtained by reimplementing \ref createSubTickVector. + This is a private class and not part of the public QCustomPlot interface. - See the documentation of all these virtual methods in QCPAxisTicker for detailed information - about the parameters and expected return values. */ +const QChar QCPLabelPainterPrivate::SymbolDot(183); +const QChar QCPLabelPainterPrivate::SymbolCross(215); + /*! - Constructs the ticker and sets reasonable default values. Axis tickers are commonly created - managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker. + Constructs a QCPLabelPainterPrivate instance. Make sure to not create a new + instance on every redraw, to utilize the caching mechanisms. + + the \a parentPlot does not take ownership of the label painter. Make sure + to delete it appropriately. */ -QCPAxisTicker::QCPAxisTicker() : - mTickStepStrategy(tssReadability), - mTickCount(5), - mTickOrigin(0) +QCPLabelPainterPrivate::QCPLabelPainterPrivate(QCustomPlot *parentPlot) : + mAnchorMode(amRectangular), + mAnchorSide(asLeft), + mAnchorReferenceType(artNormal), + mColor(Qt::black), + mPadding(0), + mRotation(0), + mSubstituteExponent(true), + mMultiplicationSymbol(QChar(215)), + mAbbreviateDecimalPowers(false), + mParentPlot(parentPlot), + mLabelCache(16) { + analyzeFontMetrics(); } -QCPAxisTicker::~QCPAxisTicker() +QCPLabelPainterPrivate::~QCPLabelPainterPrivate() { - } -/*! - Sets which strategy the axis ticker follows when choosing the size of the tick step. For the - available strategies, see \ref TickStepStrategy. -*/ -void QCPAxisTicker::setTickStepStrategy(QCPAxisTicker::TickStepStrategy strategy) +void QCPLabelPainterPrivate::setAnchorSide(AnchorSide side) { - mTickStepStrategy = strategy; + mAnchorSide = side; } -/*! - Sets how many ticks this ticker shall aim to generate across the axis range. Note that \a count - is not guaranteed to be matched exactly, as generating readable tick intervals may conflict with - the requested number of ticks. +void QCPLabelPainterPrivate::setAnchorMode(AnchorMode mode) +{ + mAnchorMode = mode; +} - Whether the readability has priority over meeting the requested \a count can be specified with - \ref setTickStepStrategy. -*/ -void QCPAxisTicker::setTickCount(int count) +void QCPLabelPainterPrivate::setAnchorReference(const QPointF &pixelPoint) { - if (count > 0) - mTickCount = count; - else - qDebug() << Q_FUNC_INFO << "tick count must be greater than zero:" << count; + mAnchorReference = pixelPoint; } -/*! - Sets the mathematical coordinate (or "offset") of the zeroth tick. This tick coordinate is just a - concept and doesn't need to be inside the currently visible axis range. - - By default \a origin is zero, which for example yields ticks {-5, 0, 5, 10, 15,...} when the tick - step is five. If \a origin is now set to 1 instead, the correspondingly generated ticks would be - {-4, 1, 6, 11, 16,...}. -*/ -void QCPAxisTicker::setTickOrigin(double origin) +void QCPLabelPainterPrivate::setAnchorReferenceType(AnchorReferenceType type) { - mTickOrigin = origin; + mAnchorReferenceType = type; } -/*! - This is the method called by QCPAxis in order to actually generate tick coordinates (\a ticks), - tick label strings (\a tickLabels) and sub tick coordinates (\a subTicks). +void QCPLabelPainterPrivate::setFont(const QFont &font) +{ + if (mFont != font) + { + mFont = font; + analyzeFontMetrics(); + } +} + +void QCPLabelPainterPrivate::setColor(const QColor &color) +{ + mColor = color; +} + +void QCPLabelPainterPrivate::setPadding(int padding) +{ + mPadding = padding; +} + +void QCPLabelPainterPrivate::setRotation(double rotation) +{ + mRotation = qBound(-90.0, rotation, 90.0); +} + +void QCPLabelPainterPrivate::setSubstituteExponent(bool enabled) +{ + mSubstituteExponent = enabled; +} + +void QCPLabelPainterPrivate::setMultiplicationSymbol(QChar symbol) +{ + mMultiplicationSymbol = symbol; +} + +void QCPLabelPainterPrivate::setAbbreviateDecimalPowers(bool enabled) +{ + mAbbreviateDecimalPowers = enabled; +} + +void QCPLabelPainterPrivate::setCacheSize(int labelCount) +{ + mLabelCache.setMaxCost(labelCount); +} + +int QCPLabelPainterPrivate::cacheSize() const +{ + return mLabelCache.maxCost(); +} + +void QCPLabelPainterPrivate::drawTickLabel(QCPPainter *painter, const QPointF &tickPos, const QString &text) +{ + double realRotation = mRotation; - The ticks are generated for the specified \a range. The generated labels typically follow the - specified \a locale, \a formatChar and number \a precision, however this might be different (or - even irrelevant) for certain QCPAxisTicker subclasses. + AnchorSide realSide = mAnchorSide; + // for circular axes, the anchor side is determined depending on the quadrant of tickPos with respect to mCircularReference + if (mAnchorMode == amSkewedUpright) + { + realSide = skewedAnchorSide(tickPos, 0.2, 0.3); + } else if (mAnchorMode == amSkewedRotated) // in this mode every label is individually rotated to match circle tangent + { + realSide = skewedAnchorSide(tickPos, 0, 0); + realRotation += QCPVector2D(tickPos-mAnchorReference).angle()/M_PI*180.0; + if (realRotation > 90) realRotation -= 180; + else if (realRotation < -90) realRotation += 180; + } - The output parameter \a ticks is filled with the generated tick positions in axis coordinates. - The output parameters \a subTicks and \a tickLabels are optional (set them to 0 if not needed) - and are respectively filled with sub tick coordinates, and tick label strings belonging to \a - ticks by index. + realSide = rotationCorrectedSide(realSide, realRotation); // rotation angles may change the true anchor side of the label + drawLabelMaybeCached(painter, mFont, mColor, getAnchorPos(tickPos), realSide, realRotation, text); +} + +/*! \internal + + Returns the size ("margin" in QCPAxisRect context, so measured perpendicular to the axis backbone + direction) needed to fit the axis. */ -void QCPAxisTicker::generate(const QCPRange &range, const QLocale &locale, QChar formatChar, int precision, QVector &ticks, QVector *subTicks, QVector *tickLabels) +/* TODO: needed? +int QCPLabelPainterPrivate::size() const { - // generate (major) ticks: - double tickStep = getTickStep(range); - ticks = createTickVector(tickStep, range); - trimTicks(range, ticks, true); // trim ticks to visible range plus one outer tick on each side (incase a subclass createTickVector creates more) + int result = 0; + // get length of tick marks pointing outwards: + if (!tickPositions.isEmpty()) + result += qMax(0, qMax(tickLengthOut, subTickLengthOut)); - // generate sub ticks between major ticks: - if (subTicks) + // calculate size of tick labels: + if (tickLabelSide == QCPAxis::lsOutside) { - if (ticks.size() > 0) + QSize tickLabelsSize(0, 0); + if (!tickLabels.isEmpty()) { - *subTicks = createSubTickVector(getSubTickCount(tickStep), ticks); - trimTicks(range, *subTicks, false); - } else - *subTicks = QVector(); + for (int i=0; ibufferDevicePixelRatio())); + result.append(QByteArray::number(mRotation)); + //result.append(QByteArray::number((int)tickLabelSide)); TODO: check whether this is really a cache-invalidating property + result.append(QByteArray::number((int)mSubstituteExponent)); + result.append(QString(mMultiplicationSymbol).toUtf8()); + result.append(mColor.name().toLatin1()+QByteArray::number(mColor.alpha(), 16)); + result.append(mFont.toString().toLatin1()); + return result; +} + +/*! \internal - // separate integer and fractional part of mantissa: - double epsilon = 0.01; - double intPartf; - int intPart; - double fracPart = modf(getMantissa(tickStep), &intPartf); - intPart = intPartf; + Draws a single tick label with the provided \a painter, utilizing the internal label cache to + significantly speed up drawing of labels that were drawn in previous calls. The tick label is + always bound to an axis, the distance to the axis is controllable via \a distanceToAxis in + pixels. The pixel position in the axis direction is passed in the \a position parameter. Hence + for the bottom axis, \a position would indicate the horizontal pixel position (not coordinate), + at which the label should be drawn. - // handle cases with (almost) integer mantissa: - if (fracPart < epsilon || 1.0-fracPart < epsilon) + In order to later draw the axis label in a place that doesn't overlap with the tick labels, the + largest tick label size is needed. This is acquired by passing a \a tickLabelsSize to the \ref + drawTickLabel calls during the process of drawing all tick labels of one axis. In every call, \a + tickLabelsSize is expanded, if the drawn label exceeds the value \a tickLabelsSize currently + holds. + + The label is drawn with the font and pen that are currently set on the \a painter. To draw + superscripted powers, the font is temporarily made smaller by a fixed factor (see \ref + getTickLabelData). +*/ +void QCPLabelPainterPrivate::drawLabelMaybeCached(QCPPainter *painter, const QFont &font, const QColor &color, const QPointF &pos, AnchorSide side, double rotation, const QString &text) +{ + // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly! + if (text.isEmpty()) return; + QSize finalSize; + + if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled { - if (1.0-fracPart < epsilon) - ++intPart; - switch (intPart) + QByteArray key = cacheKey(text, color, rotation, side); + CachedLabel *cachedLabel = mLabelCache.take(QString::fromUtf8(key)); // attempt to take label from cache (don't use object() because we want ownership/prevent deletion during our operations, we re-insert it afterwards) + if (!cachedLabel) // no cached label existed, create it { - case 1: result = 4; break; // 1.0 -> 0.2 substep - case 2: result = 3; break; // 2.0 -> 0.5 substep - case 3: result = 2; break; // 3.0 -> 1.0 substep - case 4: result = 3; break; // 4.0 -> 1.0 substep - case 5: result = 4; break; // 5.0 -> 1.0 substep - case 6: result = 2; break; // 6.0 -> 2.0 substep - case 7: result = 6; break; // 7.0 -> 1.0 substep - case 8: result = 3; break; // 8.0 -> 2.0 substep - case 9: result = 2; break; // 9.0 -> 3.0 substep + LabelData labelData = getTickLabelData(font, color, rotation, side, text); + cachedLabel = createCachedLabel(labelData); } - } else + // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels): + bool labelClippedByBorder = false; + /* + if (tickLabelSide == QCPAxis::lsOutside) + { + if (QCPAxis::orientation(type) == Qt::Horizontal) + labelClippedByBorder = labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width()/mParentPlot->bufferDevicePixelRatio() > viewportRect.right() || labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left(); + else + labelClippedByBorder = labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height()/mParentPlot->bufferDevicePixelRatio() > viewportRect.bottom() || labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top(); + } + */ + if (!labelClippedByBorder) + { + painter->drawPixmap(pos+cachedLabel->offset, cachedLabel->pixmap); + finalSize = cachedLabel->pixmap.size()/mParentPlot->bufferDevicePixelRatio(); // TODO: collect this in a member rect list? + } + mLabelCache.insert(QString::fromUtf8(key), cachedLabel); + } else // label caching disabled, draw text directly on surface: { - // handle cases with significantly fractional mantissa: - if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa + LabelData labelData = getTickLabelData(font, color, rotation, side, text); + // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels): + bool labelClippedByBorder = false; + /* + if (tickLabelSide == QCPAxis::lsOutside) { - switch (intPart) + if (QCPAxis::orientation(type) == Qt::Horizontal) + labelClippedByBorder = finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() || finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left(); + else + labelClippedByBorder = finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() || finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top(); + } + */ + if (!labelClippedByBorder) + { + drawText(painter, pos, labelData); + finalSize = labelData.rotatedTotalBounds.size(); + } + } + /* + // expand passed tickLabelsSize if current tick label is larger: + if (finalSize.width() > tickLabelsSize->width()) + tickLabelsSize->setWidth(finalSize.width()); + if (finalSize.height() > tickLabelsSize->height()) + tickLabelsSize->setHeight(finalSize.height()); + */ +} + +QPointF QCPLabelPainterPrivate::getAnchorPos(const QPointF &tickPos) +{ + switch (mAnchorMode) + { + case amRectangular: + { + switch (mAnchorSide) { - case 1: result = 2; break; // 1.5 -> 0.5 substep - case 2: result = 4; break; // 2.5 -> 0.5 substep - case 3: result = 4; break; // 3.5 -> 0.7 substep - case 4: result = 2; break; // 4.5 -> 1.5 substep - case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with default getTickStep from here on) - case 6: result = 4; break; // 6.5 -> 1.3 substep - case 7: result = 2; break; // 7.5 -> 2.5 substep - case 8: result = 4; break; // 8.5 -> 1.7 substep - case 9: result = 4; break; // 9.5 -> 1.9 substep + case asLeft: return tickPos+QPointF(mPadding, 0); + case asRight: return tickPos+QPointF(-mPadding, 0); + case asTop: return tickPos+QPointF(0, mPadding); + case asBottom: return tickPos+QPointF(0, -mPadding); + case asTopLeft: return tickPos+QPointF(mPadding*M_SQRT1_2, mPadding*M_SQRT1_2); + case asTopRight: return tickPos+QPointF(-mPadding*M_SQRT1_2, mPadding*M_SQRT1_2); + case asBottomRight: return tickPos+QPointF(-mPadding*M_SQRT1_2, -mPadding*M_SQRT1_2); + case asBottomLeft: return tickPos+QPointF(mPadding*M_SQRT1_2, -mPadding*M_SQRT1_2); } } - // if mantissa fraction isn't 0.0 or 0.5, don't bother finding good sub tick marks, leave default + case amSkewedUpright: + case amSkewedRotated: + { + QCPVector2D anchorNormal(tickPos-mAnchorReference); + if (mAnchorReferenceType == artTangent) + anchorNormal = anchorNormal.perpendicular(); + anchorNormal.normalize(); + return tickPos+(anchorNormal*mPadding).toPointF(); + } } - - return result; + return tickPos; } /*! \internal - This method returns the tick label string as it should be printed under the \a tick coordinate. - If a textual number is returned, it should respect the provided \a locale, \a formatChar and \a - precision. + This is a \ref placeTickLabel helper function. - If the returned value contains exponentials of the form "2e5" and beautifully typeset powers is - enabled in the QCPAxis number format (\ref QCPAxis::setNumberFormat), the exponential part will - be formatted accordingly using multiplication symbol and superscript during rendering of the - label automatically. + Draws the tick label specified in \a labelData with \a painter at the pixel positions \a x and \a + y. This function is used by \ref placeTickLabel to create new tick labels for the cache, or to + directly draw the labels on the QCustomPlot surface when label caching is disabled, i.e. when + QCP::phCacheLabels plotting hint is not set. */ -QString QCPAxisTicker::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) +void QCPLabelPainterPrivate::drawText(QCPPainter *painter, const QPointF &pos, const LabelData &labelData) const { - return locale.toString(tick, formatChar.toLatin1(), precision); + // backup painter settings that we're about to change: + QTransform oldTransform = painter->transform(); + QFont oldFont = painter->font(); + QPen oldPen = painter->pen(); + + // transform painter to position/rotation: + painter->translate(pos); + painter->setTransform(labelData.transform, true); + + // draw text: + painter->setFont(labelData.baseFont); + painter->setPen(QPen(labelData.color)); + if (!labelData.expPart.isEmpty()) // use superscripted exponent typesetting + { + painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart); + if (!labelData.suffixPart.isEmpty()) + painter->drawText(labelData.baseBounds.width()+1+labelData.expBounds.width(), 0, 0, 0, Qt::TextDontClip, labelData.suffixPart); + painter->setFont(labelData.expFont); + painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip, labelData.expPart); + } else + { + painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart); + } + + /* Debug code to draw label bounding boxes, baseline, and capheight + painter->save(); + painter->setPen(QPen(QColor(0, 0, 0, 150))); + painter->drawRect(labelData.totalBounds); + const int baseline = labelData.totalBounds.height()-mLetterDescent; + painter->setPen(QPen(QColor(255, 0, 0, 150))); + painter->drawLine(QLineF(0, baseline, labelData.totalBounds.width(), baseline)); + painter->setPen(QPen(QColor(0, 0, 255, 150))); + painter->drawLine(QLineF(0, baseline-mLetterCapHeight, labelData.totalBounds.width(), baseline-mLetterCapHeight)); + painter->restore(); + */ + + // reset painter settings to what it was before: + painter->setTransform(oldTransform); + painter->setFont(oldFont); + painter->setPen(oldPen); } /*! \internal - Returns a vector containing all coordinates of sub ticks that should be drawn. It generates \a - subTickCount sub ticks between each tick pair given in \a ticks. + This is a \ref placeTickLabel helper function. - If a QCPAxisTicker subclass needs maximal control over the generated sub ticks, it should - reimplement this method. Depending on the purpose of the subclass it doesn't necessarily need to - base its result on \a subTickCount or \a ticks. + Transforms the passed \a text and \a font to a tickLabelData structure that can then be further + processed by \ref getTickLabelDrawOffset and \ref drawTickLabel. It splits the text into base and + exponent if necessary (member substituteExponent) and calculates appropriate bounding boxes. */ -QVector QCPAxisTicker::createSubTickVector(int subTickCount, const QVector &ticks) +QCPLabelPainterPrivate::LabelData QCPLabelPainterPrivate::getTickLabelData(const QFont &font, const QColor &color, double rotation, AnchorSide side, const QString &text) const { - QVector result; - if (subTickCount <= 0 || ticks.size() < 2) - return result; + LabelData result; + result.rotation = rotation; + result.side = side; + result.color = color; - result.reserve((ticks.size()-1)*subTickCount); - for (int i=1; i 0 && text.at(ePos-1).isDigit()) + { + eLast = ePos; + while (eLast+1 < text.size() && (text.at(eLast+1) == QLatin1Char('+') || text.at(eLast+1) == QLatin1Char('-') || text.at(eLast+1).isDigit())) + ++eLast; + if (eLast > ePos) // only if also to right of 'e' is a digit/+/- interpret it as beautifiable power + useBeautifulPowers = true; + } } - return result; -} - -/*! \internal - Returns a vector containing all coordinates of ticks that should be drawn. The default - implementation generates ticks with a spacing of \a tickStep (mathematically starting at the tick - step origin, see \ref setTickOrigin) distributed over the passed \a range. + // calculate text bounding rects and do string preparation for beautiful decimal powers: + result.baseFont = font; + if (result.baseFont.pointSizeF() > 0) // might return -1 if specified with setPixelSize, in that case we can't do correction in next line + result.baseFont.setPointSizeF(result.baseFont.pointSizeF()+0.05); // QFontMetrics.boundingRect has a bug for exact point sizes that make the results oscillate due to internal rounding - In order for the axis ticker to generate proper sub ticks, it is necessary that the first and - last tick coordinates returned by this method are just below/above the provided \a range. - Otherwise the outer intervals won't contain any sub ticks. + QFontMetrics baseFontMetrics(result.baseFont); + if (useBeautifulPowers) + { + // split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent: + result.basePart = text.left(ePos); + result.suffixPart = text.mid(eLast+1); // also drawn normally but after exponent + // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base: + if (mAbbreviateDecimalPowers && result.basePart == QLatin1String("1")) + result.basePart = QLatin1String("10"); + else + result.basePart += QString(mMultiplicationSymbol) + QLatin1String("10"); + result.expPart = text.mid(ePos+1, eLast-ePos); + // clip "+" and leading zeros off expPart: + while (result.expPart.length() > 2 && result.expPart.at(1) == QLatin1Char('0')) // length > 2 so we leave one zero when numberFormatChar is 'e' + result.expPart.remove(1, 1); + if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+')) + result.expPart.remove(0, 1); + // prepare smaller font for exponent: + result.expFont = font; + if (result.expFont.pointSize() > 0) + result.expFont.setPointSize(result.expFont.pointSize()*0.75); + else + result.expFont.setPixelSize(result.expFont.pixelSize()*0.75); + // calculate bounding rects of base part(s), exponent part and total one: + result.baseBounds = baseFontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart); + result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart); + if (!result.suffixPart.isEmpty()) + result.suffixBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.suffixPart); + result.totalBounds = result.baseBounds.adjusted(0, 0, result.expBounds.width()+result.suffixBounds.width()+2, 0); // +2 consists of the 1 pixel spacing between base and exponent (see drawTickLabel) and an extra pixel to include AA + } else // useBeautifulPowers == false + { + result.basePart = text; + result.totalBounds = baseFontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart); + } + result.totalBounds.moveTopLeft(QPoint(0, 0)); + applyAnchorTransform(result); + result.rotatedTotalBounds = result.transform.mapRect(result.totalBounds); - If a QCPAxisTicker subclass needs maximal control over the generated ticks, it should reimplement - this method. Depending on the purpose of the subclass it doesn't necessarily need to base its - result on \a tickStep, e.g. when the ticks are spaced unequally like in the case of - QCPAxisTickerLog. -*/ -QVector QCPAxisTicker::createTickVector(double tickStep, const QCPRange &range) -{ - QVector result; - // Generate tick positions according to tickStep: - qint64 firstStep = floor((range.lower-mTickOrigin)/tickStep); // do not use qFloor here, or we'll lose 64 bit precision - qint64 lastStep = ceil((range.upper-mTickOrigin)/tickStep); // do not use qCeil here, or we'll lose 64 bit precision - int tickcount = lastStep-firstStep+1; - if (tickcount < 0) tickcount = 0; - result.resize(tickcount); - for (int i=0; i QCPAxisTicker::createLabelVector(const QVector &ticks, const QLocale &locale, QChar formatChar, int precision) -{ - QVector result; - result.reserve(ticks.size()); - for (int i=0; i &ticks, bool keepOneOutlier) const +/* +void QCPLabelPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const { - bool lowFound = false; - bool highFound = false; - int lowIndex = 0; - int highIndex = -1; - - for (int i=0; i < ticks.size(); ++i) + // note: this function must return the same tick label sizes as the placeTickLabel function. + QSize finalSize; + if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label { - if (ticks.at(i) >= range.lower) - { - lowFound = true; - lowIndex = i; - break; - } - } - for (int i=ticks.size()-1; i >= 0; --i) + const CachedLabel *cachedLabel = mLabelCache.object(text); + finalSize = cachedLabel->pixmap.size()/mParentPlot->bufferDevicePixelRatio(); + } else // label caching disabled or no label with this text cached: { - if (ticks.at(i) <= range.upper) - { - highFound = true; - highIndex = i; - break; - } + // TODO: LabelData labelData = getTickLabelData(font, text); + // TODO: finalSize = labelData.rotatedTotalBounds.size(); } - if (highFound && lowFound) - { - int trimFront = qMax(0, lowIndex-(keepOneOutlier ? 1 : 0)); - int trimBack = qMax(0, ticks.size()-(keepOneOutlier ? 2 : 1)-highIndex); - if (trimFront > 0 || trimBack > 0) - ticks = ticks.mid(trimFront, ticks.size()-trimFront-trimBack); - } else // all ticks are either all below or all above the range - ticks.clear(); + // expand passed tickLabelsSize if current tick label is larger: + if (finalSize.width() > tickLabelsSize->width()) + tickLabelsSize->setWidth(finalSize.width()); + if (finalSize.height() > tickLabelsSize->height()) + tickLabelsSize->setHeight(finalSize.height()); } +*/ -/*! \internal - - Returns the coordinate contained in \a candidates which is closest to the provided \a target. +QCPLabelPainterPrivate::CachedLabel *QCPLabelPainterPrivate::createCachedLabel(const LabelData &labelData) const +{ + CachedLabel *result = new CachedLabel; - This method assumes \a candidates is not empty and sorted in ascending order. -*/ -double QCPAxisTicker::pickClosest(double target, const QVector &candidates) const + // allocate pixmap with the correct size and pixel ratio: + if (!qFuzzyCompare(1.0, mParentPlot->bufferDevicePixelRatio())) + { + result->pixmap = QPixmap(labelData.rotatedTotalBounds.size()*mParentPlot->bufferDevicePixelRatio()); +#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED +# ifdef QCP_DEVICEPIXELRATIO_FLOAT + result->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatioF()); +# else + result->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatio()); +# endif +#endif + } else + result->pixmap = QPixmap(labelData.rotatedTotalBounds.size()); + result->pixmap.fill(Qt::transparent); + + // draw the label into the pixmap + // offset is between label anchor and topleft of cache pixmap, so pixmap can be drawn at pos+offset to make the label anchor appear at pos. + // We use rotatedTotalBounds.topLeft() because rotatedTotalBounds is in a coordinate system where the label anchor is at (0, 0) + result->offset = labelData.rotatedTotalBounds.topLeft(); + QCPPainter cachePainter(&result->pixmap); + drawText(&cachePainter, -result->offset, labelData); + return result; +} + +QByteArray QCPLabelPainterPrivate::cacheKey(const QString &text, const QColor &color, double rotation, AnchorSide side) const { - if (candidates.size() == 1) - return candidates.first(); - QVector::const_iterator it = std::lower_bound(candidates.constBegin(), candidates.constEnd(), target); - if (it == candidates.constEnd()) - return *(it-1); - else if (it == candidates.constBegin()) - return *it; - else - return target-*(it-1) < *it-target ? *(it-1) : *it; + return text.toUtf8()+ + QByteArray::number(color.red()+256*color.green()+65536*color.blue(), 36)+ + QByteArray::number(color.alpha()+256*(int)side, 36)+ + QByteArray::number((int)(rotation*100)%36000, 36); } -/*! \internal - - Returns the decimal mantissa of \a input. Optionally, if \a magnitude is not set to zero, it also - returns the magnitude of \a input as a power of 10. - - For example, an input of 142.6 will return a mantissa of 1.426 and a magnitude of 100. -*/ -double QCPAxisTicker::getMantissa(double input, double *magnitude) const +QCPLabelPainterPrivate::AnchorSide QCPLabelPainterPrivate::skewedAnchorSide(const QPointF &tickPos, double sideExpandHorz, double sideExpandVert) const { - const double mag = qPow(10.0, qFloor(qLn(input)/qLn(10.0))); - if (magnitude) *magnitude = mag; - return input/mag; + QCPVector2D anchorNormal = QCPVector2D(tickPos-mAnchorReference); + if (mAnchorReferenceType == artTangent) + anchorNormal = anchorNormal.perpendicular(); + const double radius = anchorNormal.length(); + const double sideHorz = sideExpandHorz*radius; + const double sideVert = sideExpandVert*radius; + if (anchorNormal.x() > sideHorz) + { + if (anchorNormal.y() > sideVert) return asTopLeft; + else if (anchorNormal.y() < -sideVert) return asBottomLeft; + else return asLeft; + } else if (anchorNormal.x() < -sideHorz) + { + if (anchorNormal.y() > sideVert) return asTopRight; + else if (anchorNormal.y() < -sideVert) return asBottomRight; + else return asRight; + } else + { + if (anchorNormal.y() > 0) return asTop; + else return asBottom; + } + return asBottom; // should never be reached } -/*! \internal - - Returns a number that is close to \a input but has a clean, easier human readable mantissa. How - strongly the mantissa is altered, and thus how strong the result deviates from the original \a - input, depends on the current tick step strategy (see \ref setTickStepStrategy). -*/ -double QCPAxisTicker::cleanMantissa(double input) const +QCPLabelPainterPrivate::AnchorSide QCPLabelPainterPrivate::rotationCorrectedSide(AnchorSide side, double rotation) const { - double magnitude; - const double mantissa = getMantissa(input, &magnitude); - switch (mTickStepStrategy) + AnchorSide result = side; + const bool rotateClockwise = rotation > 0; + if (!qFuzzyIsNull(rotation)) { - case tssReadability: + if (!qFuzzyCompare(qAbs(rotation), 90)) // avoid graphical collision with anchor tangent (e.g. axis line) when rotating, so change anchor side appropriately: { - return pickClosest(mantissa, QVector() << 1.0 << 2.0 << 2.5 << 5.0 << 10.0)*magnitude; - } - case tssMeetTickCount: + if (side == asTop) result = rotateClockwise ? asLeft : asRight; + else if (side == asBottom) result = rotateClockwise ? asRight : asLeft; + else if (side == asTopLeft) result = rotateClockwise ? asLeft : asTop; + else if (side == asTopRight) result = rotateClockwise ? asTop : asRight; + else if (side == asBottomLeft) result = rotateClockwise ? asBottom : asLeft; + else if (side == asBottomRight) result = rotateClockwise ? asRight : asBottom; + } else // for full rotation by +/-90 degrees, other sides are more appropriate for centering on anchor: { - // this gives effectively a mantissa of 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 8.0, 10.0 - if (mantissa <= 5.0) - return (int)(mantissa*2)/2.0*magnitude; // round digit after decimal point to 0.5 - else - return (int)(mantissa/2.0)*2.0*magnitude; // round to first digit in multiples of 2 + if (side == asLeft) result = rotateClockwise ? asBottom : asTop; + else if (side == asRight) result = rotateClockwise ? asTop : asBottom; + else if (side == asTop) result = rotateClockwise ? asLeft : asRight; + else if (side == asBottom) result = rotateClockwise ? asRight : asLeft; + else if (side == asTopLeft) result = rotateClockwise ? asBottomLeft : asTopRight; + else if (side == asTopRight) result = rotateClockwise ? asTopLeft : asBottomRight; + else if (side == asBottomLeft) result = rotateClockwise ? asBottomRight : asTopLeft; + else if (side == asBottomRight) result = rotateClockwise ? asTopRight : asBottomLeft; } } - return input; + return result; } -/* end of 'src/axis/axisticker.cpp' */ + +void QCPLabelPainterPrivate::analyzeFontMetrics() +{ + const QFontMetrics fm(mFont); + mLetterCapHeight = fm.tightBoundingRect(QLatin1String("8")).height(); // this method is slow, that's why we query it only upon font change + mLetterDescent = fm.descent(); +} +/* end of 'src/axis/labelpainter.cpp' */ -/* including file 'src/axis/axistickerdatetime.cpp', size 14443 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/axis/axisticker.cpp' */ +/* modified 2021-03-29T02:30:44, size 18688 */ //////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////// QCPAxisTickerDateTime +//////////////////// QCPAxisTicker //////////////////////////////////////////////////////////////////////////////////////////////////// -/*! \class QCPAxisTickerDateTime - \brief Specialized axis ticker for calendar dates and times as axis ticks +/*! \class QCPAxisTicker + \brief The base class tick generator used by QCPAxis to create tick positions and tick labels - \image html axisticker-datetime.png + Each QCPAxis has an internal QCPAxisTicker (or a subclass) in order to generate tick positions + and tick labels for the current axis range. The ticker of an axis can be set via \ref + QCPAxis::setTicker. Since that method takes a QSharedPointer, multiple + axes can share the same ticker instance. - This QCPAxisTicker subclass generates ticks that correspond to real calendar dates and times. The - plot axis coordinate is interpreted as Unix Time, so seconds since Epoch (January 1, 1970, 00:00 - UTC). This is also used for example by QDateTime in the toTime_t()/setTime_t() methods - with a precision of one second. Since Qt 4.7, millisecond accuracy can be obtained from QDateTime - by using QDateTime::fromMSecsSinceEpoch()/1000.0. The static methods \ref dateTimeToKey - and \ref keyToDateTime conveniently perform this conversion achieving a precision of one - millisecond on all Qt versions. + This base class generates normal tick coordinates and numeric labels for linear axes. It picks a + reasonable tick step (the separation between ticks) which results in readable tick labels. The + number of ticks that should be approximately generated can be set via \ref setTickCount. + Depending on the current tick step strategy (\ref setTickStepStrategy), the algorithm either + sacrifices readability to better match the specified tick count (\ref + QCPAxisTicker::tssMeetTickCount) or relaxes the tick count in favor of better tick steps (\ref + QCPAxisTicker::tssReadability), which is the default. - The format of the date/time display in the tick labels is controlled with \ref setDateTimeFormat. - If a different time spec (time zone) shall be used, see \ref setDateTimeSpec. + The following more specialized axis ticker subclasses are available, see details in the + respective class documentation: - This ticker produces unequal tick spacing in order to provide intuitive date and time-of-day - ticks. For example, if the axis range spans a few years such that there is one tick per year, - ticks will be positioned on 1. January of every year. This is intuitive but, due to leap years, - will result in slightly unequal tick intervals (visually unnoticeable). The same can be seen in - the image above: even though the number of days varies month by month, this ticker generates - ticks on the same day of each month. +
+ + + + + + + +
QCPAxisTickerFixed\image html axisticker-fixed.png
QCPAxisTickerLog\image html axisticker-log.png
QCPAxisTickerPi\image html axisticker-pi.png
QCPAxisTickerText\image html axisticker-text.png
QCPAxisTickerDateTime\image html axisticker-datetime.png
QCPAxisTickerTime\image html axisticker-time.png + \image html axisticker-time2.png
+
- If you would like to change the date/time that is used as a (mathematical) starting date for the - ticks, use the \ref setTickOrigin(const QDateTime &origin) method overload, which takes a - QDateTime. If you pass 15. July, 9:45 to this method, the yearly ticks will end up on 15. July at - 9:45 of every year. + \section axisticker-subclassing Creating own axis tickers - The ticker can be created and assigned to an axis like this: - \snippet documentation/doc-image-generator/mainwindow.cpp axistickerdatetime-creation + Creating own axis tickers can be achieved very easily by sublassing QCPAxisTicker and + reimplementing some or all of the available virtual methods. + + In the simplest case you might wish to just generate different tick steps than the other tickers, + so you only reimplement the method \ref getTickStep. If you additionally want control over the + string that will be shown as tick label, reimplement \ref getTickLabel. - \note If you rather wish to display relative times in terms of days, hours, minutes, seconds and - milliseconds, and are not interested in the intricacies of real calendar dates with months and - (leap) years, have a look at QCPAxisTickerTime instead. + If you wish to have complete control, you can generate the tick vectors and tick label vectors + yourself by reimplementing \ref createTickVector and \ref createLabelVector. The default + implementations use the previously mentioned virtual methods \ref getTickStep and \ref + getTickLabel, but your reimplementations don't necessarily need to do so. For example in the case + of unequal tick steps, the method \ref getTickStep loses its usefulness and can be ignored. + + The sub tick count between major ticks can be controlled with \ref getSubTickCount. Full sub tick + placement control is obtained by reimplementing \ref createSubTickVector. + + See the documentation of all these virtual methods in QCPAxisTicker for detailed information + about the parameters and expected return values. */ /*! Constructs the ticker and sets reasonable default values. Axis tickers are commonly created managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker. */ -QCPAxisTickerDateTime::QCPAxisTickerDateTime() : - mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")), - mDateTimeSpec(Qt::LocalTime), - mDateStrategy(dsNone) +QCPAxisTicker::QCPAxisTicker() : + mTickStepStrategy(tssReadability), + mTickCount(5), + mTickOrigin(0) { - setTickCount(4); } -/*! - Sets the format in which dates and times are displayed as tick labels. For details about the \a - format string, see the documentation of QDateTime::toString(). - - Newlines can be inserted with "\n". +QCPAxisTicker::~QCPAxisTicker() +{ - \see setDateTimeSpec +} + +/*! + Sets which strategy the axis ticker follows when choosing the size of the tick step. For the + available strategies, see \ref TickStepStrategy. */ -void QCPAxisTickerDateTime::setDateTimeFormat(const QString &format) +void QCPAxisTicker::setTickStepStrategy(QCPAxisTicker::TickStepStrategy strategy) { - mDateTimeFormat = format; + mTickStepStrategy = strategy; } /*! - Sets the time spec that is used for creating the tick labels from corresponding dates/times. + Sets how many ticks this ticker shall aim to generate across the axis range. Note that \a count + is not guaranteed to be matched exactly, as generating readable tick intervals may conflict with + the requested number of ticks. - The default value of QDateTime objects (and also QCPAxisTickerDateTime) is - Qt::LocalTime. However, if the date time values passed to QCustomPlot (e.g. in the form - of axis ranges or keys of a plottable) are given in the UTC spec, set \a spec to Qt::UTC - to get the correct axis labels. - - \see setDateTimeFormat + Whether the readability has priority over meeting the requested \a count can be specified with + \ref setTickStepStrategy. */ -void QCPAxisTickerDateTime::setDateTimeSpec(Qt::TimeSpec spec) +void QCPAxisTicker::setTickCount(int count) { - mDateTimeSpec = spec; + if (count > 0) + mTickCount = count; + else + qDebug() << Q_FUNC_INFO << "tick count must be greater than zero:" << count; } /*! - Sets the tick origin (see \ref QCPAxisTicker::setTickOrigin) in seconds since Epoch (1. Jan 1970, - 00:00 UTC). For the date time ticker it might be more intuitive to use the overload which - directly takes a QDateTime, see \ref setTickOrigin(const QDateTime &origin). + Sets the mathematical coordinate (or "offset") of the zeroth tick. This tick coordinate is just a + concept and doesn't need to be inside the currently visible axis range. - This is useful to define the month/day/time recurring at greater tick interval steps. For - example, If you pass 15. July, 9:45 to this method and the tick interval happens to be one tick - per year, the ticks will end up on 15. July at 9:45 of every year. + By default \a origin is zero, which for example yields ticks {-5, 0, 5, 10, 15,...} when the tick + step is five. If \a origin is now set to 1 instead, the correspondingly generated ticks would be + {-4, 1, 6, 11, 16,...}. */ -void QCPAxisTickerDateTime::setTickOrigin(double origin) +void QCPAxisTicker::setTickOrigin(double origin) { - QCPAxisTicker::setTickOrigin(origin); + mTickOrigin = origin; } /*! - Sets the tick origin (see \ref QCPAxisTicker::setTickOrigin) as a QDateTime \a origin. + This is the method called by QCPAxis in order to actually generate tick coordinates (\a ticks), + tick label strings (\a tickLabels) and sub tick coordinates (\a subTicks). - This is useful to define the month/day/time recurring at greater tick interval steps. For - example, If you pass 15. July, 9:45 to this method and the tick interval happens to be one tick - per year, the ticks will end up on 15. July at 9:45 of every year. + The ticks are generated for the specified \a range. The generated labels typically follow the + specified \a locale, \a formatChar and number \a precision, however this might be different (or + even irrelevant) for certain QCPAxisTicker subclasses. + + The output parameter \a ticks is filled with the generated tick positions in axis coordinates. + The output parameters \a subTicks and \a tickLabels are optional (set them to \c nullptr if not + needed) and are respectively filled with sub tick coordinates, and tick label strings belonging + to \a ticks by index. */ -void QCPAxisTickerDateTime::setTickOrigin(const QDateTime &origin) +void QCPAxisTicker::generate(const QCPRange &range, const QLocale &locale, QChar formatChar, int precision, QVector &ticks, QVector *subTicks, QVector *tickLabels) { - setTickOrigin(dateTimeToKey(origin)); + // generate (major) ticks: + double tickStep = getTickStep(range); + ticks = createTickVector(tickStep, range); + trimTicks(range, ticks, true); // trim ticks to visible range plus one outer tick on each side (incase a subclass createTickVector creates more) + + // generate sub ticks between major ticks: + if (subTicks) + { + if (!ticks.isEmpty()) + { + *subTicks = createSubTickVector(getSubTickCount(tickStep), ticks); + trimTicks(range, *subTicks, false); + } else + *subTicks = QVector(); + } + + // finally trim also outliers (no further clipping happens in axis drawing): + trimTicks(range, ticks, false); + // generate labels for visible ticks if requested: + if (tickLabels) + *tickLabels = createLabelVector(ticks, locale, formatChar, precision); } /*! \internal - Returns a sensible tick step with intervals appropriate for a date-time-display, such as weekly, - monthly, bi-monthly, etc. + Takes the entire currently visible axis range and returns a sensible tick step in + order to provide readable tick labels as well as a reasonable number of tick counts (see \ref + setTickCount, \ref setTickStepStrategy). - Note that this tick step isn't used exactly when generating the tick vector in \ref - createTickVector, but only as a guiding value requiring some correction for each individual tick - interval. Otherwise this would lead to unintuitive date displays, e.g. jumping between first day - in the month to the last day in the previous month from tick to tick, due to the non-uniform - length of months. The same problem arises with leap years. - - \seebaseclassmethod + If a QCPAxisTicker subclass only wants a different tick step behaviour than the default + implementation, it should reimplement this method. See \ref cleanMantissa for a possible helper + function. */ -double QCPAxisTickerDateTime::getTickStep(const QCPRange &range) +double QCPAxisTicker::getTickStep(const QCPRange &range) { - double result = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers - - mDateStrategy = dsNone; - if (result < 1) // ideal tick step is below 1 second -> use normal clean mantissa algorithm in units of seconds - { - result = cleanMantissa(result); - } else if (result < 86400*30.4375*12) // below a year - { - result = pickClosest(result, QVector() - << 1 << 2.5 << 5 << 10 << 15 << 30 << 60 << 2.5*60 << 5*60 << 10*60 << 15*60 << 30*60 << 60*60 // second, minute, hour range - << 3600*2 << 3600*3 << 3600*6 << 3600*12 << 3600*24 // hour to day range - << 86400*2 << 86400*5 << 86400*7 << 86400*14 << 86400*30.4375 << 86400*30.4375*2 << 86400*30.4375*3 << 86400*30.4375*6 << 86400*30.4375*12); // day, week, month range (avg. days per month includes leap years) - if (result > 86400*30.4375-1) // month tick intervals or larger - mDateStrategy = dsUniformDayInMonth; - else if (result > 3600*24-1) // day tick intervals or larger - mDateStrategy = dsUniformTimeInDay; - } else // more than a year, go back to normal clean mantissa algorithm but in units of years - { - const double secondsPerYear = 86400*30.4375*12; // average including leap years - result = cleanMantissa(result/secondsPerYear)*secondsPerYear; - mDateStrategy = dsUniformDayInMonth; - } - return result; + double exactStep = range.size()/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers + return cleanMantissa(exactStep); } /*! \internal - Returns a sensible sub tick count with intervals appropriate for a date-time-display, such as weekly, - monthly, bi-monthly, etc. + Takes the \a tickStep, i.e. the distance between two consecutive ticks, and returns + an appropriate number of sub ticks for that specific tick step. - \seebaseclassmethod + Note that a returned sub tick count of e.g. 4 will split each tick interval into 5 sections. */ -int QCPAxisTickerDateTime::getSubTickCount(double tickStep) +int QCPAxisTicker::getSubTickCount(double tickStep) { - int result = QCPAxisTicker::getSubTickCount(tickStep); - switch (qRound(tickStep)) // hand chosen subticks for specific minute/hour/day/week/month range (as specified in getTickStep) + int result = 1; // default to 1, if no proper value can be found + + // separate integer and fractional part of mantissa: + double epsilon = 0.01; + double intPartf; + int intPart; + double fracPart = modf(getMantissa(tickStep), &intPartf); + intPart = int(intPartf); + + // handle cases with (almost) integer mantissa: + if (fracPart < epsilon || 1.0-fracPart < epsilon) { - case 5*60: result = 4; break; - case 10*60: result = 1; break; - case 15*60: result = 2; break; - case 30*60: result = 1; break; - case 60*60: result = 3; break; - case 3600*2: result = 3; break; - case 3600*3: result = 2; break; - case 3600*6: result = 1; break; - case 3600*12: result = 3; break; - case 3600*24: result = 3; break; - case 86400*2: result = 1; break; - case 86400*5: result = 4; break; - case 86400*7: result = 6; break; - case 86400*14: result = 1; break; - case (int)(86400*30.4375+0.5): result = 3; break; - case (int)(86400*30.4375*2+0.5): result = 1; break; - case (int)(86400*30.4375*3+0.5): result = 2; break; - case (int)(86400*30.4375*6+0.5): result = 5; break; - case (int)(86400*30.4375*12+0.5): result = 3; break; + if (1.0-fracPart < epsilon) + ++intPart; + switch (intPart) + { + case 1: result = 4; break; // 1.0 -> 0.2 substep + case 2: result = 3; break; // 2.0 -> 0.5 substep + case 3: result = 2; break; // 3.0 -> 1.0 substep + case 4: result = 3; break; // 4.0 -> 1.0 substep + case 5: result = 4; break; // 5.0 -> 1.0 substep + case 6: result = 2; break; // 6.0 -> 2.0 substep + case 7: result = 6; break; // 7.0 -> 1.0 substep + case 8: result = 3; break; // 8.0 -> 2.0 substep + case 9: result = 2; break; // 9.0 -> 3.0 substep + } + } else + { + // handle cases with significantly fractional mantissa: + if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa + { + switch (intPart) + { + case 1: result = 2; break; // 1.5 -> 0.5 substep + case 2: result = 4; break; // 2.5 -> 0.5 substep + case 3: result = 4; break; // 3.5 -> 0.7 substep + case 4: result = 2; break; // 4.5 -> 1.5 substep + case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with default getTickStep from here on) + case 6: result = 4; break; // 6.5 -> 1.3 substep + case 7: result = 2; break; // 7.5 -> 2.5 substep + case 8: result = 4; break; // 8.5 -> 1.7 substep + case 9: result = 4; break; // 9.5 -> 1.9 substep + } + } + // if mantissa fraction isn't 0.0 or 0.5, don't bother finding good sub tick marks, leave default } + return result; } /*! \internal - Generates a date/time tick label for tick coordinate \a tick, based on the currently set format - (\ref setDateTimeFormat) and time spec (\ref setDateTimeSpec). + This method returns the tick label string as it should be printed under the \a tick coordinate. + If a textual number is returned, it should respect the provided \a locale, \a formatChar and \a + precision. - \seebaseclassmethod + If the returned value contains exponentials of the form "2e5" and beautifully typeset powers is + enabled in the QCPAxis number format (\ref QCPAxis::setNumberFormat), the exponential part will + be formatted accordingly using multiplication symbol and superscript during rendering of the + label automatically. */ -QString QCPAxisTickerDateTime::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) +QString QCPAxisTicker::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) { - Q_UNUSED(precision) - Q_UNUSED(formatChar) - return locale.toString(keyToDateTime(tick).toTimeSpec(mDateTimeSpec), mDateTimeFormat); + return locale.toString(tick, formatChar.toLatin1(), precision); } /*! \internal - Uses the passed \a tickStep as a guiding value and applies corrections in order to obtain - non-uniform tick intervals but intuitive tick labels, e.g. falling on the same day of each month. + Returns a vector containing all coordinates of sub ticks that should be drawn. It generates \a + subTickCount sub ticks between each tick pair given in \a ticks. - \seebaseclassmethod + If a QCPAxisTicker subclass needs maximal control over the generated sub ticks, it should + reimplement this method. Depending on the purpose of the subclass it doesn't necessarily need to + base its result on \a subTickCount or \a ticks. */ -QVector QCPAxisTickerDateTime::createTickVector(double tickStep, const QCPRange &range) +QVector QCPAxisTicker::createSubTickVector(int subTickCount, const QVector &ticks) { - QVector result = QCPAxisTicker::createTickVector(tickStep, range); - if (!result.isEmpty()) + QVector result; + if (subTickCount <= 0 || ticks.size() < 2) + return result; + + result.reserve((ticks.size()-1)*subTickCount); + for (int i=1; i 15) // with leap years involved, date month may jump backwards or forwards, and needs to be corrected before setting day - tickDateTime = tickDateTime.addMonths(-1); - tickDateTime.setDate(QDate(tickDateTime.date().year(), tickDateTime.date().month(), thisUniformDay)); - result[i] = dateTimeToKey(tickDateTime); - } - } + double subTickStep = (ticks.at(i)-ticks.at(i-1))/double(subTickCount+1); + for (int k=1; k<=subTickCount; ++k) + result.append(ticks.at(i-1) + k*subTickStep); } return result; } -/*! - A convenience method which turns \a key (in seconds since Epoch 1. Jan 1970, 00:00 UTC) into a - QDateTime object. This can be used to turn axis coordinates to actual QDateTimes. +/*! \internal - The accuracy achieved by this method is one millisecond, irrespective of the used Qt version (it - works around the lack of a QDateTime::fromMSecsSinceEpoch in Qt 4.6) + Returns a vector containing all coordinates of ticks that should be drawn. The default + implementation generates ticks with a spacing of \a tickStep (mathematically starting at the tick + step origin, see \ref setTickOrigin) distributed over the passed \a range. - \see dateTimeToKey + In order for the axis ticker to generate proper sub ticks, it is necessary that the first and + last tick coordinates returned by this method are just below/above the provided \a range. + Otherwise the outer intervals won't contain any sub ticks. + + If a QCPAxisTicker subclass needs maximal control over the generated ticks, it should reimplement + this method. Depending on the purpose of the subclass it doesn't necessarily need to base its + result on \a tickStep, e.g. when the ticks are spaced unequally like in the case of + QCPAxisTickerLog. */ -QDateTime QCPAxisTickerDateTime::keyToDateTime(double key) +QVector QCPAxisTicker::createTickVector(double tickStep, const QCPRange &range) { -# if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) - return QDateTime::fromTime_t(key).addMSecs((key-(qint64)key)*1000); -# else - return QDateTime::fromMSecsSinceEpoch(key*1000.0); -# endif + QVector result; + // Generate tick positions according to tickStep: + qint64 firstStep = qint64(floor((range.lower-mTickOrigin)/tickStep)); // do not use qFloor here, or we'll lose 64 bit precision + qint64 lastStep = qint64(ceil((range.upper-mTickOrigin)/tickStep)); // do not use qCeil here, or we'll lose 64 bit precision + int tickcount = int(lastStep-firstStep+1); + if (tickcount < 0) tickcount = 0; + result.resize(tickcount); + for (int i=0; i QCPAxisTicker::createLabelVector(const QVector &ticks, const QLocale &locale, QChar formatChar, int precision) { -# if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) - return dateTime.toTime_t()+dateTime.time().msec()/1000.0; -# else - return dateTime.toMSecsSinceEpoch()/1000.0; -# endif + QVector result; + result.reserve(ticks.size()); + foreach (double tickCoord, ticks) + result.append(getTickLabel(tickCoord, locale, formatChar, precision)); + return result; } -/*! \overload +/*! \internal - A convenience method which turns a QDate object into a double value that corresponds to - seconds since Epoch (1. Jan 1970, 00:00 UTC). This is the format used as axis coordinates by - QCPAxisTickerDateTime. + Removes tick coordinates from \a ticks which lie outside the specified \a range. If \a + keepOneOutlier is true, it preserves one tick just outside the range on both sides, if present. - \see keyToDateTime + The passed \a ticks must be sorted in ascending order. */ -double QCPAxisTickerDateTime::dateTimeToKey(const QDate date) +void QCPAxisTicker::trimTicks(const QCPRange &range, QVector &ticks, bool keepOneOutlier) const { -# if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) - return QDateTime(date).toTime_t(); -# else - return QDateTime(date).toMSecsSinceEpoch()/1000.0; -# endif -} -/* end of 'src/axis/axistickerdatetime.cpp' */ - - -/* including file 'src/axis/axistickertime.cpp', size 11747 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////// QCPAxisTickerTime -//////////////////////////////////////////////////////////////////////////////////////////////////// -/*! \class QCPAxisTickerTime - \brief Specialized axis ticker for time spans in units of milliseconds to days - - \image html axisticker-time.png - - This QCPAxisTicker subclass generates ticks that corresponds to time intervals. - - The format of the time display in the tick labels is controlled with \ref setTimeFormat and \ref - setFieldWidth. The time coordinate is in the unit of seconds with respect to the time coordinate - zero. Unlike with QCPAxisTickerDateTime, the ticks don't correspond to a specific calendar date - and time. + bool lowFound = false; + bool highFound = false; + int lowIndex = 0; + int highIndex = -1; - The time can be displayed in milliseconds, seconds, minutes, hours and days. Depending on the - largest available unit in the format specified with \ref setTimeFormat, any time spans above will - be carried in that largest unit. So for example if the format string is "%m:%s" and a tick at - coordinate value 7815 (being 2 hours, 10 minutes and 15 seconds) is created, the resulting tick - label will show "130:15" (130 minutes, 15 seconds). If the format string is "%h:%m:%s", the hour - unit will be used and the label will thus be "02:10:15". Negative times with respect to the axis - zero will carry a leading minus sign. + for (int i=0; i < ticks.size(); ++i) + { + if (ticks.at(i) >= range.lower) + { + lowFound = true; + lowIndex = i; + break; + } + } + for (int i=ticks.size()-1; i >= 0; --i) + { + if (ticks.at(i) <= range.upper) + { + highFound = true; + highIndex = i; + break; + } + } - The ticker can be created and assigned to an axis like this: - \snippet documentation/doc-image-generator/mainwindow.cpp axistickertime-creation + if (highFound && lowFound) + { + int trimFront = qMax(0, lowIndex-(keepOneOutlier ? 1 : 0)); + int trimBack = qMax(0, ticks.size()-(keepOneOutlier ? 2 : 1)-highIndex); + if (trimFront > 0 || trimBack > 0) + ticks = ticks.mid(trimFront, ticks.size()-trimFront-trimBack); + } else // all ticks are either all below or all above the range + ticks.clear(); +} + +/*! \internal - Here is an example of a time axis providing time information in days, hours and minutes. Due to - the axis range spanning a few days and the wanted tick count (\ref setTickCount), the ticker - decided to use tick steps of 12 hours: + Returns the coordinate contained in \a candidates which is closest to the provided \a target. - \image html axisticker-time2.png + This method assumes \a candidates is not empty and sorted in ascending order. +*/ +double QCPAxisTicker::pickClosest(double target, const QVector &candidates) const +{ + if (candidates.size() == 1) + return candidates.first(); + QVector::const_iterator it = std::lower_bound(candidates.constBegin(), candidates.constEnd(), target); + if (it == candidates.constEnd()) + return *(it-1); + else if (it == candidates.constBegin()) + return *it; + else + return target-*(it-1) < *it-target ? *(it-1) : *it; +} + +/*! \internal - The format string for this example is - \snippet documentation/doc-image-generator/mainwindow.cpp axistickertime-creation-2 + Returns the decimal mantissa of \a input. Optionally, if \a magnitude is not set to zero, it also + returns the magnitude of \a input as a power of 10. - \note If you rather wish to display calendar dates and times, have a look at QCPAxisTickerDateTime - instead. + For example, an input of 142.6 will return a mantissa of 1.426 and a magnitude of 100. +*/ +double QCPAxisTicker::getMantissa(double input, double *magnitude) const +{ + const double mag = qPow(10.0, qFloor(qLn(input)/qLn(10.0))); + if (magnitude) *magnitude = mag; + return input/mag; +} + +/*! \internal + + Returns a number that is close to \a input but has a clean, easier human readable mantissa. How + strongly the mantissa is altered, and thus how strong the result deviates from the original \a + input, depends on the current tick step strategy (see \ref setTickStepStrategy). +*/ +double QCPAxisTicker::cleanMantissa(double input) const +{ + double magnitude; + const double mantissa = getMantissa(input, &magnitude); + switch (mTickStepStrategy) + { + case tssReadability: + { + return pickClosest(mantissa, QVector() << 1.0 << 2.0 << 2.5 << 5.0 << 10.0)*magnitude; + } + case tssMeetTickCount: + { + // this gives effectively a mantissa of 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 8.0, 10.0 + if (mantissa <= 5.0) + return int(mantissa*2)/2.0*magnitude; // round digit after decimal point to 0.5 + else + return int(mantissa/2.0)*2.0*magnitude; // round to first digit in multiples of 2 + } + } + return input; +} +/* end of 'src/axis/axisticker.cpp' */ + + +/* including file 'src/axis/axistickerdatetime.cpp' */ +/* modified 2021-03-29T02:30:44, size 18829 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAxisTickerDateTime +//////////////////////////////////////////////////////////////////////////////////////////////////// +/*! \class QCPAxisTickerDateTime + \brief Specialized axis ticker for calendar dates and times as axis ticks + + \image html axisticker-datetime.png + + This QCPAxisTicker subclass generates ticks that correspond to real calendar dates and times. The + plot axis coordinate is interpreted as Unix Time, so seconds since Epoch (January 1, 1970, 00:00 + UTC). This is also used for example by QDateTime in the toTime_t()/setTime_t() methods + with a precision of one second. Since Qt 4.7, millisecond accuracy can be obtained from QDateTime + by using QDateTime::fromMSecsSinceEpoch()/1000.0. The static methods \ref dateTimeToKey + and \ref keyToDateTime conveniently perform this conversion achieving a precision of one + millisecond on all Qt versions. + + The format of the date/time display in the tick labels is controlled with \ref setDateTimeFormat. + If a different time spec or time zone shall be used for the tick label appearance, see \ref + setDateTimeSpec or \ref setTimeZone, respectively. + + This ticker produces unequal tick spacing in order to provide intuitive date and time-of-day + ticks. For example, if the axis range spans a few years such that there is one tick per year, + ticks will be positioned on 1. January of every year. This is intuitive but, due to leap years, + will result in slightly unequal tick intervals (visually unnoticeable). The same can be seen in + the image above: even though the number of days varies month by month, this ticker generates + ticks on the same day of each month. + + If you would like to change the date/time that is used as a (mathematical) starting date for the + ticks, use the \ref setTickOrigin(const QDateTime &origin) method overload, which takes a + QDateTime. If you pass 15. July, 9:45 to this method, the yearly ticks will end up on 15. July at + 9:45 of every year. + + The ticker can be created and assigned to an axis like this: + \snippet documentation/doc-image-generator/mainwindow.cpp axistickerdatetime-creation + + \note If you rather wish to display relative times in terms of days, hours, minutes, seconds and + milliseconds, and are not interested in the intricacies of real calendar dates with months and + (leap) years, have a look at QCPAxisTickerTime instead. */ /*! Constructs the ticker and sets reasonable default values. Axis tickers are commonly created managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker. */ -QCPAxisTickerTime::QCPAxisTickerTime() : - mTimeFormat(QLatin1String("%h:%m:%s")), - mSmallestUnit(tuSeconds), - mBiggestUnit(tuHours) +QCPAxisTickerDateTime::QCPAxisTickerDateTime() : + mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")), + mDateTimeSpec(Qt::LocalTime), + mDateStrategy(dsNone) { setTickCount(4); - mFieldWidth[tuMilliseconds] = 3; - mFieldWidth[tuSeconds] = 2; - mFieldWidth[tuMinutes] = 2; - mFieldWidth[tuHours] = 2; - mFieldWidth[tuDays] = 1; +} + +/*! + Sets the format in which dates and times are displayed as tick labels. For details about the \a + format string, see the documentation of QDateTime::toString(). - mFormatPattern[tuMilliseconds] = QLatin1String("%z"); - mFormatPattern[tuSeconds] = QLatin1String("%s"); - mFormatPattern[tuMinutes] = QLatin1String("%m"); - mFormatPattern[tuHours] = QLatin1String("%h"); - mFormatPattern[tuDays] = QLatin1String("%d"); + Typical expressions are + + + + + + + + + + + + + + + + + + + + + + + + +
\c dThe day as a number without a leading zero (1 to 31)
\c ddThe day as a number with a leading zero (01 to 31)
\c dddThe abbreviated localized day name (e.g. 'Mon' to 'Sun'). Uses the system locale to localize the name, i.e. QLocale::system().
\c ddddThe long localized day name (e.g. 'Monday' to 'Sunday'). Uses the system locale to localize the name, i.e. QLocale::system().
\c MThe month as a number without a leading zero (1 to 12)
\c MMThe month as a number with a leading zero (01 to 12)
\c MMMThe abbreviated localized month name (e.g. 'Jan' to 'Dec'). Uses the system locale to localize the name, i.e. QLocale::system().
\c MMMMThe long localized month name (e.g. 'January' to 'December'). Uses the system locale to localize the name, i.e. QLocale::system().
\c yyThe year as a two digit number (00 to 99)
\c yyyyThe year as a four digit number. If the year is negative, a minus sign is prepended, making five characters.
\c hThe hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display)
\c hhThe hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display)
\c HThe hour without a leading zero (0 to 23, even with AM/PM display)
\c HHThe hour with a leading zero (00 to 23, even with AM/PM display)
\c mThe minute without a leading zero (0 to 59)
\c mmThe minute with a leading zero (00 to 59)
\c sThe whole second, without any leading zero (0 to 59)
\c ssThe whole second, with a leading zero where applicable (00 to 59)
\c zThe fractional part of the second, to go after a decimal point, without trailing zeroes (0 to 999). Thus "s.z" reports the seconds to full available (millisecond) precision without trailing zeroes.
\c zzzThe fractional part of the second, to millisecond precision, including trailing zeroes where applicable (000 to 999).
\c AP or \c AUse AM/PM display. A/AP will be replaced by an upper-case version of either QLocale::amText() or QLocale::pmText().
\c ap or \c aUse am/pm display. a/ap will be replaced by a lower-case version of either QLocale::amText() or QLocale::pmText().
\c tThe timezone (for example "CEST")
+ + Newlines can be inserted with \c "\n", literal strings (even when containing above expressions) + by encapsulating them using single-quotes. A literal single quote can be generated by using two + consecutive single quotes in the format. + + \see setDateTimeSpec, setTimeZone +*/ +void QCPAxisTickerDateTime::setDateTimeFormat(const QString &format) +{ + mDateTimeFormat = format; } /*! - Sets the format that will be used to display time in the tick labels. + Sets the time spec that is used for creating the tick labels from corresponding dates/times. + + The default value of QDateTime objects (and also QCPAxisTickerDateTime) is + Qt::LocalTime. However, if the displayed tick labels shall be given in UTC, set \a spec + to Qt::UTC. - The available patterns are: - - %%z for milliseconds - - %%s for seconds - - %%m for minutes - - %%h for hours - - %%d for days + Tick labels corresponding to other time zones can be achieved with \ref setTimeZone (which sets + \a spec to \c Qt::TimeZone internally). Note that if \a spec is afterwards set to not be \c + Qt::TimeZone again, the \ref setTimeZone setting will be ignored accordingly. - The field width (zero padding) can be controlled for each unit with \ref setFieldWidth. + \see setDateTimeFormat, setTimeZone +*/ +void QCPAxisTickerDateTime::setDateTimeSpec(Qt::TimeSpec spec) +{ + mDateTimeSpec = spec; +} + +# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) +/*! + Sets the time zone that is used for creating the tick labels from corresponding dates/times. The + time spec (\ref setDateTimeSpec) is set to \c Qt::TimeZone. - The largest unit that appears in \a format will carry all the remaining time of a certain tick - coordinate, even if it overflows the natural limit of the unit. For example, if %%m is the - largest unit it might become larger than 59 in order to consume larger time values. If on the - other hand %%h is available, the minutes will wrap around to zero after 59 and the time will - carry to the hour digit. + \see setDateTimeFormat, setTimeZone */ -void QCPAxisTickerTime::setTimeFormat(const QString &format) +void QCPAxisTickerDateTime::setTimeZone(const QTimeZone &zone) { - mTimeFormat = format; + mTimeZone = zone; + mDateTimeSpec = Qt::TimeZone; +} +#endif + +/*! + Sets the tick origin (see \ref QCPAxisTicker::setTickOrigin) in seconds since Epoch (1. Jan 1970, + 00:00 UTC). For the date time ticker it might be more intuitive to use the overload which + directly takes a QDateTime, see \ref setTickOrigin(const QDateTime &origin). - // determine smallest and biggest unit in format, to optimize unit replacement and allow biggest - // unit to consume remaining time of a tick value and grow beyond its modulo (e.g. min > 59) - mSmallestUnit = tuMilliseconds; - mBiggestUnit = tuMilliseconds; - bool hasSmallest = false; - for (int i = tuMilliseconds; i <= tuDays; ++i) - { - TimeUnit unit = static_cast(i); - if (mTimeFormat.contains(mFormatPattern.value(unit))) - { - if (!hasSmallest) - { - mSmallestUnit = unit; - hasSmallest = true; - } - mBiggestUnit = unit; - } - } + This is useful to define the month/day/time recurring at greater tick interval steps. For + example, If you pass 15. July, 9:45 to this method and the tick interval happens to be one tick + per year, the ticks will end up on 15. July at 9:45 of every year. +*/ +void QCPAxisTickerDateTime::setTickOrigin(double origin) +{ + QCPAxisTicker::setTickOrigin(origin); } /*! - Sets the field widh of the specified \a unit to be \a width digits, when displayed in the tick - label. If the number for the specific unit is shorter than \a width, it will be padded with an - according number of zeros to the left in order to reach the field width. + Sets the tick origin (see \ref QCPAxisTicker::setTickOrigin) as a QDateTime \a origin. - \see setTimeFormat + This is useful to define the month/day/time recurring at greater tick interval steps. For + example, If you pass 15. July, 9:45 to this method and the tick interval happens to be one tick + per year, the ticks will end up on 15. July at 9:45 of every year. */ -void QCPAxisTickerTime::setFieldWidth(QCPAxisTickerTime::TimeUnit unit, int width) +void QCPAxisTickerDateTime::setTickOrigin(const QDateTime &origin) { - mFieldWidth[unit] = qMax(width, 1); + setTickOrigin(dateTimeToKey(origin)); } /*! \internal - - Returns the tick step appropriate for time displays, depending on the provided \a range and the - smallest available time unit in the current format (\ref setTimeFormat). For example if the unit - of seconds isn't available in the format, this method will not generate steps (like 2.5 minutes) - that require sub-minute precision to be displayed correctly. + + Returns a sensible tick step with intervals appropriate for a date-time-display, such as weekly, + monthly, bi-monthly, etc. + + Note that this tick step isn't used exactly when generating the tick vector in \ref + createTickVector, but only as a guiding value requiring some correction for each individual tick + interval. Otherwise this would lead to unintuitive date displays, e.g. jumping between first day + in the month to the last day in the previous month from tick to tick, due to the non-uniform + length of months. The same problem arises with leap years. \seebaseclassmethod */ -double QCPAxisTickerTime::getTickStep(const QCPRange &range) +double QCPAxisTickerDateTime::getTickStep(const QCPRange &range) { - double result = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers + double result = range.size()/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers + mDateStrategy = dsNone; // leaving it at dsNone means tick coordinates will not be tuned in any special way in createTickVector if (result < 1) // ideal tick step is below 1 second -> use normal clean mantissa algorithm in units of seconds { - if (mSmallestUnit == tuMilliseconds) - result = qMax(cleanMantissa(result), 0.001); // smallest tick step is 1 millisecond - else // have no milliseconds available in format, so stick with 1 second tickstep - result = 1.0; - } else if (result < 3600*24) // below a day + result = cleanMantissa(result); + } else if (result < 86400*30.4375*12) // below a year { - // the filling of availableSteps seems a bit contorted but it fills in a sorted fashion and thus saves a post-fill sorting run - QVector availableSteps; - // seconds range: - if (mSmallestUnit <= tuSeconds) - availableSteps << 1; - if (mSmallestUnit == tuMilliseconds) - availableSteps << 2.5; // only allow half second steps if milliseconds are there to display it - else if (mSmallestUnit == tuSeconds) - availableSteps << 2; - if (mSmallestUnit <= tuSeconds) - availableSteps << 5 << 10 << 15 << 30; - // minutes range: - if (mSmallestUnit <= tuMinutes) - availableSteps << 1*60; - if (mSmallestUnit <= tuSeconds) - availableSteps << 2.5*60; // only allow half minute steps if seconds are there to display it - else if (mSmallestUnit == tuMinutes) - availableSteps << 2*60; - if (mSmallestUnit <= tuMinutes) - availableSteps << 5*60 << 10*60 << 15*60 << 30*60; - // hours range: - if (mSmallestUnit <= tuHours) - availableSteps << 1*3600 << 2*3600 << 3*3600 << 6*3600 << 12*3600 << 24*3600; - // pick available step that is most appropriate to approximate ideal step: - result = pickClosest(result, availableSteps); - } else // more than a day, go back to normal clean mantissa algorithm but in units of days + result = pickClosest(result, QVector() + << 1 << 2.5 << 5 << 10 << 15 << 30 << 60 << 2.5*60 << 5*60 << 10*60 << 15*60 << 30*60 << 60*60 // second, minute, hour range + << 3600*2 << 3600*3 << 3600*6 << 3600*12 << 3600*24 // hour to day range + << 86400*2 << 86400*5 << 86400*7 << 86400*14 << 86400*30.4375 << 86400*30.4375*2 << 86400*30.4375*3 << 86400*30.4375*6 << 86400*30.4375*12); // day, week, month range (avg. days per month includes leap years) + if (result > 86400*30.4375-1) // month tick intervals or larger + mDateStrategy = dsUniformDayInMonth; + else if (result > 3600*24-1) // day tick intervals or larger + mDateStrategy = dsUniformTimeInDay; + } else // more than a year, go back to normal clean mantissa algorithm but in units of years { - const double secondsPerDay = 3600*24; - result = cleanMantissa(result/secondsPerDay)*secondsPerDay; + const double secondsPerYear = 86400*30.4375*12; // average including leap years + result = cleanMantissa(result/secondsPerYear)*secondsPerYear; + mDateStrategy = dsUniformDayInMonth; } return result; } /*! \internal - - Returns the sub tick count appropriate for the provided \a tickStep and time displays. + + Returns a sensible sub tick count with intervals appropriate for a date-time-display, such as weekly, + monthly, bi-monthly, etc. \seebaseclassmethod */ -int QCPAxisTickerTime::getSubTickCount(double tickStep) +int QCPAxisTickerDateTime::getSubTickCount(double tickStep) { int result = QCPAxisTicker::getSubTickCount(tickStep); - switch (qRound(tickStep)) // hand chosen subticks for specific minute/hour/day range (as specified in getTickStep) + switch (qRound(tickStep)) // hand chosen subticks for specific minute/hour/day/week/month range (as specified in getTickStep) { case 5*60: result = 4; break; case 10*60: result = 1; break; @@ -6341,109 +6690,443 @@ int QCPAxisTickerTime::getSubTickCount(double tickStep) case 3600*6: result = 1; break; case 3600*12: result = 3; break; case 3600*24: result = 3; break; + case 86400*2: result = 1; break; + case 86400*5: result = 4; break; + case 86400*7: result = 6; break; + case 86400*14: result = 1; break; + case int(86400*30.4375+0.5): result = 3; break; + case int(86400*30.4375*2+0.5): result = 1; break; + case int(86400*30.4375*3+0.5): result = 2; break; + case int(86400*30.4375*6+0.5): result = 5; break; + case int(86400*30.4375*12+0.5): result = 3; break; } return result; } /*! \internal - Returns the tick label corresponding to the provided \a tick and the configured format and field - widths (\ref setTimeFormat, \ref setFieldWidth). + Generates a date/time tick label for tick coordinate \a tick, based on the currently set format + (\ref setDateTimeFormat), time spec (\ref setDateTimeSpec), and possibly time zone (\ref + setTimeZone). \seebaseclassmethod */ -QString QCPAxisTickerTime::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) +QString QCPAxisTickerDateTime::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) { Q_UNUSED(precision) Q_UNUSED(formatChar) - Q_UNUSED(locale) - bool negative = tick < 0; - if (negative) tick *= -1; - double values[tuDays+1]; // contains the msec/sec/min/... value with its respective modulo (e.g. minute 0..59) - double restValues[tuDays+1]; // contains the msec/sec/min/... value as if it's the largest available unit and thus consumes the remaining time +# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) + if (mDateTimeSpec == Qt::TimeZone) + return locale.toString(keyToDateTime(tick).toTimeZone(mTimeZone), mDateTimeFormat); + else + return locale.toString(keyToDateTime(tick).toTimeSpec(mDateTimeSpec), mDateTimeFormat); +# else + return locale.toString(keyToDateTime(tick).toTimeSpec(mDateTimeSpec), mDateTimeFormat); +# endif +} + +/*! \internal - restValues[tuMilliseconds] = tick*1000; - values[tuMilliseconds] = modf(restValues[tuMilliseconds]/1000, &restValues[tuSeconds])*1000; - values[tuSeconds] = modf(restValues[tuSeconds]/60, &restValues[tuMinutes])*60; - values[tuMinutes] = modf(restValues[tuMinutes]/60, &restValues[tuHours])*60; - values[tuHours] = modf(restValues[tuHours]/24, &restValues[tuDays])*24; - // no need to set values[tuDays] because days are always a rest value (there is no higher unit so it consumes all remaining time) + Uses the passed \a tickStep as a guiding value and applies corrections in order to obtain + non-uniform tick intervals but intuitive tick labels, e.g. falling on the same day of each month. - QString result = mTimeFormat; - for (int i = mSmallestUnit; i <= mBiggestUnit; ++i) + \seebaseclassmethod +*/ +QVector QCPAxisTickerDateTime::createTickVector(double tickStep, const QCPRange &range) +{ + QVector result = QCPAxisTicker::createTickVector(tickStep, range); + if (!result.isEmpty()) { - TimeUnit iUnit = static_cast(i); - replaceUnit(result, iUnit, qRound(iUnit == mBiggestUnit ? restValues[iUnit] : values[iUnit])); + if (mDateStrategy == dsUniformTimeInDay) + { + QDateTime uniformDateTime = keyToDateTime(mTickOrigin); // the time of this datetime will be set for all other ticks, if possible + QDateTime tickDateTime; + for (int i=0; i 15) // with leap years involved, date month may jump backwards or forwards, and needs to be corrected before setting day + tickDateTime = tickDateTime.addMonths(-1); + tickDateTime.setDate(QDate(tickDateTime.date().year(), tickDateTime.date().month(), thisUniformDay)); + result[i] = dateTimeToKey(tickDateTime); + } + } } - if (negative) - result.prepend(QLatin1Char('-')); return result; } -/*! \internal +/*! + A convenience method which turns \a key (in seconds since Epoch 1. Jan 1970, 00:00 UTC) into a + QDateTime object. This can be used to turn axis coordinates to actual QDateTimes. - Replaces all occurrences of the format pattern belonging to \a unit in \a text with the specified - \a value, using the field width as specified with \ref setFieldWidth for the \a unit. + The accuracy achieved by this method is one millisecond, irrespective of the used Qt version (it + works around the lack of a QDateTime::fromMSecsSinceEpoch in Qt 4.6) + + \see dateTimeToKey */ -void QCPAxisTickerTime::replaceUnit(QString &text, QCPAxisTickerTime::TimeUnit unit, int value) const +QDateTime QCPAxisTickerDateTime::keyToDateTime(double key) { - QString valueStr = QString::number(value); - while (valueStr.size() < mFieldWidth.value(unit)) - valueStr.prepend(QLatin1Char('0')); +# if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) + return QDateTime::fromTime_t(key).addMSecs((key-(qint64)key)*1000); +# else + return QDateTime::fromMSecsSinceEpoch(qint64(key*1000.0)); +# endif +} + +/*! \overload - text.replace(mFormatPattern.value(unit), valueStr); + A convenience method which turns a QDateTime object into a double value that corresponds to + seconds since Epoch (1. Jan 1970, 00:00 UTC). This is the format used as axis coordinates by + QCPAxisTickerDateTime. + + The accuracy achieved by this method is one millisecond, irrespective of the used Qt version (it + works around the lack of a QDateTime::toMSecsSinceEpoch in Qt 4.6) + + \see keyToDateTime +*/ +double QCPAxisTickerDateTime::dateTimeToKey(const QDateTime &dateTime) +{ +# if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) + return dateTime.toTime_t()+dateTime.time().msec()/1000.0; +# else + return dateTime.toMSecsSinceEpoch()/1000.0; +# endif } -/* end of 'src/axis/axistickertime.cpp' */ + +/*! \overload + + A convenience method which turns a QDate object into a double value that corresponds to seconds + since Epoch (1. Jan 1970, 00:00 UTC). This is the format used + as axis coordinates by QCPAxisTickerDateTime. + + The returned value will be the start of the passed day of \a date, interpreted in the given \a + timeSpec. + + \see keyToDateTime +*/ +double QCPAxisTickerDateTime::dateTimeToKey(const QDate &date, Qt::TimeSpec timeSpec) +{ +# if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) + return QDateTime(date, QTime(0, 0), timeSpec).toTime_t(); +# elif QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + return QDateTime(date, QTime(0, 0), timeSpec).toMSecsSinceEpoch()/1000.0; +# else + return date.startOfDay(timeSpec).toMSecsSinceEpoch()/1000.0; +# endif +} +/* end of 'src/axis/axistickerdatetime.cpp' */ -/* including file 'src/axis/axistickerfixed.cpp', size 5583 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/axis/axistickertime.cpp' */ +/* modified 2021-03-29T02:30:44, size 11745 */ //////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////// QCPAxisTickerFixed +//////////////////// QCPAxisTickerTime //////////////////////////////////////////////////////////////////////////////////////////////////// -/*! \class QCPAxisTickerFixed - \brief Specialized axis ticker with a fixed tick step +/*! \class QCPAxisTickerTime + \brief Specialized axis ticker for time spans in units of milliseconds to days - \image html axisticker-fixed.png + \image html axisticker-time.png - This QCPAxisTicker subclass generates ticks with a fixed tick step set with \ref setTickStep. It - is also possible to allow integer multiples and integer powers of the specified tick step with - \ref setScaleStrategy. + This QCPAxisTicker subclass generates ticks that corresponds to time intervals. - A typical application of this ticker is to make an axis only display integers, by setting the - tick step of the ticker to 1.0 and the scale strategy to \ref ssMultiples. + The format of the time display in the tick labels is controlled with \ref setTimeFormat and \ref + setFieldWidth. The time coordinate is in the unit of seconds with respect to the time coordinate + zero. Unlike with QCPAxisTickerDateTime, the ticks don't correspond to a specific calendar date + and time. - Another case is when a certain number has a special meaning and axis ticks should only appear at - multiples of that value. In this case you might also want to consider \ref QCPAxisTickerPi - because despite the name it is not limited to only pi symbols/values. + The time can be displayed in milliseconds, seconds, minutes, hours and days. Depending on the + largest available unit in the format specified with \ref setTimeFormat, any time spans above will + be carried in that largest unit. So for example if the format string is "%m:%s" and a tick at + coordinate value 7815 (being 2 hours, 10 minutes and 15 seconds) is created, the resulting tick + label will show "130:15" (130 minutes, 15 seconds). If the format string is "%h:%m:%s", the hour + unit will be used and the label will thus be "02:10:15". Negative times with respect to the axis + zero will carry a leading minus sign. The ticker can be created and assigned to an axis like this: - \snippet documentation/doc-image-generator/mainwindow.cpp axistickerfixed-creation + \snippet documentation/doc-image-generator/mainwindow.cpp axistickertime-creation + + Here is an example of a time axis providing time information in days, hours and minutes. Due to + the axis range spanning a few days and the wanted tick count (\ref setTickCount), the ticker + decided to use tick steps of 12 hours: + + \image html axisticker-time2.png + + The format string for this example is + \snippet documentation/doc-image-generator/mainwindow.cpp axistickertime-creation-2 + + \note If you rather wish to display calendar dates and times, have a look at QCPAxisTickerDateTime + instead. */ /*! Constructs the ticker and sets reasonable default values. Axis tickers are commonly created managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker. */ -QCPAxisTickerFixed::QCPAxisTickerFixed() : - mTickStep(1.0), - mScaleStrategy(ssNone) +QCPAxisTickerTime::QCPAxisTickerTime() : + mTimeFormat(QLatin1String("%h:%m:%s")), + mSmallestUnit(tuSeconds), + mBiggestUnit(tuHours) { + setTickCount(4); + mFieldWidth[tuMilliseconds] = 3; + mFieldWidth[tuSeconds] = 2; + mFieldWidth[tuMinutes] = 2; + mFieldWidth[tuHours] = 2; + mFieldWidth[tuDays] = 1; + + mFormatPattern[tuMilliseconds] = QLatin1String("%z"); + mFormatPattern[tuSeconds] = QLatin1String("%s"); + mFormatPattern[tuMinutes] = QLatin1String("%m"); + mFormatPattern[tuHours] = QLatin1String("%h"); + mFormatPattern[tuDays] = QLatin1String("%d"); } /*! - Sets the fixed tick interval to \a step. + Sets the format that will be used to display time in the tick labels. - The axis ticker will only use this tick step when generating axis ticks. This might cause a very - high tick density and overlapping labels if the axis range is zoomed out. Using \ref - setScaleStrategy it is possible to relax the fixed step and also allow multiples or powers of \a - step. This will enable the ticker to reduce the number of ticks to a reasonable amount (see \ref - setTickCount). + The available patterns are: + - %%z for milliseconds + - %%s for seconds + - %%m for minutes + - %%h for hours + - %%d for days + + The field width (zero padding) can be controlled for each unit with \ref setFieldWidth. + + The largest unit that appears in \a format will carry all the remaining time of a certain tick + coordinate, even if it overflows the natural limit of the unit. For example, if %%m is the + largest unit it might become larger than 59 in order to consume larger time values. If on the + other hand %%h is available, the minutes will wrap around to zero after 59 and the time will + carry to the hour digit. */ -void QCPAxisTickerFixed::setTickStep(double step) +void QCPAxisTickerTime::setTimeFormat(const QString &format) { - if (step > 0) + mTimeFormat = format; + + // determine smallest and biggest unit in format, to optimize unit replacement and allow biggest + // unit to consume remaining time of a tick value and grow beyond its modulo (e.g. min > 59) + mSmallestUnit = tuMilliseconds; + mBiggestUnit = tuMilliseconds; + bool hasSmallest = false; + for (int i = tuMilliseconds; i <= tuDays; ++i) + { + TimeUnit unit = static_cast(i); + if (mTimeFormat.contains(mFormatPattern.value(unit))) + { + if (!hasSmallest) + { + mSmallestUnit = unit; + hasSmallest = true; + } + mBiggestUnit = unit; + } + } +} + +/*! + Sets the field widh of the specified \a unit to be \a width digits, when displayed in the tick + label. If the number for the specific unit is shorter than \a width, it will be padded with an + according number of zeros to the left in order to reach the field width. + + \see setTimeFormat +*/ +void QCPAxisTickerTime::setFieldWidth(QCPAxisTickerTime::TimeUnit unit, int width) +{ + mFieldWidth[unit] = qMax(width, 1); +} + +/*! \internal + + Returns the tick step appropriate for time displays, depending on the provided \a range and the + smallest available time unit in the current format (\ref setTimeFormat). For example if the unit + of seconds isn't available in the format, this method will not generate steps (like 2.5 minutes) + that require sub-minute precision to be displayed correctly. + + \seebaseclassmethod +*/ +double QCPAxisTickerTime::getTickStep(const QCPRange &range) +{ + double result = range.size()/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers + + if (result < 1) // ideal tick step is below 1 second -> use normal clean mantissa algorithm in units of seconds + { + if (mSmallestUnit == tuMilliseconds) + result = qMax(cleanMantissa(result), 0.001); // smallest tick step is 1 millisecond + else // have no milliseconds available in format, so stick with 1 second tickstep + result = 1.0; + } else if (result < 3600*24) // below a day + { + // the filling of availableSteps seems a bit contorted but it fills in a sorted fashion and thus saves a post-fill sorting run + QVector availableSteps; + // seconds range: + if (mSmallestUnit <= tuSeconds) + availableSteps << 1; + if (mSmallestUnit == tuMilliseconds) + availableSteps << 2.5; // only allow half second steps if milliseconds are there to display it + else if (mSmallestUnit == tuSeconds) + availableSteps << 2; + if (mSmallestUnit <= tuSeconds) + availableSteps << 5 << 10 << 15 << 30; + // minutes range: + if (mSmallestUnit <= tuMinutes) + availableSteps << 1*60; + if (mSmallestUnit <= tuSeconds) + availableSteps << 2.5*60; // only allow half minute steps if seconds are there to display it + else if (mSmallestUnit == tuMinutes) + availableSteps << 2*60; + if (mSmallestUnit <= tuMinutes) + availableSteps << 5*60 << 10*60 << 15*60 << 30*60; + // hours range: + if (mSmallestUnit <= tuHours) + availableSteps << 1*3600 << 2*3600 << 3*3600 << 6*3600 << 12*3600 << 24*3600; + // pick available step that is most appropriate to approximate ideal step: + result = pickClosest(result, availableSteps); + } else // more than a day, go back to normal clean mantissa algorithm but in units of days + { + const double secondsPerDay = 3600*24; + result = cleanMantissa(result/secondsPerDay)*secondsPerDay; + } + return result; +} + +/*! \internal + + Returns the sub tick count appropriate for the provided \a tickStep and time displays. + + \seebaseclassmethod +*/ +int QCPAxisTickerTime::getSubTickCount(double tickStep) +{ + int result = QCPAxisTicker::getSubTickCount(tickStep); + switch (qRound(tickStep)) // hand chosen subticks for specific minute/hour/day range (as specified in getTickStep) + { + case 5*60: result = 4; break; + case 10*60: result = 1; break; + case 15*60: result = 2; break; + case 30*60: result = 1; break; + case 60*60: result = 3; break; + case 3600*2: result = 3; break; + case 3600*3: result = 2; break; + case 3600*6: result = 1; break; + case 3600*12: result = 3; break; + case 3600*24: result = 3; break; + } + return result; +} + +/*! \internal + + Returns the tick label corresponding to the provided \a tick and the configured format and field + widths (\ref setTimeFormat, \ref setFieldWidth). + + \seebaseclassmethod +*/ +QString QCPAxisTickerTime::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) +{ + Q_UNUSED(precision) + Q_UNUSED(formatChar) + Q_UNUSED(locale) + bool negative = tick < 0; + if (negative) tick *= -1; + double values[tuDays+1]; // contains the msec/sec/min/... value with its respective modulo (e.g. minute 0..59) + double restValues[tuDays+1]; // contains the msec/sec/min/... value as if it's the largest available unit and thus consumes the remaining time + + restValues[tuMilliseconds] = tick*1000; + values[tuMilliseconds] = modf(restValues[tuMilliseconds]/1000, &restValues[tuSeconds])*1000; + values[tuSeconds] = modf(restValues[tuSeconds]/60, &restValues[tuMinutes])*60; + values[tuMinutes] = modf(restValues[tuMinutes]/60, &restValues[tuHours])*60; + values[tuHours] = modf(restValues[tuHours]/24, &restValues[tuDays])*24; + // no need to set values[tuDays] because days are always a rest value (there is no higher unit so it consumes all remaining time) + + QString result = mTimeFormat; + for (int i = mSmallestUnit; i <= mBiggestUnit; ++i) + { + TimeUnit iUnit = static_cast(i); + replaceUnit(result, iUnit, qRound(iUnit == mBiggestUnit ? restValues[iUnit] : values[iUnit])); + } + if (negative) + result.prepend(QLatin1Char('-')); + return result; +} + +/*! \internal + + Replaces all occurrences of the format pattern belonging to \a unit in \a text with the specified + \a value, using the field width as specified with \ref setFieldWidth for the \a unit. +*/ +void QCPAxisTickerTime::replaceUnit(QString &text, QCPAxisTickerTime::TimeUnit unit, int value) const +{ + QString valueStr = QString::number(value); + while (valueStr.size() < mFieldWidth.value(unit)) + valueStr.prepend(QLatin1Char('0')); + + text.replace(mFormatPattern.value(unit), valueStr); +} +/* end of 'src/axis/axistickertime.cpp' */ + + +/* including file 'src/axis/axistickerfixed.cpp' */ +/* modified 2021-03-29T02:30:44, size 5575 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAxisTickerFixed +//////////////////////////////////////////////////////////////////////////////////////////////////// +/*! \class QCPAxisTickerFixed + \brief Specialized axis ticker with a fixed tick step + + \image html axisticker-fixed.png + + This QCPAxisTicker subclass generates ticks with a fixed tick step set with \ref setTickStep. It + is also possible to allow integer multiples and integer powers of the specified tick step with + \ref setScaleStrategy. + + A typical application of this ticker is to make an axis only display integers, by setting the + tick step of the ticker to 1.0 and the scale strategy to \ref ssMultiples. + + Another case is when a certain number has a special meaning and axis ticks should only appear at + multiples of that value. In this case you might also want to consider \ref QCPAxisTickerPi + because despite the name it is not limited to only pi symbols/values. + + The ticker can be created and assigned to an axis like this: + \snippet documentation/doc-image-generator/mainwindow.cpp axistickerfixed-creation +*/ + +/*! + Constructs the ticker and sets reasonable default values. Axis tickers are commonly created + managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker. +*/ +QCPAxisTickerFixed::QCPAxisTickerFixed() : + mTickStep(1.0), + mScaleStrategy(ssNone) +{ +} + +/*! + Sets the fixed tick interval to \a step. + + The axis ticker will only use this tick step when generating axis ticks. This might cause a very + high tick density and overlapping labels if the axis range is zoomed out. Using \ref + setScaleStrategy it is possible to relax the fixed step and also allow multiples or powers of \a + step. This will enable the ticker to reduce the number of ticks to a reasonable amount (see \ref + setTickCount). +*/ +void QCPAxisTickerFixed::setTickStep(double step) +{ + if (step > 0) mTickStep = step; else qDebug() << Q_FUNC_INFO << "tick step must be greater than zero:" << step; @@ -6481,16 +7164,16 @@ double QCPAxisTickerFixed::getTickStep(const QCPRange &range) } case ssMultiples: { - double exactStep = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers + double exactStep = range.size()/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers if (exactStep < mTickStep) return mTickStep; else - return (qint64)(cleanMantissa(exactStep/mTickStep)+0.5)*mTickStep; + return qint64(cleanMantissa(exactStep/mTickStep)+0.5)*mTickStep; } case ssPowers: { - double exactStep = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers - return qPow(mTickStep, (int)(qLn(exactStep)/qLn(mTickStep)+0.5)); + double exactStep = range.size()/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers + return qPow(mTickStep, int(qLn(exactStep)/qLn(mTickStep)+0.5)); } } return mTickStep; @@ -6498,8 +7181,8 @@ double QCPAxisTickerFixed::getTickStep(const QCPRange &range) /* end of 'src/axis/axistickerfixed.cpp' */ -/* including file 'src/axis/axistickertext.cpp', size 8661 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/axis/axistickertext.cpp' */ +/* modified 2021-03-29T02:30:44, size 8742 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTickerText @@ -6624,7 +7307,11 @@ void QCPAxisTickerText::addTick(double position, const QString &label) */ void QCPAxisTickerText::addTicks(const QMap &ticks) { +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) mTicks.unite(ticks); +#else + mTicks.insert(ticks); +#endif } /*! \overload @@ -6711,8 +7398,8 @@ QVector QCPAxisTickerText::createTickVector(double tickStep, const QCPRa /* end of 'src/axis/axistickertext.cpp' */ -/* including file 'src/axis/axistickerpi.cpp', size 11170 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/axis/axistickerpi.cpp' */ +/* modified 2021-03-29T02:30:44, size 11177 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTickerPi @@ -6802,7 +7489,7 @@ void QCPAxisTickerPi::setFractionStyle(QCPAxisTickerPi::FractionStyle style) */ double QCPAxisTickerPi::getTickStep(const QCPRange &range) { - mPiTickStep = range.size()/mPiValue/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers + mPiTickStep = range.size()/mPiValue/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers mPiTickStep = cleanMantissa(mPiTickStep); return mPiTickStep*mPiValue; } @@ -6900,7 +7587,7 @@ QString QCPAxisTickerPi::fractionToString(int numerator, int denominator) const if (mFractionStyle == fsFloatingPoint) // should never be the case when calling this function { qDebug() << Q_FUNC_INFO << "shouldn't be called with fraction style fsDecimal"; - return QString::number(numerator/(double)denominator); // failsafe + return QString::number(numerator/double(denominator)); // failsafe } int sign = numerator*denominator < 0 ? -1 : 1; numerator = qAbs(numerator); @@ -6922,7 +7609,7 @@ QString QCPAxisTickerPi::fractionToString(int numerator, int denominator) const { return QString(QLatin1String("%1%2%3/%4")) .arg(sign == -1 ? QLatin1String("-") : QLatin1String("")) - .arg(integerPart > 0 ? QString::number(integerPart)+QLatin1String(" ") : QLatin1String("")) + .arg(integerPart > 0 ? QString::number(integerPart)+QLatin1String(" ") : QString(QLatin1String(""))) .arg(remainder) .arg(denominator); } else if (mFractionStyle == fsUnicodeFractions) @@ -6998,8 +7685,8 @@ QString QCPAxisTickerPi::unicodeSubscript(int number) const /* end of 'src/axis/axistickerpi.cpp' */ -/* including file 'src/axis/axistickerlog.cpp', size 7106 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/axis/axistickerlog.cpp' */ +/* modified 2021-03-29T02:30:44, size 7890 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTickerLog @@ -7022,6 +7709,12 @@ QString QCPAxisTickerPi::unicodeSubscript(int number) const The ticker can be created and assigned to an axis like this: \snippet documentation/doc-image-generator/mainwindow.cpp axistickerlog-creation + + Note that the nature of logarithmic ticks imply that there exists a smallest possible tick step, + corresponding to one multiplication by the log base. If the user zooms in further than that, no + new ticks would appear, leading to very sparse or even no axis ticks on the axis. To prevent this + situation, this ticker falls back to regular tick generation if the axis range would be covered + by too few logarithmically placed ticks. */ /*! @@ -7067,20 +7760,6 @@ void QCPAxisTickerLog::setSubTickCount(int subTicks) qDebug() << Q_FUNC_INFO << "sub tick count can't be negative:" << subTicks; } -/*! \internal - - Since logarithmic tick steps are necessarily different for each tick interval, this method does - nothing in the case of QCPAxisTickerLog - - \seebaseclassmethod -*/ -double QCPAxisTickerLog::getTickStep(const QCPRange &range) -{ - // Logarithmic axis ticker has unequal tick spacing, so doesn't need this method - Q_UNUSED(range) - return 1.0; -} - /*! \internal Returns the sub tick count specified in \ref setSubTickCount. For QCPAxisTickerLog, there is no @@ -7098,19 +7777,24 @@ int QCPAxisTickerLog::getSubTickCount(double tickStep) Creates ticks with a spacing given by the logarithm base and an increasing integer power in the provided \a range. The step in which the power increases tick by tick is chosen in order to keep - the total number of ticks as close as possible to the tick count (\ref setTickCount). The - parameter \a tickStep is ignored for QCPAxisTickerLog + the total number of ticks as close as possible to the tick count (\ref setTickCount). + + The parameter \a tickStep is ignored for the normal logarithmic ticker generation. Only when + zoomed in very far such that not enough logarithmically placed ticks would be visible, this + function falls back to the regular QCPAxisTicker::createTickVector, which then uses \a tickStep. \seebaseclassmethod */ QVector QCPAxisTickerLog::createTickVector(double tickStep, const QCPRange &range) { - Q_UNUSED(tickStep) QVector result; if (range.lower > 0 && range.upper > 0) // positive range { - double exactPowerStep = qLn(range.upper/range.lower)*mLogBaseLnInv/(double)(mTickCount+1e-10); - double newLogBase = qPow(mLogBase, qMax((int)cleanMantissa(exactPowerStep), 1)); + const double baseTickCount = qLn(range.upper/range.lower)*mLogBaseLnInv; + if (baseTickCount < 1.6) // if too few log ticks would be visible in axis range, fall back to regular tick vector generation + return QCPAxisTicker::createTickVector(tickStep, range); + const double exactPowerStep = baseTickCount/double(mTickCount+1e-10); + const double newLogBase = qPow(mLogBase, qMax(int(cleanMantissa(exactPowerStep)), 1)); double currentTick = qPow(newLogBase, qFloor(qLn(range.lower)/qLn(newLogBase))); result.append(currentTick); while (currentTick < range.upper && currentTick > 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case @@ -7120,8 +7804,11 @@ QVector QCPAxisTickerLog::createTickVector(double tickStep, const QCPRan } } else if (range.lower < 0 && range.upper < 0) // negative range { - double exactPowerStep = qLn(range.lower/range.upper)*mLogBaseLnInv/(double)(mTickCount+1e-10); - double newLogBase = qPow(mLogBase, qMax((int)cleanMantissa(exactPowerStep), 1)); + const double baseTickCount = qLn(range.lower/range.upper)*mLogBaseLnInv; + if (baseTickCount < 1.6) // if too few log ticks would be visible in axis range, fall back to regular tick vector generation + return QCPAxisTicker::createTickVector(tickStep, range); + const double exactPowerStep = baseTickCount/double(mTickCount+1e-10); + const double newLogBase = qPow(mLogBase, qMax(int(cleanMantissa(exactPowerStep)), 1)); double currentTick = -qPow(newLogBase, qCeil(qLn(-range.lower)/qLn(newLogBase))); result.append(currentTick); while (currentTick < range.upper && currentTick < 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case @@ -7139,8 +7826,8 @@ QVector QCPAxisTickerLog::createTickVector(double tickStep, const QCPRan /* end of 'src/axis/axistickerlog.cpp' */ -/* including file 'src/axis/axis.cpp', size 99515 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/axis/axis.cpp' */ +/* modified 2021-03-29T02:30:44, size 99883 */ //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -7167,6 +7854,9 @@ QVector QCPAxisTickerLog::createTickVector(double tickStep, const QCPRan */ QCPGrid::QCPGrid(QCPAxis *parentAxis) : QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis), + mSubGridVisible{}, + mAntialiasedSubGrid{}, + mAntialiasedZeroLine{}, mParentAxis(parentAxis) { // warning: this is called in QCPAxis constructor, so parentAxis members should not be accessed/called @@ -7353,16 +8043,16 @@ void QCPGrid::drawSubGridLines(QCPPainter *painter) const painter->setPen(mSubGridPen); if (mParentAxis->orientation() == Qt::Horizontal) { - for (int i=0; imSubTickVector.size(); ++i) + foreach (double tickCoord, mParentAxis->mSubTickVector) { - t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i)); // x + t = mParentAxis->coordToPixel(tickCoord); // x painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top())); } } else { - for (int i=0; imSubTickVector.size(); ++i) + foreach (double tickCoord, mParentAxis->mSubTickVector) { - t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i)); // y + t = mParentAxis->coordToPixel(tickCoord); // y painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t)); } } @@ -7545,7 +8235,8 @@ QCPAxis::QCPAxis(QCPAxisRect *parent, AxisType type) : mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())), mTicker(new QCPAxisTicker), mCachedMarginValid(false), - mCachedMargin(0) + mCachedMargin(0), + mDragging(false) { setParent(parent); mGrid->setVisible(false); @@ -7878,7 +8569,7 @@ void QCPAxis::setTicker(QSharedPointer ticker) if (ticker) mTicker = ticker; else - qDebug() << Q_FUNC_INFO << "can not set 0 as axis ticker"; + qDebug() << Q_FUNC_INFO << "can not set nullptr as axis ticker"; // no need to invalidate margin cache here because produced tick labels are checked for changes in setupTickVector } @@ -7986,11 +8677,16 @@ void QCPAxis::setTickLabelSide(LabelSide side) of the format code used e.g. by QString::number() and QLocale::toString(). For reference about that, see the "Argument Formats" section in the detailed description of the QString class. - \a formatCode is a string of one, two or three characters. The first character is identical to + \a formatCode is a string of one, two or three characters. + + The first character is identical to the normal format code used by Qt. In short, this means: 'e'/'E' scientific format, 'f' fixed - format, 'g'/'G' scientific or fixed, whichever is shorter. - - The second and third characters are optional and specific to QCustomPlot:\n + format, 'g'/'G' scientific or fixed, whichever is shorter. For the 'e', 'E', and 'f' formats, + the precision set by \ref setNumberPrecision represents the number of digits after the decimal + point. For the 'g' and 'G' formats, the precision represents the maximum number of significant + digits, trailing zeroes are omitted. + + The second and third characters are optional and specific to QCustomPlot:\n If the first char was 'e' or 'g', numbers are/might be displayed in the scientific format, e.g. "5.5e9", which is ugly in a plot. So when the second char of \a formatCode is set to 'b' (for "beautiful"), those exponential numbers are formatted in a more natural way, i.e. "5.5 @@ -8504,7 +9200,7 @@ void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio) else ownPixelSize = axisRect()->height(); - double newRangeSize = ratio*otherAxis->range().size()*ownPixelSize/(double)otherPixelSize; + double newRangeSize = ratio*otherAxis->range().size()*ownPixelSize/double(otherPixelSize); setRange(range().center(), newRangeSize, Qt::AlignCenter); } @@ -8516,22 +9212,21 @@ void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio) */ void QCPAxis::rescale(bool onlyVisiblePlottables) { - QList p = plottables(); QCPRange newRange; bool haveRange = false; - for (int i=0; irealVisibility() && onlyVisiblePlottables) + if (!plottable->realVisibility() && onlyVisiblePlottables) continue; QCPRange plottableRange; bool currentFoundRange; QCP::SignDomain signDomain = QCP::sdBoth; if (mScaleType == stLogarithmic) signDomain = (mRange.upper < 0 ? QCP::sdNegative : QCP::sdPositive); - if (p.at(i)->keyAxis() == this) - plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain); + if (plottable->keyAxis() == this) + plottableRange = plottable->getKeyRange(currentFoundRange, signDomain); else - plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain); + plottableRange = plottable->getValueRange(currentFoundRange, signDomain); if (currentFoundRange) { if (!haveRange) @@ -8570,30 +9265,30 @@ double QCPAxis::pixelToCoord(double value) const if (mScaleType == stLinear) { if (!mRangeReversed) - return (value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.lower; + return (value-mAxisRect->left())/double(mAxisRect->width())*mRange.size()+mRange.lower; else - return -(value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.upper; + return -(value-mAxisRect->left())/double(mAxisRect->width())*mRange.size()+mRange.upper; } else // mScaleType == stLogarithmic { if (!mRangeReversed) - return qPow(mRange.upper/mRange.lower, (value-mAxisRect->left())/(double)mAxisRect->width())*mRange.lower; + return qPow(mRange.upper/mRange.lower, (value-mAxisRect->left())/double(mAxisRect->width()))*mRange.lower; else - return qPow(mRange.upper/mRange.lower, (mAxisRect->left()-value)/(double)mAxisRect->width())*mRange.upper; + return qPow(mRange.upper/mRange.lower, (mAxisRect->left()-value)/double(mAxisRect->width()))*mRange.upper; } } else // orientation() == Qt::Vertical { if (mScaleType == stLinear) { if (!mRangeReversed) - return (mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.lower; + return (mAxisRect->bottom()-value)/double(mAxisRect->height())*mRange.size()+mRange.lower; else - return -(mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.upper; + return -(mAxisRect->bottom()-value)/double(mAxisRect->height())*mRange.size()+mRange.upper; } else // mScaleType == stLogarithmic { if (!mRangeReversed) - return qPow(mRange.upper/mRange.lower, (mAxisRect->bottom()-value)/(double)mAxisRect->height())*mRange.lower; + return qPow(mRange.upper/mRange.lower, (mAxisRect->bottom()-value)/double(mAxisRect->height()))*mRange.lower; else - return qPow(mRange.upper/mRange.lower, (value-mAxisRect->bottom())/(double)mAxisRect->height())*mRange.upper; + return qPow(mRange.upper/mRange.lower, (value-mAxisRect->bottom())/double(mAxisRect->height()))*mRange.upper; } } } @@ -8699,10 +9394,10 @@ QList QCPAxis::plottables() const QList result; if (!mParentPlot) return result; - for (int i=0; imPlottables.size(); ++i) + foreach (QCPAbstractPlottable *plottable, mParentPlot->mPlottables) { - if (mParentPlot->mPlottables.at(i)->keyAxis() == this ||mParentPlot->mPlottables.at(i)->valueAxis() == this) - result.append(mParentPlot->mPlottables.at(i)); + if (plottable->keyAxis() == this || plottable->valueAxis() == this) + result.append(plottable); } return result; } @@ -8717,10 +9412,10 @@ QList QCPAxis::graphs() const QList result; if (!mParentPlot) return result; - for (int i=0; imGraphs.size(); ++i) + foreach (QCPGraph *graph, mParentPlot->mGraphs) { - if (mParentPlot->mGraphs.at(i)->keyAxis() == this || mParentPlot->mGraphs.at(i)->valueAxis() == this) - result.append(mParentPlot->mGraphs.at(i)); + if (graph->keyAxis() == this || graph->valueAxis() == this) + result.append(graph); } return result; } @@ -8736,14 +9431,13 @@ QList QCPAxis::items() const QList result; if (!mParentPlot) return result; - for (int itemId=0; itemIdmItems.size(); ++itemId) + foreach (QCPAbstractItem *item, mParentPlot->mItems) { - QList positions = mParentPlot->mItems.at(itemId)->positions(); - for (int posId=0; posIdpositions()) { - if (positions.at(posId)->keyAxis() == this || positions.at(posId)->valueAxis() == this) + if (position->keyAxis() == this || position->valueAxis() == this) { - result.append(mParentPlot->mItems.at(itemId)); + result.append(item); break; } } @@ -8765,7 +9459,7 @@ QCPAxis::AxisType QCPAxis::marginSideToAxisType(QCP::MarginSide side) case QCP::msBottom: return atBottom; default: break; } - qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << (int)side; + qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << static_cast(side); return atLeft; } @@ -8776,12 +9470,13 @@ QCPAxis::AxisType QCPAxis::opposite(QCPAxis::AxisType type) { switch (type) { - case atLeft: return atRight; break; - case atRight: return atLeft; break; - case atBottom: return atTop; break; - case atTop: return atBottom; break; - default: qDebug() << Q_FUNC_INFO << "invalid axis type"; return atLeft; break; + case atLeft: return atRight; + case atRight: return atLeft; + case atBottom: return atTop; + case atTop: return atBottom; } + qDebug() << Q_FUNC_INFO << "invalid axis type"; + return atLeft; } /* inherits documentation from base class */ @@ -8932,9 +9627,21 @@ void QCPAxis::wheelEvent(QWheelEvent *event) return; } - const double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) + const double delta = event->delta(); +#else + const double delta = event->angleDelta().y(); +#endif + +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + const QPointF pos = event->pos(); +#else + const QPointF pos = event->position(); +#endif + + const double wheelSteps = delta/120.0; // a single step delta is +/-120 usually const double factor = qPow(mAxisRect->rangeZoomFactor(orientation()), wheelSteps); - scaleRange(factor, pixelToCoord(orientation() == Qt::Horizontal ? event->pos().x() : event->pos().y())); + scaleRange(factor, pixelToCoord(orientation() == Qt::Horizontal ? pos.x() : pos.y())); mParentPlot->replot(); } @@ -9026,7 +9733,7 @@ void QCPAxis::setupTickVectors() if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return; QVector oldLabels = mTickVectorLabels; - mTicker->generate(mRange, mParentPlot->locale(), mNumberFormatChar, mNumberPrecision, mTickVector, mSubTicks ? &mSubTickVector : 0, mTickLabels ? &mTickVectorLabels : 0); + mTicker->generate(mRange, mParentPlot->locale(), mNumberFormatChar, mNumberPrecision, mTickVector, mSubTicks ? &mSubTickVector : nullptr, mTickLabels ? &mTickVectorLabels : nullptr); mCachedMarginValid &= mTickVectorLabels == oldLabels; // if labels have changed, margin might have changed, too } @@ -9264,12 +9971,12 @@ void QCPAxisPainterPrivate::draw(QCPPainter *painter) int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; // direction of ticks ("inward" is right for left axis and left for right axis) if (QCPAxis::orientation(type) == Qt::Horizontal) { - for (int i=0; idrawLine(QLineF(tickPositions.at(i)+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPositions.at(i)+xCor, origin.y()+tickLengthIn*tickDir+yCor)); + foreach (double tickPos, tickPositions) + painter->drawLine(QLineF(tickPos+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPos+xCor, origin.y()+tickLengthIn*tickDir+yCor)); } else { - for (int i=0; idrawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPositions.at(i)+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPositions.at(i)+yCor)); + foreach (double tickPos, tickPositions) + painter->drawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPos+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPos+yCor)); } } @@ -9281,12 +9988,12 @@ void QCPAxisPainterPrivate::draw(QCPPainter *painter) int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; if (QCPAxis::orientation(type) == Qt::Horizontal) { - for (int i=0; idrawLine(QLineF(subTickPositions.at(i)+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPositions.at(i)+xCor, origin.y()+subTickLengthIn*tickDir+yCor)); + foreach (double subTickPos, subTickPositions) + painter->drawLine(QLineF(subTickPos+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPos+xCor, origin.y()+subTickLengthIn*tickDir+yCor)); } else { - for (int i=0; idrawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPositions.at(i)+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPositions.at(i)+yCor)); + foreach (double subTickPos, subTickPositions) + painter->drawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPos+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPos+yCor)); } } margin += qMax(0, qMax(tickLengthOut, subTickLengthOut)); @@ -9413,9 +10120,16 @@ void QCPAxisPainterPrivate::draw(QCPPainter *painter) Returns the size ("margin" in QCPAxisRect context, so measured perpendicular to the axis backbone direction) needed to fit the axis. */ -int QCPAxisPainterPrivate::size() const +int QCPAxisPainterPrivate::size() { int result = 0; + + QByteArray newHash = generateLabelParameterHash(); + if (newHash != mLabelParameterHash) + { + mLabelCache.clear(); + mLabelParameterHash = newHash; + } // get length of tick marks pointing outwards: if (!tickPositions.isEmpty()) @@ -9427,8 +10141,8 @@ int QCPAxisPainterPrivate::size() const QSize tickLabelsSize(0, 0); if (!tickLabels.isEmpty()) { - for (int i=0; ibufferDevicePixelRatio())); result.append(QByteArray::number(tickLabelRotation)); - result.append(QByteArray::number((int)tickLabelSide)); - result.append(QByteArray::number((int)substituteExponent)); - result.append(QByteArray::number((int)numberMultiplyCross)); + result.append(QByteArray::number(int(tickLabelSide))); + result.append(QByteArray::number(int(substituteExponent))); + result.append(QByteArray::number(int(numberMultiplyCross))); result.append(tickLabelColor.name().toLatin1()+QByteArray::number(tickLabelColor.alpha(), 16)); result.append(tickLabelFont.toString().toLatin1()); return result; @@ -9668,9 +10382,9 @@ QCPAxisPainterPrivate::TickLabelData QCPAxisPainterPrivate::getTickLabelData(con // prepare smaller font for exponent: result.expFont = font; if (result.expFont.pointSize() > 0) - result.expFont.setPointSize(result.expFont.pointSize()*0.75); + result.expFont.setPointSize(int(result.expFont.pointSize()*0.75)); else - result.expFont.setPixelSize(result.expFont.pixelSize()*0.75); + result.expFont.setPixelSize(int(result.expFont.pixelSize()*0.75)); // calculate bounding rects of base part(s), exponent part and total one: result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart); result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart); @@ -9721,7 +10435,8 @@ QPointF QCPAxisPainterPrivate::getTickLabelDrawOffset(const TickLabelData &label bool doRotation = !qFuzzyIsNull(tickLabelRotation); bool flip = qFuzzyCompare(qAbs(tickLabelRotation), 90.0); // perfect +/-90 degree flip. Indicates vertical label centering on vertical axes. double radians = tickLabelRotation/180.0*M_PI; - int x=0, y=0; + double x = 0; + double y = 0; if ((type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsInside)) // Anchor at right side of tick label { if (doRotation) @@ -9796,7 +10511,7 @@ QPointF QCPAxisPainterPrivate::getTickLabelDrawOffset(const TickLabelData &label } } - return QPointF(x, y); + return {x, y}; } /*! \internal @@ -9829,8 +10544,8 @@ void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString /* end of 'src/axis/axis.cpp' */ -/* including file 'src/scatterstyle.cpp', size 17450 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/scatterstyle.cpp' */ +/* modified 2021-03-29T02:30:44, size 17466 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPScatterStyle @@ -10285,7 +11000,7 @@ void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const const QRectF clipRect = painter->clipBoundingRect().adjusted(-widthHalf, -heightHalf, widthHalf, heightHalf); #endif if (clipRect.contains(x, y)) - painter->drawPixmap(x-widthHalf, y-heightHalf, mPixmap); + painter->drawPixmap(qRound(x-widthHalf), qRound(y-heightHalf), mPixmap); break; } case ssCustom: @@ -10301,10 +11016,9 @@ void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const } /* end of 'src/scatterstyle.cpp' */ -//amalgamation: add datacontainer.cpp -/* including file 'src/plottable.cpp', size 38845 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/plottable.cpp' */ +/* modified 2021-03-29T02:30:44, size 38818 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPSelectionDecorator @@ -10342,9 +11056,8 @@ void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const QCPSelectionDecorator::QCPSelectionDecorator() : mPen(QColor(80, 80, 255), 2.5), mBrush(Qt::NoBrush), - mScatterStyle(), mUsedScatterProperties(QCPScatterStyle::spNone), - mPlottable(0) + mPlottable(nullptr) { } @@ -10550,7 +11263,7 @@ bool QCPSelectionDecorator::registerWithPlottable(QCPAbstractPlottable *plottabl QPointer<\ref QCPAxis> \b mKeyAxis, \b mValueAxis The key and value axes this plottable is attached to. Call their QCPAxis::coordToPixel functions to translate coordinates - to pixels in either the key or value dimension. Make sure to check whether the pointer is null before using it. If one of + to pixels in either the key or value dimension. Make sure to check whether the pointer is \c nullptr before using it. If one of the axes is null, don't draw the plottable. \ref QCPSelectionDecorator \b mSelectionDecorator @@ -10708,7 +11421,7 @@ QCPAbstractPlottable::QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis) mKeyAxis(keyAxis), mValueAxis(valueAxis), mSelectable(QCP::stWhole), - mSelectionDecorator(0) + mSelectionDecorator(nullptr) { if (keyAxis->parentPlot() != valueAxis->parentPlot()) qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis."; @@ -10724,7 +11437,7 @@ QCPAbstractPlottable::~QCPAbstractPlottable() if (mSelectionDecorator) { delete mSelectionDecorator; - mSelectionDecorator = 0; + mSelectionDecorator = nullptr; } } @@ -10863,14 +11576,13 @@ void QCPAbstractPlottable::setSelectionDecorator(QCPSelectionDecorator *decorato { if (decorator->registerWithPlottable(this)) { - if (mSelectionDecorator) // delete old decorator if necessary - delete mSelectionDecorator; + delete mSelectionDecorator; // delete old decorator if necessary mSelectionDecorator = decorator; } } else if (mSelectionDecorator) // just clear decorator { delete mSelectionDecorator; - mSelectionDecorator = 0; + mSelectionDecorator = nullptr; } } @@ -11170,7 +11882,7 @@ QRect QCPAbstractPlottable::clipRect() const if (mKeyAxis && mValueAxis) return mKeyAxis.data()->axisRect()->rect() & mValueAxis.data()->axisRect()->rect(); else - return QRect(); + return {}; } /* inherits documentation from base class */ @@ -11276,8 +11988,8 @@ void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged) /* end of 'src/plottable.cpp' */ -/* including file 'src/item.cpp', size 49271 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/item.cpp' */ +/* modified 2021-03-29T02:30:44, size 49486 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemAnchor @@ -11308,8 +12020,8 @@ void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged) /*! \fn virtual QCPItemPosition *QCPItemAnchor::toQCPItemPosition() - Returns 0 if this instance is merely a QCPItemAnchor, and a valid pointer of type QCPItemPosition* if - it actually is a QCPItemPosition (which is a subclass of QCPItemAnchor). + Returns \c nullptr if this instance is merely a QCPItemAnchor, and a valid pointer of type + QCPItemPosition* if it actually is a QCPItemPosition (which is a subclass of QCPItemAnchor). This safe downcast functionality could also be achieved with a dynamic_cast. However, QCustomPlot avoids dynamic_cast to work with projects that don't have RTTI support enabled (e.g. -fno-rtti flag with @@ -11334,15 +12046,15 @@ QCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentIte QCPItemAnchor::~QCPItemAnchor() { // unregister as parent at children: - foreach (QCPItemPosition *child, mChildrenX.toList()) + foreach (QCPItemPosition *child, mChildrenX.values()) { if (child->parentAnchorX() == this) - child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX + child->setParentAnchorX(nullptr); // this acts back on this anchor and child removes itself from mChildrenX } - foreach (QCPItemPosition *child, mChildrenY.toList()) + foreach (QCPItemPosition *child, mChildrenY.values()) { if (child->parentAnchorY() == this) - child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY + child->setParentAnchorY(nullptr); // this acts back on this anchor and child removes itself from mChildrenY } } @@ -11362,12 +12074,12 @@ QPointF QCPItemAnchor::pixelPosition() const } else { qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId; - return QPointF(); + return {}; } } else { qDebug() << Q_FUNC_INFO << "no parent item set"; - return QPointF(); + return {}; } } @@ -11441,10 +12153,11 @@ void QCPItemAnchor::removeChildY(QCPItemPosition *pos) QCPItemPosition has a type (\ref PositionType) that can be set with \ref setType. This type defines how coordinates passed to \ref setCoords are to be interpreted, e.g. as absolute pixel - coordinates, as plot coordinates of certain axes, etc. For more advanced plots it is also + coordinates, as plot coordinates of certain axes (\ref QCPItemPosition::setAxes), as fractions of + the axis rect (\ref QCPItemPosition::setAxisRect), etc. For more advanced plots it is also possible to assign different types per X/Y coordinate of the position (see \ref setTypeX, \ref - setTypeY). This way an item could be positioned at a fixed pixel distance from the top in the Y - direction, while following a plot coordinate in the X direction. + setTypeY). This way an item could be positioned for example at a fixed pixel distance from the + top in the Y direction, while following a plot coordinate in the X direction. A QCPItemPosition may have a parent QCPItemAnchor, see \ref setParentAnchor. This way you can tie multiple items together. If the QCPItemPosition has a parent, its coordinates (\ref setCoords) @@ -11502,8 +12215,8 @@ QCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *paren mPositionTypeY(ptAbsolute), mKey(0), mValue(0), - mParentAnchorX(0), - mParentAnchorY(0) + mParentAnchorX(nullptr), + mParentAnchorY(nullptr) { } @@ -11512,15 +12225,15 @@ QCPItemPosition::~QCPItemPosition() // unregister as parent at children: // Note: this is done in ~QCPItemAnchor again, but it's important QCPItemPosition does it itself, because only then // the setParentAnchor(0) call the correct QCPItemPosition::pixelPosition function instead of QCPItemAnchor::pixelPosition - foreach (QCPItemPosition *child, mChildrenX.toList()) + foreach (QCPItemPosition *child, mChildrenX.values()) { if (child->parentAnchorX() == this) - child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX + child->setParentAnchorX(nullptr); // this acts back on this anchor and child removes itself from mChildrenX } - foreach (QCPItemPosition *child, mChildrenY.toList()) + foreach (QCPItemPosition *child, mChildrenY.values()) { if (child->parentAnchorY() == this) - child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY + child->setParentAnchorY(nullptr); // this acts back on this anchor and child removes itself from mChildrenY } // unregister as child in parent: if (mParentAnchorX) @@ -11636,7 +12349,7 @@ void QCPItemPosition::setTypeY(QCPItemPosition::PositionType type) during reparenting. If it's set to false, the coordinates are set to (0, 0), i.e. the position will be exactly on top of the parent anchor. - To remove this QCPItemPosition from any parent anchor, set \a parentAnchor to 0. + To remove this QCPItemPosition from any parent anchor, set \a parentAnchor to \c nullptr. If the QCPItemPosition previously had no parent and the type is \ref ptPlotCoords, the type is set to \ref ptAbsolute, to keep the position in a valid state. @@ -11965,7 +12678,7 @@ void QCPItemPosition::setPixelPosition(const QPointF &pixelPosition) x -= mParentAnchorX->pixelPosition().x(); else x -= mParentPlot->viewport().left(); - x /= (double)mParentPlot->viewport().width(); + x /= double(mParentPlot->viewport().width()); break; } case ptAxisRectRatio: @@ -11976,7 +12689,7 @@ void QCPItemPosition::setPixelPosition(const QPointF &pixelPosition) x -= mParentAnchorX->pixelPosition().x(); else x -= mAxisRect.data()->left(); - x /= (double)mAxisRect.data()->width(); + x /= double(mAxisRect.data()->width()); } else qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined"; break; @@ -12007,7 +12720,7 @@ void QCPItemPosition::setPixelPosition(const QPointF &pixelPosition) y -= mParentAnchorY->pixelPosition().y(); else y -= mParentPlot->viewport().top(); - y /= (double)mParentPlot->viewport().height(); + y /= double(mParentPlot->viewport().height()); break; } case ptAxisRectRatio: @@ -12018,7 +12731,7 @@ void QCPItemPosition::setPixelPosition(const QPointF &pixelPosition) y -= mParentAnchorY->pixelPosition().y(); else y -= mAxisRect.data()->top(); - y /= (double)mAxisRect.data()->height(); + y /= double(mAxisRect.data()->height()); } else qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined"; break; @@ -12220,7 +12933,7 @@ QCPAbstractItem::QCPAbstractItem(QCustomPlot *parentPlot) : parentPlot->registerItem(this); QList rects = parentPlot->axisRects(); - if (rects.size() > 0) + if (!rects.isEmpty()) { setClipToAxisRect(true); setClipAxisRect(rects.first()); @@ -12308,7 +13021,7 @@ void QCPAbstractItem::setSelected(bool selected) /*! Returns the QCPItemPosition with the specified \a name. If this item doesn't have a position by - that name, returns 0. + that name, returns \c nullptr. This function provides an alternative way to access item positions. Normally, you access positions direcly by their member pointers (which typically have the same variable name as \a @@ -12318,18 +13031,18 @@ void QCPAbstractItem::setSelected(bool selected) */ QCPItemPosition *QCPAbstractItem::position(const QString &name) const { - for (int i=0; iname() == name) - return mPositions.at(i); + if (position->name() == name) + return position; } qDebug() << Q_FUNC_INFO << "position with name not found:" << name; - return 0; + return nullptr; } /*! Returns the QCPItemAnchor with the specified \a name. If this item doesn't have an anchor by - that name, returns 0. + that name, returns \c nullptr. This function provides an alternative way to access item anchors. Normally, you access anchors direcly by their member pointers (which typically have the same variable name as \a @@ -12339,13 +13052,13 @@ QCPItemPosition *QCPAbstractItem::position(const QString &name) const */ QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const { - for (int i=0; iname() == name) - return mAnchors.at(i); + if (anchor->name() == name) + return anchor; } qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name; - return 0; + return nullptr; } /*! @@ -12358,9 +13071,9 @@ QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const */ bool QCPAbstractItem::hasAnchor(const QString &name) const { - for (int i=0; iname() == name) + if (anchor->name() == name) return true; } return false; @@ -12419,13 +13132,13 @@ double QCPAbstractItem::rectDistance(const QRectF &rect, const QPointF &pos, boo double result = -1; // distance to border: - QList lines; - lines << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight()) - << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight()); + const QList lines = QList() << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight()) + << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight()); + const QCPVector2D posVec(pos); double minDistSqr = (std::numeric_limits::max)(); - for (int i=0; isetDevicePixelRatio(mBufferDevicePixelRatio); + foreach (QSharedPointer buffer, mPaintBuffers) + buffer->setDevicePixelRatio(mBufferDevicePixelRatio); // Note: axis label cache has devicePixelRatio as part of cache hash, so no need to manually clear cache here #else qDebug() << Q_FUNC_INFO << "Device pixel ratios not supported for Qt versions before 5.4"; @@ -13494,7 +14229,7 @@ void QCustomPlot::setBackgroundScaledMode(Qt::AspectRatioMode mode) } /*! - Returns the plottable with \a index. If the index is invalid, returns 0. + Returns the plottable with \a index. If the index is invalid, returns \c nullptr. There is an overloaded version of this function with no parameter which returns the last added plottable, see QCustomPlot::plottable() @@ -13509,14 +14244,14 @@ QCPAbstractPlottable *QCustomPlot::plottable(int index) } else { qDebug() << Q_FUNC_INFO << "index out of bounds:" << index; - return 0; + return nullptr; } } /*! \overload Returns the last plottable that was added to the plot. If there are no plottables in the plot, - returns 0. + returns \c nullptr. \see plottableCount */ @@ -13526,7 +14261,7 @@ QCPAbstractPlottable *QCustomPlot::plottable() { return mPlottables.last(); } else - return 0; + return nullptr; } /*! @@ -13616,38 +14351,17 @@ QList QCustomPlot::selectedPlottables() const } /*! - Returns the plottable at the pixel position \a pos. Plottables that only consist of single lines - (like graphs) have a tolerance band around them, see \ref setSelectionTolerance. If multiple - plottables come into consideration, the one closest to \a pos is returned. + Returns any plottable at the pixel position \a pos. Since it can capture all plottables, the + return type is the abstract base class of all plottables, QCPAbstractPlottable. - If \a onlySelectable is true, only plottables that are selectable - (QCPAbstractPlottable::setSelectable) are considered. + For details, and if you wish to specify a certain plottable type (e.g. QCPGraph), see the + template method plottableAt() - If there is no plottable at \a pos, the return value is 0. - - \see itemAt, layoutElementAt + \see plottableAt(), itemAt, layoutElementAt */ -QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable) const +QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable, int *dataIndex) const { - QCPAbstractPlottable *resultPlottable = 0; - double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value - - foreach (QCPAbstractPlottable *plottable, mPlottables) - { - if (onlySelectable && !plottable->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPabstractPlottable::selectable - continue; - if ((plottable->keyAxis()->axisRect()->rect() & plottable->valueAxis()->axisRect()->rect()).contains(pos.toPoint())) // only consider clicks inside the rect that is spanned by the plottable's key/value axes - { - double currentDistance = plottable->selectTest(pos, false); - if (currentDistance >= 0 && currentDistance < resultDistance) - { - resultPlottable = plottable; - resultDistance = currentDistance; - } - } - } - - return resultPlottable; + return plottableAt(pos, onlySelectable, dataIndex); } /*! @@ -13659,7 +14373,7 @@ bool QCustomPlot::hasPlottable(QCPAbstractPlottable *plottable) const } /*! - Returns the graph with \a index. If the index is invalid, returns 0. + Returns the graph with \a index. If the index is invalid, returns \c nullptr. There is an overloaded version of this function with no parameter which returns the last created graph, see QCustomPlot::graph() @@ -13674,14 +14388,14 @@ QCPGraph *QCustomPlot::graph(int index) const } else { qDebug() << Q_FUNC_INFO << "index out of bounds:" << index; - return 0; + return nullptr; } } /*! \overload Returns the last graph, that was created with \ref addGraph. If there are no graphs in the plot, - returns 0. + returns \c nullptr. \see graphCount, addGraph */ @@ -13691,7 +14405,7 @@ QCPGraph *QCustomPlot::graph() const { return mGraphs.last(); } else - return 0; + return nullptr; } /*! @@ -13702,7 +14416,7 @@ QCPGraph *QCustomPlot::graph() const \a keyAxis will be used as key axis (typically "x") and \a valueAxis as value axis (typically "y") for the graph. - Returns a pointer to the newly created graph, or 0 if adding the graph failed. + Returns a pointer to the newly created graph, or \c nullptr if adding the graph failed. \see graph, graphCount, removeGraph, clearGraphs */ @@ -13713,12 +14427,12 @@ QCPGraph *QCustomPlot::addGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "can't use default QCustomPlot xAxis or yAxis, because at least one is invalid (has been deleted)"; - return 0; + return nullptr; } if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this) { qDebug() << Q_FUNC_INFO << "passed keyAxis or valueAxis doesn't have this QCustomPlot as parent"; - return 0; + return nullptr; } QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis); @@ -13730,7 +14444,7 @@ QCPGraph *QCustomPlot::addGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) Removes the specified \a graph from the plot and deletes it. If necessary, the corresponding legend item is also removed from the default legend (QCustomPlot::legend). If any other graphs in the plot have a channel fill set towards the removed graph, the channel fill property of those - graphs is reset to zero (no channel fill). + graphs is reset to \c nullptr (no channel fill). Returns true on success. @@ -13799,7 +14513,7 @@ QList QCustomPlot::selectedGraphs() const } /*! - Returns the item with \a index. If the index is invalid, returns 0. + Returns the item with \a index. If the index is invalid, returns \c nullptr. There is an overloaded version of this function with no parameter which returns the last added item, see QCustomPlot::item() @@ -13814,14 +14528,14 @@ QCPAbstractItem *QCustomPlot::item(int index) const } else { qDebug() << Q_FUNC_INFO << "index out of bounds:" << index; - return 0; + return nullptr; } } /*! \overload Returns the last item that was added to this plot. If there are no items in the plot, - returns 0. + returns \c nullptr. \see itemCount */ @@ -13831,7 +14545,7 @@ QCPAbstractItem *QCustomPlot::item() const { return mItems.last(); } else - return 0; + return nullptr; } /*! @@ -13912,39 +14626,17 @@ QList QCustomPlot::selectedItems() const } /*! - Returns the item at the pixel position \a pos. Items that only consist of single lines (e.g. \ref - QCPItemLine or \ref QCPItemCurve) have a tolerance band around them, see \ref - setSelectionTolerance. If multiple items come into consideration, the one closest to \a pos is - returned. - - If \a onlySelectable is true, only items that are selectable (QCPAbstractItem::setSelectable) are - considered. + Returns the item at the pixel position \a pos. Since it can capture all items, the + return type is the abstract base class of all items, QCPAbstractItem. - If there is no item at \a pos, the return value is 0. + For details, and if you wish to specify a certain item type (e.g. QCPItemLine), see the + template method itemAt() - \see plottableAt, layoutElementAt + \see itemAt(), plottableAt, layoutElementAt */ QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const { - QCPAbstractItem *resultItem = 0; - double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value - - foreach (QCPAbstractItem *item, mItems) - { - if (onlySelectable && !item->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPAbstractItem::selectable - continue; - if (!item->clipToAxisRect() || item->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it - { - double currentDistance = item->selectTest(pos, false); - if (currentDistance >= 0 && currentDistance < resultDistance) - { - resultItem = item; - resultDistance = currentDistance; - } - } - } - - return resultItem; + return itemAt(pos, onlySelectable); } /*! @@ -13958,8 +14650,8 @@ bool QCustomPlot::hasItem(QCPAbstractItem *item) const } /*! - Returns the layer with the specified \a name. If there is no layer with the specified name, 0 is - returned. + Returns the layer with the specified \a name. If there is no layer with the specified name, \c + nullptr is returned. Layer names are case-sensitive. @@ -13972,12 +14664,12 @@ QCPLayer *QCustomPlot::layer(const QString &name) const if (layer->name() == name) return layer; } - return 0; + return nullptr; } /*! \overload - Returns the layer by \a index. If the index is invalid, 0 is returned. + Returns the layer by \a index. If the index is invalid, \c nullptr is returned. \see addLayer, moveLayer, removeLayer */ @@ -13989,7 +14681,7 @@ QCPLayer *QCustomPlot::layer(int index) const } else { qDebug() << Q_FUNC_INFO << "index out of bounds:" << index; - return 0; + return nullptr; } } @@ -14120,21 +14812,19 @@ bool QCustomPlot::removeLayer(QCPLayer *layer) bool isFirstLayer = removedIndex==0; QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex+1) : mLayers.at(removedIndex-1); QList children = layer->children(); - if (isFirstLayer) // prepend in reverse order (so order relative to each other stays the same) - { - for (int i=children.size()-1; i>=0; --i) - children.at(i)->moveToLayer(targetLayer, true); - } else // append normally - { - for (int i=0; imoveToLayer(targetLayer, false); - } + if (isFirstLayer) // prepend in reverse order (such that relative order stays the same) + std::reverse(children.begin(), children.end()); + foreach (QCPLayerable *child, children) + child->moveToLayer(targetLayer, isFirstLayer); // prepend if isFirstLayer, otherwise append + // if removed layer is current layer, change current layer to layer below/above: if (layer == mCurrentLayer) setCurrentLayer(targetLayer); + // invalidate the paint buffer that was responsible for this layer: - if (!layer->mPaintBuffer.isNull()) - layer->mPaintBuffer.data()->setInvalidated(); + if (QSharedPointer pb = layer->mPaintBuffer.toStrongRef()) + pb->setInvalidated(); + // remove layer: delete layer; mLayers.removeOne(layer); @@ -14170,10 +14860,10 @@ bool QCustomPlot::moveLayer(QCPLayer *layer, QCPLayer *otherLayer, QCustomPlot:: mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 0:-1)); // invalidate the paint buffers that are responsible for the layers: - if (!layer->mPaintBuffer.isNull()) - layer->mPaintBuffer.data()->setInvalidated(); - if (!otherLayer->mPaintBuffer.isNull()) - otherLayer->mPaintBuffer.data()->setInvalidated(); + if (QSharedPointer pb = layer->mPaintBuffer.toStrongRef()) + pb->setInvalidated(); + if (QSharedPointer pb = otherLayer->mPaintBuffer.toStrongRef()) + pb->setInvalidated(); updateLayerIndices(); return true; @@ -14221,7 +14911,7 @@ QCPAxisRect *QCustomPlot::axisRect(int index) const } else { qDebug() << Q_FUNC_INFO << "invalid axis rect index" << index; - return 0; + return nullptr; } } @@ -14261,7 +14951,7 @@ QList QCustomPlot::axisRects() const /*! Returns the layout element at pixel position \a pos. If there is no element at that position, - returns 0. + returns \c nullptr. Only visible elements are used. If \ref QCPLayoutElement::setVisible on the element itself or on any of its parent elements is set to false, it will not be considered. @@ -14291,7 +14981,7 @@ QCPLayoutElement *QCustomPlot::layoutElementAt(const QPointF &pos) const /*! Returns the layout element of type \ref QCPAxisRect at pixel position \a pos. This method ignores other layout elements even if they are visually in front of the axis rect (e.g. a \ref - QCPLegend). If there is no axis rect at that position, returns 0. + QCPLegend). If there is no axis rect at that position, returns \c nullptr. Only visible axis rects are used. If \ref QCPLayoutElement::setVisible on the axis rect itself or on any of its parent elements is set to false, it will not be considered. @@ -14300,7 +14990,7 @@ QCPLayoutElement *QCustomPlot::layoutElementAt(const QPointF &pos) const */ QCPAxisRect *QCustomPlot::axisRectAt(const QPointF &pos) const { - QCPAxisRect *result = 0; + QCPAxisRect *result = nullptr; QCPLayoutElement *currentElement = mPlotLayout; bool searchSubElements = true; while (searchSubElements && currentElement) @@ -14391,7 +15081,7 @@ void QCustomPlot::deselectAll() foreach (QCPLayer *layer, mLayers) { foreach (QCPLayerable *layerable, layer->children()) - layerable->deselectEvent(0); + layerable->deselectEvent(nullptr); } } @@ -14419,6 +15109,8 @@ void QCustomPlot::deselectAll() If a layer is in mode \ref QCPLayer::lmBuffered (\ref QCPLayer::setMode), it is also possible to replot only that specific layer via \ref QCPLayer::replot. See the documentation there for details. + + \see replotTime */ void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority) { @@ -14438,23 +15130,52 @@ void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority) mReplotQueued = false; emit beforeReplot(); +# if QT_VERSION < QT_VERSION_CHECK(4, 8, 0) + QTime replotTimer; + replotTimer.start(); +# else + QElapsedTimer replotTimer; + replotTimer.start(); +# endif + updateLayout(); // draw all layered objects (grid, axes, plottables, items, legend,...) into their buffers: setupPaintBuffers(); foreach (QCPLayer *layer, mLayers) layer->drawToPaintBuffer(); - for (int i=0; isetInvalidated(false); + foreach (QSharedPointer buffer, mPaintBuffers) + buffer->setInvalidated(false); if ((refreshPriority == rpRefreshHint && mPlottingHints.testFlag(QCP::phImmediateRefresh)) || refreshPriority==rpImmediateRefresh) repaint(); else update(); +# if QT_VERSION < QT_VERSION_CHECK(4, 8, 0) + mReplotTime = replotTimer.elapsed(); +# else + mReplotTime = replotTimer.nsecsElapsed()*1e-6; +# endif + if (!qFuzzyIsNull(mReplotTimeAverage)) + mReplotTimeAverage = mReplotTimeAverage*0.9 + mReplotTime*0.1; // exponential moving average with a time constant of 10 last replots + else + mReplotTimeAverage = mReplotTime; // no previous replots to average with, so initialize with replot time + emit afterReplot(); mReplotting = false; } +/*! + Returns the time in milliseconds that the last replot took. If \a average is set to true, an + exponential moving average over the last couple of replots is returned. + + \see replot +*/ +double QCustomPlot::replotTime(bool average) const +{ + return average ? mReplotTimeAverage : mReplotTime; +} + /*! Rescales the axes such that all plottables (like graphs) in the plot are fully visible. @@ -14744,16 +15465,18 @@ QSize QCustomPlot::sizeHint() const */ void QCustomPlot::paintEvent(QPaintEvent *event) { - Q_UNUSED(event); + Q_UNUSED(event) QCPPainter painter(this); if (painter.isActive()) { - painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem +#endif if (mBackgroundBrush.style() != Qt::NoBrush) painter.fillRect(mViewport, mBackgroundBrush); drawBackground(&painter); - for (int bufferIndex = 0; bufferIndex < mPaintBuffers.size(); ++bufferIndex) - mPaintBuffers.at(bufferIndex)->draw(&painter); + foreach (QSharedPointer buffer, mPaintBuffers) + buffer->draw(&painter); } } @@ -14814,7 +15537,7 @@ void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event) else if (QCPAbstractItem *ai = qobject_cast(candidates.first())) emit itemDoubleClick(ai, event); else if (QCPLegend *lg = qobject_cast(candidates.first())) - emit legendDoubleClick(lg, 0, event); + emit legendDoubleClick(lg, nullptr, event); else if (QCPAbstractLegendItem *li = qobject_cast(candidates.first())) emit legendDoubleClick(li->parentLegend(), li, event); } @@ -14933,10 +15656,10 @@ void QCustomPlot::mouseReleaseEvent(QMouseEvent *event) else if (QCPAbstractItem *ai = qobject_cast(mMouseSignalLayerable)) emit itemClick(ai, event); else if (QCPLegend *lg = qobject_cast(mMouseSignalLayerable)) - emit legendClick(lg, 0, event); + emit legendClick(lg, nullptr, event); else if (QCPAbstractLegendItem *li = qobject_cast(mMouseSignalLayerable)) emit legendClick(li->parentLegend(), li, event); - mMouseSignalLayerable = 0; + mMouseSignalLayerable = nullptr; } if (mSelectionRect && mSelectionRect->isActive()) // Note: if a click was detected above, the selection rect is canceled there @@ -14949,7 +15672,7 @@ void QCustomPlot::mouseReleaseEvent(QMouseEvent *event) if (mMouseEventLayerable) { mMouseEventLayerable->mouseReleaseEvent(event, mMousePressPos); - mMouseEventLayerable = 0; + mMouseEventLayerable = nullptr; } } @@ -14967,12 +15690,18 @@ void QCustomPlot::mouseReleaseEvent(QMouseEvent *event) void QCustomPlot::wheelEvent(QWheelEvent *event) { emit mouseWheel(event); + +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + const QPointF pos = event->pos(); +#else + const QPointF pos = event->position(); +#endif + // forward event to layerable under cursor: - QList candidates = layerableListAt(event->pos(), false); - for (int i=0; iaccept(); // default impl of QCPLayerable's mouse events ignore the event, in that case propagate to next candidate in list - candidates.at(i)->wheelEvent(event); + candidate->wheelEvent(event); if (event->isAccepted()) break; } @@ -15001,7 +15730,7 @@ void QCustomPlot::draw(QCPPainter *painter) layer->draw(painter); /* Debug code to draw all layout element rects - foreach (QCPLayoutElement* el, findChildren()) + foreach (QCPLayoutElement *el, findChildren()) { painter->setBrush(Qt::NoBrush); painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine)); @@ -15026,6 +15755,8 @@ void QCustomPlot::updateLayout() mPlotLayout->update(QCPLayoutElement::upPreparation); mPlotLayout->update(QCPLayoutElement::upMargins); mPlotLayout->update(QCPLayoutElement::upLayout); + + emit afterLayout(); } /*! \internal @@ -15116,11 +15847,11 @@ void QCustomPlot::setupPaintBuffers() while (mPaintBuffers.size()-1 > bufferIndex) mPaintBuffers.removeLast(); // resize buffers to viewport size and clear contents: - for (int i=0; i buffer, mPaintBuffers) { - mPaintBuffers.at(i)->setSize(viewport().size()); // won't do anything if already correct size - mPaintBuffers.at(i)->clear(Qt::transparent); - mPaintBuffers.at(i)->setInvalidated(); + buffer->setSize(viewport().size()); // won't do anything if already correct size + buffer->clear(Qt::transparent); + buffer->setInvalidated(); } } @@ -15154,16 +15885,16 @@ QCPAbstractPaintBuffer *QCustomPlot::createPaintBuffer() If any buffer is invalidated, a partial replot (\ref QCPLayer::replot) is not allowed and always causes a full replot (\ref QCustomPlot::replot) of all layers. This is the case when for example - the layer order has changed, new layers were added, layers were removed, or layer modes were - changed (\ref QCPLayer::setMode). + the layer order has changed, new layers were added or removed, layer modes were changed (\ref + QCPLayer::setMode), or layerables were added or removed. \see QCPAbstractPaintBuffer::setInvalidated */ bool QCustomPlot::hasInvalidatedPaintBuffers() { - for (int i=0; i buffer, mPaintBuffers) { - if (mPaintBuffers.at(i)->invalidated()) + if (buffer->invalidated()) return true; } return false; @@ -15257,13 +15988,13 @@ void QCustomPlot::freeOpenGl() void QCustomPlot::axisRemoved(QCPAxis *axis) { if (xAxis == axis) - xAxis = 0; + xAxis = nullptr; if (xAxis2 == axis) - xAxis2 = 0; + xAxis2 = nullptr; if (yAxis == axis) - yAxis = 0; + yAxis = nullptr; if (yAxis2 == axis) - yAxis2 = 0; + yAxis2 = nullptr; // Note: No need to take care of range drag axes and range zoom axes, because they are stored in smart pointers } @@ -15276,7 +16007,7 @@ void QCustomPlot::axisRemoved(QCPAxis *axis) void QCustomPlot::legendRemoved(QCPLegend *legend) { if (this->legend == legend) - this->legend = 0; + this->legend = nullptr; } /*! \internal @@ -15298,11 +16029,14 @@ void QCustomPlot::legendRemoved(QCPLegend *legend) */ void QCustomPlot::processRectSelection(QRect rect, QMouseEvent *event) { + typedef QPair SelectionCandidate; + typedef QMultiMap SelectionCandidates; // map key is number of selected data points, so we have selections sorted by size + bool selectionStateChanged = false; if (mInteractions.testFlag(QCP::iSelectPlottables)) { - QMap > potentialSelections; // map key is number of selected data points, so we have selections sorted by size + SelectionCandidates potentialSelections; QRectF rectF(rect.normalized()); if (QCPAxisRect *affectedAxisRect = axisRectAt(rectF.topLeft())) { @@ -15313,7 +16047,7 @@ void QCustomPlot::processRectSelection(QRect rect, QMouseEvent *event) { QCPDataSelection dataSel = plottableInterface->selectTestRect(rectF, true); if (!dataSel.isEmpty()) - potentialSelections.insertMulti(dataSel.dataPointCount(), QPair(plottable, dataSel)); + potentialSelections.insert(dataSel.dataPointCount(), SelectionCandidate(plottable, dataSel)); } } @@ -15322,8 +16056,8 @@ void QCustomPlot::processRectSelection(QRect rect, QMouseEvent *event) // only leave plottable with most selected points in map, since we will only select a single plottable: if (!potentialSelections.isEmpty()) { - QMap >::iterator it = potentialSelections.begin(); - while (it != potentialSelections.end()-1) // erase all except last element + SelectionCandidates::iterator it = potentialSelections.begin(); + while (it != std::prev(potentialSelections.end())) // erase all except last element it = potentialSelections.erase(it); } } @@ -15348,7 +16082,7 @@ void QCustomPlot::processRectSelection(QRect rect, QMouseEvent *event) } // go through selections in reverse (largest selection first) and emit select events: - QMap >::const_iterator it = potentialSelections.constEnd(); + SelectionCandidates::const_iterator it = potentialSelections.constEnd(); while (it != potentialSelections.constBegin()) { --it; @@ -15387,7 +16121,7 @@ void QCustomPlot::processRectZoom(QRect rect, QMouseEvent *event) if (QCPAxisRect *axisRect = axisRectAt(rect.topLeft())) { QList affectedAxes = QList() << axisRect->rangeZoomAxes(Qt::Horizontal) << axisRect->rangeZoomAxes(Qt::Vertical); - affectedAxes.removeAll(static_cast(0)); + affectedAxes.removeAll(static_cast(nullptr)); axisRect->zoom(QRectF(rect), affectedAxes); } replot(rpQueuedReplot); // always replot to make selection rect disappear @@ -15564,13 +16298,13 @@ void QCustomPlot::updateLayerIndices() const QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails) const { QList details; - QList candidates = layerableListAt(pos, onlySelectable, selectionDetails ? &details : 0); + QList candidates = layerableListAt(pos, onlySelectable, selectionDetails ? &details : nullptr); if (selectionDetails && !details.isEmpty()) *selectionDetails = details.first(); if (!candidates.isEmpty()) return candidates.first(); else - return 0; + return nullptr; } /*! \internal @@ -15579,7 +16313,8 @@ QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable, layerables that are selectable will be considered. (Layerable subclasses communicate their selectability via the QCPLayerable::selectTest method, by returning -1.) - The returned list is sorted by the layerable/drawing order. If you only need to know the top-most + The returned list is sorted by the layerable/drawing order such that the layerable that appears + on top in the plot is at index 0 of the returned list. If you only need to know the top layerable, rather use \ref layerableAt. \a selectionDetails is an output parameter that contains selection specifics of the affected @@ -15602,7 +16337,7 @@ QList QCustomPlot::layerableListAt(const QPointF &pos, bool onlyS if (!layerables.at(i)->realVisibility()) continue; QVariant details; - double dist = layerables.at(i)->selectTest(pos, onlySelectable, selectionDetails ? &details : 0); + double dist = layerables.at(i)->selectTest(pos, onlySelectable, selectionDetails ? &details : nullptr); if (dist >= 0 && dist < selectionTolerance()) { result.append(layerables.at(i)); @@ -15641,7 +16376,7 @@ bool QCustomPlot::saveRastered(const QString &fileName, int width, int height, d { case QCP::ruDotsPerMeter: dotsPerMeter = resolution; break; case QCP::ruDotsPerCentimeter: dotsPerMeter = resolution*100; break; - case QCP::ruDotsPerInch: dotsPerMeter = resolution/0.0254; break; + case QCP::ruDotsPerInch: dotsPerMeter = int(resolution/0.0254); break; } buffer.setDotsPerMeterX(dotsPerMeter); // this is saved together with some image formats, e.g. PNG, and is relevant when opening image in other tools buffer.setDotsPerMeterY(dotsPerMeter); // this is saved together with some image formats, e.g. PNG, and is relevant when opening image in other tools @@ -15743,10 +16478,9 @@ void QCustomPlot::toPainter(QCPPainter *painter, int width, int height) } /* end of 'src/core.cpp' */ -//amalgamation: add plottable1d.cpp -/* including file 'src/colorgradient.cpp', size 25342 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/colorgradient.cpp' */ +/* modified 2021-03-29T02:30:44, size 25278 */ //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -15769,9 +16503,11 @@ void QCustomPlot::toPainter(QCPPainter *painter, int width, int height) configured color stops. This allows to display some portions of the data range as transparent in the plot. + How NaN values are interpreted can be configured with \ref setNanHandling. + \image html QCPColorGradient.png - The \ref QCPColorGradient(GradientPreset preset) constructor allows directly converting a \ref + The constructor \ref QCPColorGradient(GradientPreset preset) allows directly converting a \ref GradientPreset to a QCPColorGradient. This means that you can directly pass \ref GradientPreset to all the \a setGradient methods, e.g.: \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorgradient-setgradient @@ -15790,6 +16526,8 @@ void QCustomPlot::toPainter(QCPPainter *painter, int width, int height) QCPColorGradient::QCPColorGradient() : mLevelCount(350), mColorInterpolation(ciRGB), + mNanHandling(nhNone), + mNanColor(Qt::black), mPeriodic(false), mColorBufferInvalidated(true) { @@ -15805,6 +16543,8 @@ QCPColorGradient::QCPColorGradient() : QCPColorGradient::QCPColorGradient(GradientPreset preset) : mLevelCount(350), mColorInterpolation(ciRGB), + mNanHandling(nhNone), + mNanColor(Qt::black), mPeriodic(false), mColorBufferInvalidated(true) { @@ -15817,6 +16557,8 @@ bool QCPColorGradient::operator==(const QCPColorGradient &other) const { return ((other.mLevelCount == this->mLevelCount) && (other.mColorInterpolation == this->mColorInterpolation) && + (other.mNanHandling == this ->mNanHandling) && + (other.mNanColor == this->mNanColor) && (other.mPeriodic == this->mPeriodic) && (other.mColorStops == this->mColorStops)); } @@ -15886,6 +16628,27 @@ void QCPColorGradient::setColorInterpolation(QCPColorGradient::ColorInterpolatio } } +/*! + Sets how NaNs in the data are displayed in the plot. + + \see setNanColor +*/ +void QCPColorGradient::setNanHandling(QCPColorGradient::NanHandling handling) +{ + mNanHandling = handling; +} + +/*! + Sets the color that NaN data is represented by, if \ref setNanHandling is set + to ref nhNanColor. + + \see setNanHandling +*/ +void QCPColorGradient::setNanColor(const QColor &color) +{ + mNanColor = color; +} + /*! Sets whether data points that are outside the configured data range (e.g. \ref QCPColorMap::setDataRange) are colored by periodically repeating the color gradient or whether @@ -15921,7 +16684,7 @@ void QCPColorGradient::setPeriodic(bool enabled) Use the overloaded method to additionally provide alpha map data. - The QRgb values that are placed in \a scanLine have their r, g and b components premultiplied + The QRgb values that are placed in \a scanLine have their r, g, and b components premultiplied with alpha (see QImage::Format_ARGB32_Premultiplied). */ void QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic) @@ -15940,51 +16703,33 @@ void QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb if (mColorBufferInvalidated) updateColorBuffer(); - if (!logarithmic) + const bool skipNanCheck = mNanHandling == nhNone; + const double posToIndexFactor = !logarithmic ? (mLevelCount-1)/range.size() : (mLevelCount-1)/qLn(range.upper/range.lower); + for (int i=0; i= mLevelCount) - index = mLevelCount-1; - scanLine[i] = mColorBuffer.at(index); - } - } - } else // logarithmic == true - { - if (mPeriodic) - { - for (int i=0; i= mLevelCount) - index = mLevelCount-1; - scanLine[i] = mColorBuffer.at(index); + case nhLowestColor: scanLine[i] = mColorBuffer.first(); break; + case nhHighestColor: scanLine[i] = mColorBuffer.last(); break; + case nhTransparent: scanLine[i] = qRgba(0, 0, 0, 0); break; + case nhNanColor: scanLine[i] = mNanColor.rgba(); break; + case nhNone: break; // shouldn't happen } } } @@ -16019,83 +16764,41 @@ void QCPColorGradient::colorize(const double *data, const unsigned char *alpha, if (mColorBufferInvalidated) updateColorBuffer(); - if (!logarithmic) + const bool skipNanCheck = mNanHandling == nhNone; + const double posToIndexFactor = !logarithmic ? (mLevelCount-1)/range.size() : (mLevelCount-1)/qLn(range.upper/range.lower); + for (int i=0; i= mLevelCount) - index = mLevelCount-1; - if (alpha[dataIndexFactor*i] == 255) - { - scanLine[i] = mColorBuffer.at(index); - } else - { - const QRgb rgb = mColorBuffer.at(index); - const float alphaF = alpha[dataIndexFactor*i]/255.0f; - scanLine[i] = qRgba(qRed(rgb)*alphaF, qGreen(rgb)*alphaF, qBlue(rgb)*alphaF, qAlpha(rgb)*alphaF); - } - } - } - } else // logarithmic == true - { - if (mPeriodic) - { - for (int i=0; i= mLevelCount) - index = mLevelCount-1; - if (alpha[dataIndexFactor*i] == 255) - { - scanLine[i] = mColorBuffer.at(index); - } else - { - const QRgb rgb = mColorBuffer.at(index); - const float alphaF = alpha[dataIndexFactor*i]/255.0f; - scanLine[i] = qRgba(qRed(rgb)*alphaF, qGreen(rgb)*alphaF, qBlue(rgb)*alphaF, qAlpha(rgb)*alphaF); - } + case nhLowestColor: scanLine[i] = mColorBuffer.first(); break; + case nhHighestColor: scanLine[i] = mColorBuffer.last(); break; + case nhTransparent: scanLine[i] = qRgba(0, 0, 0, 0); break; + case nhNanColor: scanLine[i] = mNanColor.rgba(); break; + case nhNone: break; // shouldn't happen } } } @@ -16118,22 +16821,30 @@ QRgb QCPColorGradient::color(double position, const QCPRange &range, bool logari // If you change something here, make sure to also adapt ::colorize() if (mColorBufferInvalidated) updateColorBuffer(); - int index = 0; - if (!logarithmic) - index = (position-range.lower)*(mLevelCount-1)/range.size(); - else - index = qLn(position/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1); - if (mPeriodic) + + const bool skipNanCheck = mNanHandling == nhNone; + if (!skipNanCheck && std::isnan(position)) { - index = index % mLevelCount; - if (index < 0) - index += mLevelCount; + switch(mNanHandling) + { + case nhLowestColor: return mColorBuffer.first(); + case nhHighestColor: return mColorBuffer.last(); + case nhTransparent: return qRgba(0, 0, 0, 0); + case nhNanColor: return mNanColor.rgba(); + case nhNone: return qRgba(0, 0, 0, 0); // shouldn't happen + } + } + + const double posToIndexFactor = !logarithmic ? (mLevelCount-1)/range.size() : (mLevelCount-1)/qLn(range.upper/range.lower); + int index = int((!logarithmic ? position-range.lower : qLn(position/range.lower)) * posToIndexFactor); + if (!mPeriodic) + { + index = qBound(0, index, mLevelCount-1); } else { + index %= mLevelCount; if (index < 0) - index = 0; - else if (index >= mLevelCount) - index = mLevelCount-1; + index += mLevelCount; } return mColorBuffer.at(index); } @@ -16301,7 +17012,7 @@ void QCPColorGradient::updateColorBuffer() mColorBuffer.resize(mLevelCount); if (mColorStops.size() > 1) { - double indexToPosFactor = 1.0/(double)(mLevelCount-1); + double indexToPosFactor = 1.0/double(mLevelCount-1); const bool useAlpha = stopsUseAlpha(); for (int i=0; i::const_iterator high = it; - QMap::const_iterator low = it-1; + QMap::const_iterator low = std::prev(it); double t = (position-low.key())/(high.key()-low.key()); // interpolation factor 0..1 switch (mColorInterpolation) { @@ -16336,17 +17053,17 @@ void QCPColorGradient::updateColorBuffer() { if (useAlpha) { - const int alpha = (1-t)*low.value().alpha() + t*high.value().alpha(); - const float alphaPremultiplier = alpha/255.0f; // since we use QImage::Format_ARGB32_Premultiplied - mColorBuffer[i] = qRgba(((1-t)*low.value().red() + t*high.value().red())*alphaPremultiplier, - ((1-t)*low.value().green() + t*high.value().green())*alphaPremultiplier, - ((1-t)*low.value().blue() + t*high.value().blue())*alphaPremultiplier, + const int alpha = int((1-t)*low.value().alpha() + t*high.value().alpha()); + const double alphaPremultiplier = alpha/255.0; // since we use QImage::Format_ARGB32_Premultiplied + mColorBuffer[i] = qRgba(int( ((1-t)*low.value().red() + t*high.value().red())*alphaPremultiplier ), + int( ((1-t)*low.value().green() + t*high.value().green())*alphaPremultiplier ), + int( ((1-t)*low.value().blue() + t*high.value().blue())*alphaPremultiplier ), alpha); } else { - mColorBuffer[i] = qRgb(((1-t)*low.value().red() + t*high.value().red()), - ((1-t)*low.value().green() + t*high.value().green()), - ((1-t)*low.value().blue() + t*high.value().blue())); + mColorBuffer[i] = qRgb(int( ((1-t)*low.value().red() + t*high.value().red()) ), + int( ((1-t)*low.value().green() + t*high.value().green()) ), + int( ((1-t)*low.value().blue() + t*high.value().blue())) ); } break; } @@ -16369,8 +17086,8 @@ void QCPColorGradient::updateColorBuffer() const QRgb rgb = QColor::fromHsvF(hue, (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(), (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb(); - const float alpha = (1-t)*lowHsv.alphaF() + t*highHsv.alphaF(); - mColorBuffer[i] = qRgba(qRed(rgb)*alpha, qGreen(rgb)*alpha, qBlue(rgb)*alpha, 255*alpha); + const double alpha = (1-t)*lowHsv.alphaF() + t*highHsv.alphaF(); + mColorBuffer[i] = qRgba(int(qRed(rgb)*alpha), int(qGreen(rgb)*alpha), int(qBlue(rgb)*alpha), int(255*alpha)); } else { @@ -16386,8 +17103,8 @@ void QCPColorGradient::updateColorBuffer() } else if (mColorStops.size() == 1) { const QRgb rgb = mColorStops.constBegin().value().rgb(); - const float alpha = mColorStops.constBegin().value().alphaF(); - mColorBuffer.fill(qRgba(qRed(rgb)*alpha, qGreen(rgb)*alpha, qBlue(rgb)*alpha, 255*alpha)); + const double alpha = mColorStops.constBegin().value().alphaF(); + mColorBuffer.fill(qRgba(int(qRed(rgb)*alpha), int(qGreen(rgb)*alpha), int(qBlue(rgb)*alpha), int(255*alpha))); } else // mColorStops is empty, fill color buffer with black { mColorBuffer.fill(qRgb(0, 0, 0)); @@ -16397,8 +17114,8 @@ void QCPColorGradient::updateColorBuffer() /* end of 'src/colorgradient.cpp' */ -/* including file 'src/selectiondecorator-bracket.cpp', size 12313 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/selectiondecorator-bracket.cpp' */ +/* modified 2021-03-29T02:30:44, size 12308 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPSelectionDecoratorBracket @@ -16540,12 +17257,12 @@ void QCPSelectionDecoratorBracket::drawBracket(QCPPainter *painter, int directio } case bsHalfEllipse: { - painter->drawArc(-mBracketWidth*0.5, -mBracketHeight*0.5, mBracketWidth, mBracketHeight, -90*16, -180*16*direction); + painter->drawArc(QRectF(-mBracketWidth*0.5, -mBracketHeight*0.5, mBracketWidth, mBracketHeight), -90*16, -180*16*direction); break; } case bsEllipse: { - painter->drawEllipse(-mBracketWidth*0.5, -mBracketHeight*0.5, mBracketWidth, mBracketHeight); + painter->drawEllipse(QRectF(-mBracketWidth*0.5, -mBracketHeight*0.5, mBracketWidth, mBracketHeight)); break; } case bsPlus: @@ -16645,7 +17362,7 @@ double QCPSelectionDecoratorBracket::getTangentAngle(const QCPPlottableInterface pointsAverage += points[i]; currentIndex += direction; } - pointsAverage /= (double)averageCount; + pointsAverage /= double(averageCount); // calculate slope of linear regression through points: double numSum = 0; @@ -16673,18 +17390,18 @@ QPointF QCPSelectionDecoratorBracket::getPixelCoordinates(const QCPPlottableInte { QCPAxis *keyAxis = mPlottable->keyAxis(); QCPAxis *valueAxis = mPlottable->valueAxis(); - if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(0, 0); } + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return {0, 0}; } if (keyAxis->orientation() == Qt::Horizontal) - return QPointF(keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex)), valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex))); + return {keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex)), valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex))}; else - return QPointF(valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex)), keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex))); + return {valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex)), keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex))}; } /* end of 'src/selectiondecorator-bracket.cpp' */ -/* including file 'src/layoutelements/layoutelement-axisrect.cpp', size 47584 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/layoutelements/layoutelement-axisrect.cpp' */ +/* modified 2021-03-29T02:30:44, size 47193 */ //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -16863,11 +17580,10 @@ QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) : QCPAxisRect::~QCPAxisRect() { delete mInsetLayout; - mInsetLayout = 0; + mInsetLayout = nullptr; - QList axesList = axes(); - for (int i=0; i QCPAxisRect::axes() const previously created outside QCustomPlot. It is important to note that QCustomPlot takes ownership of the axis, so you may not delete it afterwards. Further, the \a axis must have been created with this axis rect as parent and with the same axis type as specified in \a type. If this is not - the case, a debug output is generated, the axis is not added, and the method returns 0. + the case, a debug output is generated, the axis is not added, and the method returns \c nullptr. This method can not be used to move \a axis between axis rects. The same \a axis instance must not be added multiple times to the same or different axis rects. @@ -16967,20 +17683,20 @@ QCPAxis *QCPAxisRect::addAxis(QCPAxis::AxisType type, QCPAxis *axis) if (newAxis->axisType() != type) { qDebug() << Q_FUNC_INFO << "passed axis has different axis type than specified in type parameter"; - return 0; + return nullptr; } if (newAxis->axisRect() != this) { qDebug() << Q_FUNC_INFO << "passed axis doesn't have this axis rect as parent axis rect"; - return 0; + return nullptr; } if (axes().contains(newAxis)) { qDebug() << Q_FUNC_INFO << "passed axis is already owned by this axis rect"; - return 0; + return nullptr; } } - if (mAxes[type].size() > 0) // multiple axes on one side, add half-bar axis ending to additional axes with offset + if (!mAxes[type].isEmpty()) // multiple axes on one side, add half-bar axis ending to additional axes with offset { bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom); newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert)); @@ -17179,10 +17895,10 @@ QList QCPAxisRect::plottables() const { // Note: don't append all QCPAxis::plottables() into a list, because we might get duplicate entries QList result; - for (int i=0; imPlottables.size(); ++i) + foreach (QCPAbstractPlottable *plottable, mParentPlot->mPlottables) { - if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this || mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this) - result.append(mParentPlot->mPlottables.at(i)); + if (plottable->keyAxis()->axisRect() == this || plottable->valueAxis()->axisRect() == this) + result.append(plottable); } return result; } @@ -17199,10 +17915,10 @@ QList QCPAxisRect::graphs() const { // Note: don't append all QCPAxis::graphs() into a list, because we might get duplicate entries QList result; - for (int i=0; imGraphs.size(); ++i) + foreach (QCPGraph *graph, mParentPlot->mGraphs) { - if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this || mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this) - result.append(mParentPlot->mGraphs.at(i)); + if (graph->keyAxis()->axisRect() == this || graph->valueAxis()->axisRect() == this) + result.append(graph); } return result; } @@ -17222,21 +17938,20 @@ QList QCPAxisRect::items() const // Note: don't just append all QCPAxis::items() into a list, because we might get duplicate entries // and miss those items that have this axis rect as clipAxisRect. QList result; - for (int itemId=0; itemIdmItems.size(); ++itemId) + foreach (QCPAbstractItem *item, mParentPlot->mItems) { - if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this) + if (item->clipAxisRect() == this) { - result.append(mParentPlot->mItems.at(itemId)); + result.append(item); continue; } - QList positions = mParentPlot->mItems.at(itemId)->positions(); - for (int posId=0; posIdpositions()) { - if (positions.at(posId)->axisRect() == this || - positions.at(posId)->keyAxis()->axisRect() == this || - positions.at(posId)->valueAxis()->axisRect() == this) + if (position->axisRect() == this || + position->keyAxis()->axisRect() == this || + position->valueAxis()->axisRect() == this) { - result.append(mParentPlot->mItems.at(itemId)); + result.append(item); break; } } @@ -17262,9 +17977,8 @@ void QCPAxisRect::update(UpdatePhase phase) { case upPreparation: { - QList allAxes = axes(); - for (int i=0; isetupTickVectors(); + foreach (QCPAxis *axis, axes()) + axis->setupTickVectors(); break; } case upLayout: @@ -17392,9 +18106,9 @@ void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode) QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation) { if (orientation == Qt::Horizontal) - return mRangeDragHorzAxis.isEmpty() ? 0 : mRangeDragHorzAxis.first().data(); + return mRangeDragHorzAxis.isEmpty() ? nullptr : mRangeDragHorzAxis.first().data(); else - return mRangeDragVertAxis.isEmpty() ? 0 : mRangeDragVertAxis.first().data(); + return mRangeDragVertAxis.isEmpty() ? nullptr : mRangeDragVertAxis.first().data(); } /*! @@ -17406,9 +18120,9 @@ QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation) QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation) { if (orientation == Qt::Horizontal) - return mRangeZoomHorzAxis.isEmpty() ? 0 : mRangeZoomHorzAxis.first().data(); + return mRangeZoomHorzAxis.isEmpty() ? nullptr : mRangeZoomHorzAxis.first().data(); else - return mRangeZoomVertAxis.isEmpty() ? 0 : mRangeZoomVertAxis.first().data(); + return mRangeZoomVertAxis.isEmpty() ? nullptr : mRangeZoomVertAxis.first().data(); } /*! @@ -17421,17 +18135,17 @@ QList QCPAxisRect::rangeDragAxes(Qt::Orientation orientation) QList result; if (orientation == Qt::Horizontal) { - for (int i=0; i axis, mRangeDragHorzAxis) { - if (!mRangeDragHorzAxis.at(i).isNull()) - result.append(mRangeDragHorzAxis.at(i).data()); + if (!axis.isNull()) + result.append(axis.data()); } } else { - for (int i=0; i axis, mRangeDragVertAxis) { - if (!mRangeDragVertAxis.at(i).isNull()) - result.append(mRangeDragVertAxis.at(i).data()); + if (!axis.isNull()) + result.append(axis.data()); } } return result; @@ -17447,17 +18161,17 @@ QList QCPAxisRect::rangeZoomAxes(Qt::Orientation orientation) QList result; if (orientation == Qt::Horizontal) { - for (int i=0; i axis, mRangeZoomHorzAxis) { - if (!mRangeZoomHorzAxis.at(i).isNull()) - result.append(mRangeZoomHorzAxis.at(i).data()); + if (!axis.isNull()) + result.append(axis.data()); } } else { - for (int i=0; i axis, mRangeZoomVertAxis) { - if (!mRangeZoomVertAxis.at(i).isNull()) - result.append(mRangeZoomVertAxis.at(i).data()); + if (!axis.isNull()) + result.append(axis.data()); } } return result; @@ -17480,9 +18194,9 @@ double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation) default, the horizontal axis is the bottom axis (xAxis) and the vertical axis is the left axis (yAxis). - To disable range dragging entirely, pass 0 as \a orientations or remove \ref QCP::iRangeDrag from \ref - QCustomPlot::setInteractions. To enable range dragging for both directions, pass Qt::Horizontal | - Qt::Vertical as \a orientations. + To disable range dragging entirely, pass \c nullptr as \a orientations or remove \ref + QCP::iRangeDrag from \ref QCustomPlot::setInteractions. To enable range dragging for both + directions, pass Qt::Horizontal | Qt::Vertical as \a orientations. In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions contains \ref QCP::iRangeDrag to enable the range dragging interaction. @@ -17500,9 +18214,9 @@ void QCPAxisRect::setRangeDrag(Qt::Orientations orientations) QCPAxis *vertical). By default, the horizontal axis is the bottom axis (xAxis) and the vertical axis is the left axis (yAxis). - To disable range zooming entirely, pass 0 as \a orientations or remove \ref QCP::iRangeZoom from \ref - QCustomPlot::setInteractions. To enable range zooming for both directions, pass Qt::Horizontal | - Qt::Vertical as \a orientations. + To disable range zooming entirely, pass \c nullptr as \a orientations or remove \ref + QCP::iRangeZoom from \ref QCustomPlot::setInteractions. To enable range zooming for both + directions, pass Qt::Horizontal | Qt::Vertical as \a orientations. In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions contains \ref QCP::iRangeZoom to enable the range zooming interaction. @@ -17517,7 +18231,8 @@ void QCPAxisRect::setRangeZoom(Qt::Orientations orientations) /*! \overload Sets the axes whose range will be dragged when \ref setRangeDrag enables mouse range dragging on - the QCustomPlot widget. Pass 0 if no axis shall be dragged in the respective orientation. + the QCustomPlot widget. Pass \c nullptr if no axis shall be dragged in the respective + orientation. Use the overload taking a list of axes, if multiple axes (more than one per orientation) shall react to dragging interactions. @@ -17586,7 +18301,7 @@ void QCPAxisRect::setRangeDragAxes(QList horizontal, QList v /*! Sets the axes whose range will be zoomed when \ref setRangeZoom enables mouse wheel zooming on - the QCustomPlot widget. Pass 0 if no axis shall be zoomed in the respective orientation. + the QCustomPlot widget. Pass \c nullptr if no axis shall be zoomed in the respective orientation. The two axes can be zoomed with different strengths, when different factors are passed to \ref setRangeZoomFactor(double horizontalFactor, double verticalFactor). @@ -17764,7 +18479,7 @@ int QCPAxisRect::calculateAutoMargin(QCP::MarginSide side) // note: only need to look at the last (outer most) axis to determine the total margin, due to updateAxisOffset call const QList axesList = mAxes.value(QCPAxis::marginSideToAxisType(side)); - if (axesList.size() > 0) + if (!axesList.isEmpty()) return axesList.last()->offset() + axesList.last()->calculateMargin(); else return 0; @@ -17822,11 +18537,11 @@ void QCPAxisRect::mousePressEvent(QMouseEvent *event, const QVariant &details) if (mParentPlot->interactions().testFlag(QCP::iRangeDrag)) { mDragStartHorzRange.clear(); - for (int i=0; irange()); + foreach (QPointer axis, mRangeDragHorzAxis) + mDragStartHorzRange.append(axis.isNull() ? QCPRange() : axis->range()); mDragStartVertRange.clear(); - for (int i=0; irange()); + foreach (QPointer axis, mRangeDragVertAxis) + mDragStartVertRange.append(axis.isNull() ? QCPRange() : axis->range()); } } } @@ -17918,37 +18633,49 @@ void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) dependent on the mouse wheel delta (which direction the wheel was rotated) to provide a natural zooming feel. The Strength of the zoom can be controlled via \ref setRangeZoomFactor. - Note, that event->delta() is usually +/-120 for single rotation steps. However, if the mouse - wheel is turned rapidly, many steps may bunch up to one event, so the event->delta() may then be - multiples of 120. This is taken into account here, by calculating \a wheelSteps and using it as - exponent of the range zoom factor. This takes care of the wheel direction automatically, by - inverting the factor, when the wheel step is negative (f^-1 = 1/f). + Note, that event->angleDelta() is usually +/-120 for single rotation steps. However, if the mouse + wheel is turned rapidly, many steps may bunch up to one event, so the delta may then be multiples + of 120. This is taken into account here, by calculating \a wheelSteps and using it as exponent of + the range zoom factor. This takes care of the wheel direction automatically, by inverting the + factor, when the wheel step is negative (f^-1 = 1/f). */ void QCPAxisRect::wheelEvent(QWheelEvent *event) { +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) + const double delta = event->delta(); +#else + const double delta = event->angleDelta().y(); +#endif + +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + const QPointF pos = event->pos(); +#else + const QPointF pos = event->position(); +#endif + // Mouse range zooming interaction: if (mParentPlot->interactions().testFlag(QCP::iRangeZoom)) { if (mRangeZoom != 0) { double factor; - double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually + double wheelSteps = delta/120.0; // a single step delta is +/-120 usually if (mRangeZoom.testFlag(Qt::Horizontal)) { factor = qPow(mRangeZoomFactorHorz, wheelSteps); - for (int i=0; i axis, mRangeZoomHorzAxis) { - if (!mRangeZoomHorzAxis.at(i).isNull()) - mRangeZoomHorzAxis.at(i)->scaleRange(factor, mRangeZoomHorzAxis.at(i)->pixelToCoord(event->pos().x())); + if (!axis.isNull()) + axis->scaleRange(factor, axis->pixelToCoord(pos.x())); } } if (mRangeZoom.testFlag(Qt::Vertical)) { factor = qPow(mRangeZoomFactorVert, wheelSteps); - for (int i=0; i axis, mRangeZoomVertAxis) { - if (!mRangeZoomVertAxis.at(i).isNull()) - mRangeZoomVertAxis.at(i)->scaleRange(factor, mRangeZoomVertAxis.at(i)->pixelToCoord(event->pos().y())); + if (!axis.isNull()) + axis->scaleRange(factor, axis->pixelToCoord(pos.y())); } } mParentPlot->replot(); @@ -17958,8 +18685,8 @@ void QCPAxisRect::wheelEvent(QWheelEvent *event) /* end of 'src/layoutelements/layoutelement-axisrect.cpp' */ -/* including file 'src/layoutelements/layoutelement-legend.cpp', size 31153 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/layoutelements/layoutelement-legend.cpp' */ +/* modified 2021-03-29T02:30:44, size 31762 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAbstractLegendItem @@ -18225,9 +18952,9 @@ void QCPPlottableLegendItem::draw(QCPPainter *painter) if (!mPlottable) return; painter->setFont(getFont()); painter->setPen(QPen(getTextColor())); - QSizeF iconSize = mParentLegend->iconSize(); - QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name()); - QRectF iconRect(mRect.topLeft(), iconSize); + QSize iconSize = mParentLegend->iconSize(); + QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name()); + QRect iconRect(mRect.topLeft(), iconSize); int textHeight = qMax(textRect.height(), iconSize.height()); // if text has smaller height than icon, center text vertically in icon height, else align tops painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name()); // draw icon: @@ -18255,7 +18982,7 @@ void QCPPlottableLegendItem::draw(QCPPainter *painter) */ QSize QCPPlottableLegendItem::minimumOuterSizeHint() const { - if (!mPlottable) return QSize(); + if (!mPlottable) return {}; QSize result(0, 0); QRect textRect; QFontMetrics fontMetrics(getFont()); @@ -18324,7 +19051,8 @@ QSize QCPPlottableLegendItem::minimumOuterSizeHint() const Note that by default, QCustomPlot already contains a legend ready to be used as \ref QCustomPlot::legend */ -QCPLegend::QCPLegend() +QCPLegend::QCPLegend() : + mIconTextPadding{} { setFillOrder(QCPLayoutGrid::foRowsFirst); setWrap(0); @@ -18600,7 +19328,8 @@ void QCPLegend::setSelectedTextColor(const QColor &color) } /*! - Returns the item with index \a i. + Returns the item with index \a i. If non-legend items were added to the legend, and the element + at the specified cell index is not a QCPAbstractLegendItem, returns \c nullptr. Note that the linear index depends on the current fill order (\ref setFillOrder). @@ -18613,7 +19342,7 @@ QCPAbstractLegendItem *QCPLegend::item(int index) const /*! Returns the QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*). - If such an item isn't in the legend, returns 0. + If such an item isn't in the legend, returns \c nullptr. \see hasItemWithPlottable */ @@ -18627,11 +19356,13 @@ QCPPlottableLegendItem *QCPLegend::itemWithPlottable(const QCPAbstractPlottable return pli; } } - return 0; + return nullptr; } /*! - Returns the number of items currently in the legend. + Returns the number of items currently in the legend. It is identical to the base class + QCPLayoutGrid::elementCount(), and unlike the other "item" interface methods of QCPLegend, + doesn't only address elements which can be cast to QCPAbstractLegendItem. Note that if empty cells are in the legend (e.g. by calling methods of the \ref QCPLayoutGrid base class which allows creating empty cells), they are included in the returned count. @@ -18734,8 +19465,12 @@ bool QCPLegend::removeItem(QCPAbstractLegendItem *item) */ void QCPLegend::clearItems() { - for (int i=itemCount()-1; i>=0; --i) - removeItem(i); + for (int i=elementCount()-1; i>=0; --i) + { + if (item(i)) + removeAt(i); // don't use removeItem() because it would unnecessarily reorder the whole legend for each item + } + setFillOrder(fillOrder(), true); // get rid of empty cells by reordering once after all items are removed } /*! @@ -18874,8 +19609,8 @@ void QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot) /* end of 'src/layoutelements/layoutelement-legend.cpp' */ -/* including file 'src/layoutelements/layoutelement-textelement.cpp', size 12761 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/layoutelements/layoutelement-textelement.cpp' */ +/* modified 2021-03-29T02:30:44, size 12925 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPTextElement @@ -18925,7 +19660,7 @@ void QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot) QCPTextElement::QCPTextElement(QCustomPlot *parentPlot) : QCPLayoutElement(parentPlot), mText(), - mTextFlags(Qt::AlignCenter|Qt::TextWordWrap), + mTextFlags(Qt::AlignCenter), mFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below mTextColor(Qt::black), mSelectedFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below @@ -18950,7 +19685,7 @@ QCPTextElement::QCPTextElement(QCustomPlot *parentPlot) : QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text) : QCPLayoutElement(parentPlot), mText(text), - mTextFlags(Qt::AlignCenter|Qt::TextWordWrap), + mTextFlags(Qt::AlignCenter), mFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below mTextColor(Qt::black), mSelectedFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below @@ -18975,14 +19710,15 @@ QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text) : QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, double pointSize) : QCPLayoutElement(parentPlot), mText(text), - mTextFlags(Qt::AlignCenter|Qt::TextWordWrap), - mFont(QFont(QLatin1String("sans serif"), pointSize)), // will be taken from parentPlot if available, see below + mTextFlags(Qt::AlignCenter), + mFont(QFont(QLatin1String("sans serif"), int(pointSize))), // will be taken from parentPlot if available, see below mTextColor(Qt::black), - mSelectedFont(QFont(QLatin1String("sans serif"), pointSize)), // will be taken from parentPlot if available, see below + mSelectedFont(QFont(QLatin1String("sans serif"), int(pointSize))), // will be taken from parentPlot if available, see below mSelectedTextColor(Qt::blue), mSelectable(false), mSelected(false) { + mFont.setPointSizeF(pointSize); // set here again as floating point, because constructor above only takes integer if (parentPlot) { mFont = parentPlot->font(); @@ -19002,14 +19738,15 @@ QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, dou QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, const QString &fontFamily, double pointSize) : QCPLayoutElement(parentPlot), mText(text), - mTextFlags(Qt::AlignCenter|Qt::TextWordWrap), - mFont(QFont(fontFamily, pointSize)), + mTextFlags(Qt::AlignCenter), + mFont(QFont(fontFamily, int(pointSize))), mTextColor(Qt::black), - mSelectedFont(QFont(fontFamily, pointSize)), + mSelectedFont(QFont(fontFamily, int(pointSize))), mSelectedTextColor(Qt::blue), mSelectable(false), mSelected(false) { + mFont.setPointSizeF(pointSize); // set here again as floating point, because constructor above only takes integer setMargins(QMargins(2, 2, 2, 2)); } @@ -19022,7 +19759,7 @@ QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, con QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, const QFont &font) : QCPLayoutElement(parentPlot), mText(text), - mTextFlags(Qt::AlignCenter|Qt::TextWordWrap), + mTextFlags(Qt::AlignCenter), mFont(font), mTextColor(Qt::black), mSelectedFont(font), @@ -19150,14 +19887,14 @@ void QCPTextElement::draw(QCPPainter *painter) { painter->setFont(mainFont()); painter->setPen(QPen(mainTextColor())); - painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect); + painter->drawText(mRect, mTextFlags, mText, &mTextBoundingRect); } /* inherits documentation from base class */ QSize QCPTextElement::minimumOuterSizeHint() const { QFontMetrics metrics(mFont); - QSize result(metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size()); + QSize result(metrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, mText).size()); result.rwidth() += mMargins.left()+mMargins.right(); result.rheight() += mMargins.top()+mMargins.bottom(); return result; @@ -19167,7 +19904,7 @@ QSize QCPTextElement::minimumOuterSizeHint() const QSize QCPTextElement::maximumOuterSizeHint() const { QFontMetrics metrics(mFont); - QSize result(metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size()); + QSize result(metrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, mText).size()); result.setWidth(QWIDGETSIZE_MAX); result.rheight() += mMargins.top()+mMargins.bottom(); return result; @@ -19278,8 +20015,8 @@ QColor QCPTextElement::mainTextColor() const /* end of 'src/layoutelements/layoutelement-textelement.cpp' */ -/* including file 'src/layoutelements/layoutelement-colorscale.cpp', size 26246 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/layoutelements/layoutelement-colorscale.cpp' */ +/* modified 2021-03-29T02:30:44, size 26531 */ //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -19372,6 +20109,7 @@ QCPColorScale::QCPColorScale(QCustomPlot *parentPlot) : QCPLayoutElement(parentPlot), mType(QCPAxis::atTop), // set to atTop such that setType(QCPAxis::atRight) below doesn't skip work because it thinks it's already atRight mDataScaleType(QCPAxis::stLinear), + mGradient(QCPColorGradient::gpCold), mBarWidth(20), mAxisRect(new QCPColorScaleAxisRectPrivate(this)) { @@ -19446,7 +20184,7 @@ void QCPColorScale::setType(QCPAxis::AxisType type) QString labelTransfer; QSharedPointer tickerTransfer; // transfer/revert some settings on old axis if it exists: - bool doTransfer = (bool)mColorAxis; + bool doTransfer = !mColorAxis.isNull(); if (doTransfer) { rangeTransfer = mColorAxis.data()->range(); @@ -19456,7 +20194,7 @@ void QCPColorScale::setType(QCPAxis::AxisType type) disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange))); disconnect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType))); } - QList allAxisTypes = QList() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop; + const QList allAxisTypes = QList() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop; foreach (QCPAxis::AxisType atype, allAxisTypes) { mAxisRect.data()->axis(atype)->setTicks(atype == mType); @@ -19586,9 +20324,16 @@ void QCPColorScale::setRangeDrag(bool enabled) } if (enabled) + { mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType)); - else - mAxisRect.data()->setRangeDrag(0); + } else + { +#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0) + mAxisRect.data()->setRangeDrag(nullptr); +#else + mAxisRect.data()->setRangeDrag({}); +#endif + } } /*! @@ -19606,9 +20351,16 @@ void QCPColorScale::setRangeZoom(bool enabled) } if (enabled) + { mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType)); - else - mAxisRect.data()->setRangeZoom(0); + } else + { +#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0) + mAxisRect.data()->setRangeDrag(nullptr); +#else + mAxisRect.data()->setRangeZoom({}); +#endif + } } /*! @@ -19640,15 +20392,15 @@ void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps) QCP::SignDomain sign = QCP::sdBoth; if (mDataScaleType == QCPAxis::stLogarithmic) sign = (mDataRange.upper < 0 ? QCP::sdNegative : QCP::sdPositive); - for (int i=0; irealVisibility() && onlyVisibleMaps) + if (!map->realVisibility() && onlyVisibleMaps) continue; QCPRange mapRange; - if (maps.at(i)->colorScale() == this) + if (map->colorScale() == this) { bool currentFoundRange = true; - mapRange = maps.at(i)->data()->dataBounds(); + mapRange = map->data()->dataBounds(); if (sign == QCP::sdPositive) { if (mapRange.lower <= 0 && mapRange.upper > 0) @@ -19802,7 +20554,7 @@ QCPColorScaleAxisRectPrivate::QCPColorScaleAxisRectPrivate(QCPColorScale *parent { setParentLayerable(parentColorScale); setMinimumMargins(QMargins(0, 0, 0, 0)); - QList allAxisTypes = QList() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight; + const QList allAxisTypes = QList() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight; foreach (QCPAxis::AxisType type, allAxisTypes) { axis(type)->setVisible(true); @@ -19878,7 +20630,7 @@ void QCPColorScaleAxisRectPrivate::updateGradientImage() pixels.append(reinterpret_cast(mGradientImage.scanLine(y))); mParentColorScale->mGradient.colorize(data.constData(), QCPRange(0, n-1), pixels.first(), n); for (int y=1; y allAxisTypes = QList() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight; + const QList allAxisTypes = QList() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight; foreach (QCPAxis::AxisType type, allAxisTypes) { if (QCPAxis *senderAxis = qobject_cast(sender())) @@ -19928,7 +20680,7 @@ void QCPColorScaleAxisRectPrivate::axisSelectionChanged(QCPAxis::SelectableParts void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectableParts selectableParts) { // synchronize axis base selectability: - QList allAxisTypes = QList() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight; + const QList allAxisTypes = QList() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight; foreach (QCPAxis::AxisType type, allAxisTypes) { if (QCPAxis *senderAxis = qobject_cast(sender())) @@ -19947,8 +20699,8 @@ void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectablePart /* end of 'src/layoutelements/layoutelement-colorscale.cpp' */ -/* including file 'src/plottables/plottable-graph.cpp', size 74194 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/plottables/plottable-graph.cpp' */ +/* modified 2021-03-29T02:30:44, size 74518 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPGraphData @@ -20106,7 +20858,10 @@ QCPGraphData::QCPGraphData(double key, double value) : To directly create a graph inside a plot, you can also use the simpler QCustomPlot::addGraph function. */ QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) : - QCPAbstractPlottable1D(keyAxis, valueAxis) + QCPAbstractPlottable1D(keyAxis, valueAxis), + mLineStyle{}, + mScatterSkip{}, + mAdaptiveSampling{} { // special handling for QCPGraphs to maintain the simple graph interface: mParentPlot->registerGraph(this); @@ -20116,7 +20871,7 @@ QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) : setLineStyle(lsLine); setScatterSkip(0); - setChannelFillGraph(0); + setChannelFillGraph(nullptr); setAdaptiveSampling(true); } @@ -20214,14 +20969,14 @@ void QCPGraph::setChannelFillGraph(QCPGraph *targetGraph) if (targetGraph == this) { qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself"; - mChannelFillGraph = 0; + mChannelFillGraph = nullptr; return; } // prevent setting channel target to a graph not in the plot: if (targetGraph && targetGraph->mParentPlot != mParentPlot) { qDebug() << Q_FUNC_INFO << "targetGraph not in same plot"; - mChannelFillGraph = 0; + mChannelFillGraph = nullptr; return; } @@ -20322,13 +21077,13 @@ double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *d if (!mKeyAxis || !mValueAxis) return -1; - if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) + if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect)) { QCPGraphDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd(); double result = pointDistance(pos, closestDataPoint); if (details) { - int pointIndex = closestDataPoint-mDataContainer->constBegin(); + int pointIndex = int(closestDataPoint-mDataContainer->constBegin()); details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1))); } return result; @@ -20453,7 +21208,7 @@ void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const /*! \internal - This method retrieves an optimized set of data points via \ref getOptimizedLineData, an branches + This method retrieves an optimized set of data points via \ref getOptimizedLineData, and branches out to the line style specific functions such as \ref dataToLines, \ref dataToStepLeftLines, etc. according to the line style of the graph. @@ -20818,12 +21573,12 @@ void QCPGraph::drawFill(QCPPainter *painter, QVector *lines) const if (painter->brush().style() == Qt::NoBrush || painter->brush().color().alpha() == 0) return; applyFillAntialiasingHint(painter); - QVector segments = getNonNanSegments(lines, keyAxis()->orientation()); + const QVector segments = getNonNanSegments(lines, keyAxis()->orientation()); if (!mChannelFillGraph) { // draw base fill under graph, fill goes all the way to the zero-value-line: - for (int i=0; idrawPolygon(getFillPolygon(lines, segments.at(i))); + foreach (QCPDataRange segment, segments) + painter->drawPolygon(getFillPolygon(lines, segment)); } else { // draw fill between this graph and mChannelFillGraph: @@ -20850,8 +21605,8 @@ void QCPGraph::drawScatterPlot(QCPPainter *painter, const QVector &scat { applyScattersAntialiasingHint(painter); style.applyTo(painter, mPen); - for (int i=0; i *lineData, const QCPGr if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } if (begin == end) return; - int dataCount = end-begin; + int dataCount = int(end-begin); int maxCount = (std::numeric_limits::max)(); if (mAdaptiveSampling) { double keyPixelSpan = qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key)); if (2*keyPixelSpan+2 < static_cast((std::numeric_limits::max)())) - maxCount = 2*keyPixelSpan+2; + maxCount = int(2*keyPixelSpan+2); } if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average @@ -20928,7 +21683,7 @@ void QCPGraph::getOptimizedLineData(QVector *lineData, const QCPGr QCPGraphDataContainer::const_iterator currentIntervalFirstPoint = it; int reversedFactor = keyAxis->pixelOrientation(); // is used to calculate keyEpsilon pixel into the correct direction int reversedRound = reversedFactor==-1 ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey - double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(begin->key)+reversedRound)); + double currentIntervalStartKey = keyAxis->pixelToCoord(int(keyAxis->coordToPixel(begin->key)+reversedRound)); double lastIntervalEndKey = currentIntervalStartKey; double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes) @@ -20959,7 +21714,7 @@ void QCPGraph::getOptimizedLineData(QVector *lineData, const QCPGr minValue = it->value; maxValue = it->value; currentIntervalFirstPoint = it; - currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it->key)+reversedRound)); + currentIntervalStartKey = keyAxis->pixelToCoord(int(keyAxis->coordToPixel(it->key)+reversedRound)); if (keyEpsilonVariable) keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); intervalDataCount = 1; @@ -21004,19 +21759,19 @@ void QCPGraph::getOptimizedScatterData(QVector *scatterData, QCPGr const int scatterModulo = mScatterSkip+1; const bool doScatterSkip = mScatterSkip > 0; - int beginIndex = begin-mDataContainer->constBegin(); - int endIndex = end-mDataContainer->constBegin(); + int beginIndex = int(begin-mDataContainer->constBegin()); + int endIndex = int(end-mDataContainer->constBegin()); while (doScatterSkip && begin != end && beginIndex % scatterModulo != 0) // advance begin iterator to first non-skipped scatter { ++beginIndex; ++begin; } if (begin == end) return; - int dataCount = end-begin; + int dataCount = int(end-begin); int maxCount = (std::numeric_limits::max)(); if (mAdaptiveSampling) { - int keyPixelSpan = qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key)); + int keyPixelSpan = int(qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key))); maxCount = 2*keyPixelSpan+2; } @@ -21025,7 +21780,7 @@ void QCPGraph::getOptimizedScatterData(QVector *scatterData, QCPGr double valueMaxRange = valueAxis->range().upper; double valueMinRange = valueAxis->range().lower; QCPGraphDataContainer::const_iterator it = begin; - int itIndex = beginIndex; + int itIndex = int(beginIndex); double minValue = it->value; double maxValue = it->value; QCPGraphDataContainer::const_iterator minValueIt = it; @@ -21033,7 +21788,7 @@ void QCPGraph::getOptimizedScatterData(QVector *scatterData, QCPGr QCPGraphDataContainer::const_iterator currentIntervalStart = it; int reversedFactor = keyAxis->pixelOrientation(); // is used to calculate keyEpsilon pixel into the correct direction int reversedRound = reversedFactor==-1 ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey - double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(begin->key)+reversedRound)); + double currentIntervalStartKey = keyAxis->pixelToCoord(int(keyAxis->coordToPixel(begin->key)+reversedRound)); double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes) int intervalDataCount = 1; @@ -21090,7 +21845,7 @@ void QCPGraph::getOptimizedScatterData(QVector *scatterData, QCPGr minValue = it->value; maxValue = it->value; currentIntervalStart = it; - currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it->key)+reversedRound)); + currentIntervalStartKey = keyAxis->pixelToCoord(int(keyAxis->coordToPixel(it->key)+reversedRound)); if (keyEpsilonVariable) keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); intervalDataCount = 1; @@ -21117,7 +21872,7 @@ void QCPGraph::getOptimizedScatterData(QVector *scatterData, QCPGr double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue)); int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average QCPGraphDataContainer::const_iterator intervalIt = currentIntervalStart; - int intervalItIndex = intervalIt-mDataContainer->constBegin(); + int intervalItIndex = int(intervalIt-mDataContainer->constBegin()); int c = 0; while (intervalIt != it) { @@ -21364,7 +22119,7 @@ QPointF QCPGraph::getFillBasePoint(QPointF matchingDataPoint) const { QCPAxis *keyAxis = mKeyAxis.data(); QCPAxis *valueAxis = mValueAxis.data(); - if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); } + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return {}; } QPointF result; if (valueAxis->scaleType() == QCPAxis::stLinear) @@ -21663,7 +22418,7 @@ double QCPGraph::pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer: { // line displayed, calculate distance to line segments: QVector lineData; - getLines(&lineData, QCPDataRange(0, dataCount())); + getLines(&lineData, QCPDataRange(0, dataCount())); // don't limit data range further since with sharp data spikes, line segments may be closer to test point than segments with closer key coordinate QCPVector2D p(pixelPoint); const int step = mLineStyle==lsImpulse ? 2 : 1; // impulse plot differs from other line styles in that the lineData points are only pairwise connected for (int i=0; i *data, double y) const /* end of 'src/plottables/plottable-graph.cpp' */ -/* including file 'src/plottables/plottable-curve.cpp', size 63742 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/plottables/plottable-curve.cpp' */ +/* modified 2021-03-29T02:30:44, size 63851 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPCurveData @@ -21861,7 +22616,9 @@ QCPCurveData::QCPCurveData(double t, double key, double value) : but use QCustomPlot::removePlottable() instead. */ QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) : - QCPAbstractPlottable1D(keyAxis, valueAxis) + QCPAbstractPlottable1D(keyAxis, valueAxis), + mScatterSkip{}, + mLineStyle{} { // modify inherited properties from abstract plottable: setPen(QPen(Qt::blue, 0)); @@ -22084,13 +22841,13 @@ double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *d if (!mKeyAxis || !mValueAxis) return -1; - if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) + if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect)) { QCPCurveDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd(); double result = pointDistance(pos, closestDataPoint); if (details) { - int pointIndex = closestDataPoint-mDataContainer->constBegin(); + int pointIndex = int( closestDataPoint-mDataContainer->constBegin() ); details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1))); } return result; @@ -22240,9 +22997,9 @@ void QCPCurve::drawScatterPlot(QCPPainter *painter, const QVector &poin // draw scatter point symbols: applyScattersAntialiasingHint(painter); style.applyTo(painter, mPen); - for (int i=0; i *scatters, const QCPDataRange &dataR return; const int scatterModulo = mScatterSkip+1; const bool doScatterSkip = mScatterSkip > 0; - int endIndex = end-mDataContainer->constBegin(); + int endIndex = int( end-mDataContainer->constBegin() ); QCPRange keyRange = keyAxis->range(); QCPRange valueRange = valueAxis->range(); @@ -22402,7 +23159,7 @@ void QCPCurve::getScatters(QVector *scatters, const QCPDataRange &dataR valueRange.upper = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueRange.upper)+scatterWidth*valueAxis->pixelOrientation()); QCPCurveDataContainer::const_iterator it = begin; - int itIndex = begin-mDataContainer->constBegin(); + int itIndex = int( begin-mDataContainer->constBegin() ); while (doScatterSkip && it != end && itIndex % scatterModulo != 0) // advance begin iterator to first non-skipped scatter { ++itIndex; @@ -22611,9 +23368,9 @@ QPointF QCPCurve::getOptimizedPoint(int otherRegion, double otherKey, double oth } } if (mKeyAxis->orientation() == Qt::Horizontal) - return QPointF(intersectKeyPx, intersectValuePx); + return {intersectKeyPx, intersectValuePx}; else - return QPointF(intersectValuePx, intersectKeyPx); + return {intersectValuePx, intersectKeyPx}; } /*! \internal @@ -22917,12 +23674,12 @@ bool QCPCurve::getTraverse(double prevKey, double prevValue, double key, double const double valuePx = mValueAxis->coordToPixel(value); const double prevKeyPx = mKeyAxis->coordToPixel(prevKey); const double prevValuePx = mValueAxis->coordToPixel(prevValue); - if (qFuzzyIsNull(key-prevKey)) // line is parallel to value axis + if (qFuzzyIsNull(keyPx-prevKeyPx)) // line is parallel to value axis { // due to region filter in mayTraverse(), if line is parallel to value or key axis, region 5 is traversed here intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyPx, valueMinPx) : QPointF(valueMinPx, keyPx)); // direction will be taken care of at end of method intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyPx, valueMaxPx) : QPointF(valueMaxPx, keyPx)); - } else if (qFuzzyIsNull(value-prevValue)) // line is parallel to key axis + } else if (qFuzzyIsNull(valuePx-prevValuePx)) // line is parallel to key axis { // due to region filter in mayTraverse(), if line is parallel to value or key axis, region 5 is traversed here intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMinPx, valuePx) : QPointF(valuePx, keyMinPx)); // direction will be taken care of at end of method @@ -23158,8 +23915,8 @@ double QCPCurve::pointDistance(const QPointF &pixelPoint, QCPCurveDataContainer: /* end of 'src/plottables/plottable-curve.cpp' */ -/* including file 'src/plottables/plottable-bars.cpp', size 43725 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/plottables/plottable-bars.cpp' */ +/* modified 2021-03-29T02:30:44, size 43907 */ //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -23271,7 +24028,7 @@ void QCPBarsGroup::setSpacing(double spacing) /*! Returns the QCPBars instance with the specified \a index in this group. If no such QCPBars - exists, returns 0. + exists, returns \c nullptr. \see bars(), size */ @@ -23283,7 +24040,7 @@ QCPBars *QCPBarsGroup::bars(int index) const } else { qDebug() << Q_FUNC_INFO << "index out of bounds:" << index; - return 0; + return nullptr; } } @@ -23294,8 +24051,9 @@ QCPBars *QCPBarsGroup::bars(int index) const */ void QCPBarsGroup::clear() { - foreach (QCPBars *bars, mBars) // since foreach takes a copy, removing bars in the loop is okay - bars->setBarsGroup(0); // removes itself via removeBars + const QList oldBars = mBars; + foreach (QCPBars *bars, oldBars) + bars->setBarsGroup(nullptr); // removes itself from mBars via removeBars } /*! @@ -23356,7 +24114,7 @@ void QCPBarsGroup::remove(QCPBars *bars) } if (mBars.contains(bars)) - bars->setBarsGroup(0); + bars->setBarsGroup(nullptr); else qDebug() << Q_FUNC_INFO << "bars plottable is not in this bars group:" << reinterpret_cast(bars); } @@ -23623,14 +24381,14 @@ QCPBarsData::QCPBarsData(double key, double value) : /*! \fn QCPBars *QCPBars::barBelow() const Returns the bars plottable that is directly below this bars plottable. - If there is no such plottable, returns 0. + If there is no such plottable, returns \c nullptr. \see barAbove, moveBelow, moveAbove */ /*! \fn QCPBars *QCPBars::barAbove() const Returns the bars plottable that is directly above this bars plottable. - If there is no such plottable, returns 0. + If there is no such plottable, returns \c nullptr. \see barBelow, moveBelow, moveAbove */ @@ -23651,9 +24409,9 @@ QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) : QCPAbstractPlottable1D(keyAxis, valueAxis), mWidth(0.75), mWidthType(wtPlotCoords), - mBarsGroup(0), + mBarsGroup(nullptr), mBaseValue(0), - mStackingGap(0) + mStackingGap(1) { // modify inherited properties from abstract plottable: mPen.setColor(Qt::blue); @@ -23665,7 +24423,7 @@ QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) : QCPBars::~QCPBars() { - setBarsGroup(0); + setBarsGroup(nullptr); if (mBarBelow || mBarAbove) connectBars(mBarBelow.data(), mBarAbove.data()); // take this bar out of any stacking } @@ -23735,7 +24493,7 @@ void QCPBars::setWidthType(QCPBars::WidthType widthType) Sets to which QCPBarsGroup this QCPBars instance belongs to. Alternatively, you can also use \ref QCPBarsGroup::append. - To remove this QCPBars from any group, set \a barsGroup to 0. + To remove this QCPBars from any group, set \a barsGroup to \c nullptr. */ void QCPBars::setBarsGroup(QCPBarsGroup *barsGroup) { @@ -23827,7 +24585,7 @@ void QCPBars::addData(double key, double value) is already between two other bars, the two other bars will be stacked on top of each other after the operation. - To remove this bars plottable from any stacking, set \a bars to 0. + To remove this bars plottable from any stacking, set \a bars to \c nullptr. \see moveBelow, barAbove, barBelow */ @@ -23860,7 +24618,7 @@ void QCPBars::moveBelow(QCPBars *bars) is already between two other bars, the two other bars will be stacked on top of each other after the operation. - To remove this bars plottable from any stacking, set \a bars to 0. + To remove this bars plottable from any stacking, set \a bars to \c nullptr. \see moveBelow, barBelow, barAbove */ @@ -23900,7 +24658,7 @@ QCPDataSelection QCPBars::selectTestRect(const QRectF &rect, bool onlySelectable for (QCPBarsDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it) { if (rect.intersects(getBarRect(it->key, it->value))) - result.addDataRange(QCPDataRange(it-mDataContainer->constBegin(), it-mDataContainer->constBegin()+1), false); + result.addDataRange(QCPDataRange(int(it-mDataContainer->constBegin()), int(it-mDataContainer->constBegin()+1)), false); } result.simplify(); return result; @@ -23922,7 +24680,7 @@ double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *de if (!mKeyAxis || !mValueAxis) return -1; - if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) + if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect)) { // get visible data range: QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd; @@ -23933,7 +24691,7 @@ double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *de { if (details) { - int pointIndex = it-mDataContainer->constBegin(); + int pointIndex = int(it-mDataContainer->constBegin()); details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1))); } return mParentPlot->selectionTolerance()*0.99; @@ -23996,8 +24754,8 @@ QCPRange QCPBars::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, QCPBarsDataContainer::const_iterator itEnd = mDataContainer->constEnd(); if (inKeyRange != QCPRange()) { - itBegin = mDataContainer->findBegin(inKeyRange.lower); - itEnd = mDataContainer->findEnd(inKeyRange.upper); + itBegin = mDataContainer->findBegin(inKeyRange.lower, false); + itEnd = mDataContainer->findEnd(inKeyRange.upper, false); } for (QCPBarsDataContainer::const_iterator it = itBegin; it != itEnd; ++it) { @@ -24029,19 +24787,19 @@ QPointF QCPBars::dataPixelPosition(int index) const { QCPAxis *keyAxis = mKeyAxis.data(); QCPAxis *valueAxis = mValueAxis.data(); - if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); } + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return {}; } const QCPDataContainer::const_iterator it = mDataContainer->constBegin()+index; const double valuePixel = valueAxis->coordToPixel(getStackedBaseValue(it->key, it->value >= 0) + it->value); const double keyPixel = keyAxis->coordToPixel(it->key) + (mBarsGroup ? mBarsGroup->keyPixelOffset(this, it->key) : 0); if (keyAxis->orientation() == Qt::Horizontal) - return QPointF(keyPixel, valuePixel); + return {keyPixel, valuePixel}; else - return QPointF(valuePixel, keyPixel); + return {valuePixel, keyPixel}; } else { qDebug() << Q_FUNC_INFO << "Index out of bounds" << index; - return QPointF(); + return {}; } } @@ -24184,7 +24942,7 @@ QRectF QCPBars::getBarRect(double key, double value) const { QCPAxis *keyAxis = mKeyAxis.data(); QCPAxis *valueAxis = mValueAxis.data(); - if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QRectF(); } + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return {}; } double lowerPixelWidth, upperPixelWidth; getPixelWidth(key, lowerPixelWidth, upperPixelWidth); @@ -24310,22 +25068,22 @@ void QCPBars::connectBars(QCPBars *lower, QCPBars *upper) { // disconnect old bar below upper: if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper) - upper->mBarBelow.data()->mBarAbove = 0; - upper->mBarBelow = 0; + upper->mBarBelow.data()->mBarAbove = nullptr; + upper->mBarBelow = nullptr; } else if (!upper) // disconnect lower at top { // disconnect old bar above lower: if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower) - lower->mBarAbove.data()->mBarBelow = 0; - lower->mBarAbove = 0; + lower->mBarAbove.data()->mBarBelow = nullptr; + lower->mBarAbove = nullptr; } else // connect lower and upper { // disconnect old bar above lower: if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower) - lower->mBarAbove.data()->mBarBelow = 0; + lower->mBarAbove.data()->mBarBelow = nullptr; // disconnect old bar below upper: if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper) - upper->mBarBelow.data()->mBarAbove = 0; + upper->mBarBelow.data()->mBarAbove = nullptr; lower->mBarAbove = upper; upper->mBarBelow = lower; } @@ -24333,8 +25091,8 @@ void QCPBars::connectBars(QCPBars *lower, QCPBars *upper) /* end of 'src/plottables/plottable-bars.cpp' */ -/* including file 'src/plottables/plottable-statisticalbox.cpp', size 28837 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/plottables/plottable-statisticalbox.cpp' */ +/* modified 2021-03-29T02:30:44, size 28951 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPStatisticalBoxData @@ -24732,7 +25490,7 @@ QCPDataSelection QCPStatisticalBox::selectTestRect(const QRectF &rect, bool only for (QCPStatisticalBoxDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it) { if (rect.intersects(getQuartileBox(it))) - result.addDataRange(QCPDataRange(it-mDataContainer->constBegin(), it-mDataContainer->constBegin()+1), false); + result.addDataRange(QCPDataRange(int(it-mDataContainer->constBegin()), int(it-mDataContainer->constBegin()+1)), false); } result.simplify(); return result; @@ -24754,7 +25512,7 @@ double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QV if (!mKeyAxis || !mValueAxis) return -1; - if (mKeyAxis->axisRect()->rect().contains(pos.toPoint())) + if (mKeyAxis->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect)) { // get visible data range: QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd; @@ -24773,10 +25531,11 @@ double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QV } } else // whiskers { - const QVector whiskerBackbones(getWhiskerBackboneLines(it)); - for (int i=0; i whiskerBackbones = getWhiskerBackboneLines(it); + const QCPVector2D posVec(pos); + foreach (const QLineF &backbone, whiskerBackbones) { - double currentDistSqr = QCPVector2D(pos).distanceSquaredToLine(whiskerBackbones.at(i)); + double currentDistSqr = posVec.distanceSquaredToLine(backbone); if (currentDistSqr < minDistSqr) { minDistSqr = currentDistSqr; @@ -24787,7 +25546,7 @@ double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QV } if (details) { - int pointIndex = closestDataPoint-mDataContainer->constBegin(); + int pointIndex = int(closestDataPoint-mDataContainer->constBegin()); details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1))); } return qSqrt(minDistSqr); @@ -24994,8 +25753,8 @@ QVector QCPStatisticalBox::getWhiskerBarLines(QCPStatisticalBoxDataConta /* end of 'src/plottables/plottable-statisticalbox.cpp' */ -/* including file 'src/plottables/plottable-colormap.cpp', size 47881 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/plottables/plottable-colormap.cpp' */ +/* modified 2021-03-29T02:30:44, size 48149 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPColorMapData @@ -25057,8 +25816,8 @@ QCPColorMapData::QCPColorMapData(int keySize, int valueSize, const QCPRange &key mKeyRange(keyRange), mValueRange(valueRange), mIsEmpty(true), - mData(0), - mAlpha(0), + mData(nullptr), + mAlpha(nullptr), mDataModified(true) { setSize(keySize, valueSize); @@ -25067,10 +25826,8 @@ QCPColorMapData::QCPColorMapData(int keySize, int valueSize, const QCPRange &key QCPColorMapData::~QCPColorMapData() { - if (mData) - delete[] mData; - if (mAlpha) - delete[] mAlpha; + delete[] mData; + delete[] mAlpha; } /*! @@ -25080,8 +25837,8 @@ QCPColorMapData::QCPColorMapData(const QCPColorMapData &other) : mKeySize(0), mValueSize(0), mIsEmpty(true), - mData(0), - mAlpha(0), + mData(nullptr), + mAlpha(nullptr), mDataModified(true) { *this = other; @@ -25105,9 +25862,9 @@ QCPColorMapData &QCPColorMapData::operator=(const QCPColorMapData &other) setRange(other.keyRange(), other.valueRange()); if (!isEmpty()) { - memcpy(mData, other.mData, sizeof(mData[0])*keySize*valueSize); + memcpy(mData, other.mData, sizeof(mData[0])*size_t(keySize*valueSize)); if (mAlpha) - memcpy(mAlpha, other.mAlpha, sizeof(mAlpha[0])*keySize*valueSize); + memcpy(mAlpha, other.mAlpha, sizeof(mAlpha[0])*size_t(keySize*valueSize)); } mDataBounds = other.mDataBounds; mDataModified = true; @@ -25118,8 +25875,8 @@ QCPColorMapData &QCPColorMapData::operator=(const QCPColorMapData &other) /* undocumented getter */ double QCPColorMapData::data(double key, double value) { - int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5; - int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5; + int keyCell = int( (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5 ); + int valueCell = int( (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5 ); if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize) return mData[valueCell*mKeySize + keyCell]; else @@ -25169,24 +25926,23 @@ void QCPColorMapData::setSize(int keySize, int valueSize) { mKeySize = keySize; mValueSize = valueSize; - if (mData) - delete[] mData; + delete[] mData; mIsEmpty = mKeySize == 0 || mValueSize == 0; if (!mIsEmpty) { #ifdef __EXCEPTIONS try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message #endif - mData = new double[mKeySize*mValueSize]; + mData = new double[size_t(mKeySize*mValueSize)]; #ifdef __EXCEPTIONS - } catch (...) { mData = 0; } + } catch (...) { mData = nullptr; } #endif if (mData) fill(0); else qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize; } else - mData = 0; + mData = nullptr; if (mAlpha) // if we had an alpha map, recreate it with new size createAlpha(); @@ -25285,8 +26041,8 @@ void QCPColorMapData::setValueRange(const QCPRange &valueRange) */ void QCPColorMapData::setData(double key, double value, double z) { - int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5; - int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5; + int keyCell = int( (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5 ); + int valueCell = int( (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5 ); if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize) { mData[valueCell*mKeySize + keyCell] = z; @@ -25401,7 +26157,7 @@ void QCPColorMapData::clearAlpha() if (mAlpha) { delete[] mAlpha; - mAlpha = 0; + mAlpha = nullptr; mDataModified = true; } } @@ -25445,8 +26201,8 @@ void QCPColorMapData::fillAlpha(unsigned char alpha) The retrieved key/value cell indices can then be used for example with \ref setCell. - If you are only interested in a key or value index, you may pass 0 as \a valueIndex or \a - keyIndex. + If you are only interested in a key or value index, you may pass \c nullptr as \a valueIndex or + \a keyIndex. \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes, @@ -25458,9 +26214,9 @@ void QCPColorMapData::fillAlpha(unsigned char alpha) void QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int *valueIndex) const { if (keyIndex) - *keyIndex = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5; + *keyIndex = int( (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5 ); if (valueIndex) - *valueIndex = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5; + *valueIndex = int( (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5 ); } /*! @@ -25468,7 +26224,7 @@ void QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int * instance. The resulting coordinates are returned via the output parameters \a key and \a value. - If you are only interested in a key or value coordinate, you may pass 0 as \a key or \a + If you are only interested in a key or value coordinate, you may pass \c nullptr as \a key or \a value. \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or @@ -25481,9 +26237,9 @@ void QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int * void QCPColorMapData::cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const { if (key) - *key = keyIndex/(double)(mKeySize-1)*(mKeyRange.upper-mKeyRange.lower)+mKeyRange.lower; + *key = keyIndex/double(mKeySize-1)*(mKeyRange.upper-mKeyRange.lower)+mKeyRange.lower; if (value) - *value = valueIndex/(double)(mValueSize-1)*(mValueRange.upper-mValueRange.lower)+mValueRange.lower; + *value = valueIndex/double(mValueSize-1)*(mValueRange.upper-mValueRange.lower)+mValueRange.lower; } /*! \internal @@ -25508,9 +26264,9 @@ bool QCPColorMapData::createAlpha(bool initializeOpaque) #ifdef __EXCEPTIONS try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message #endif - mAlpha = new unsigned char[mKeySize*mValueSize]; + mAlpha = new unsigned char[size_t(mKeySize*mValueSize)]; #ifdef __EXCEPTIONS - } catch (...) { mAlpha = 0; } + } catch (...) { mAlpha = nullptr; } #endif if (mAlpha) { @@ -25561,13 +26317,14 @@ bool QCPColorMapData::createAlpha(bool initializeOpaque) \section qcpcolormap-appearance Changing the appearance - The central part of the appearance is the color gradient, which can be specified via \ref + Most important to the appearance is the color gradient, which can be specified via \ref setGradient. See the documentation of \ref QCPColorGradient for details on configuring a color gradient. The \a data range that is mapped to the colors of the gradient can be specified with \ref setDataRange. To make the data range encompass the whole data set minimum to maximum, call \ref - rescaleDataRange. + rescaleDataRange. If your data may contain NaN values, use \ref QCPColorGradient::setNanHandling + to define how they are displayed. \section qcpcolormap-transparency Transparency @@ -25789,7 +26546,7 @@ void QCPColorMap::setTightBoundary(bool enabled) type of \a colorScale. After this call, you may change these properties at either the color map or the color scale, and the setting will be applied to both. - Pass 0 as \a colorScale to disconnect the color scale from this color map again. + Pass \c nullptr as \a colorScale to disconnect the color scale from this color map again. */ void QCPColorMap::setColorScale(QCPColorScale *colorScale) { @@ -25880,7 +26637,7 @@ double QCPColorMap::selectTest(const QPointF &pos, bool onlySelectable, QVariant if (!mKeyAxis || !mValueAxis) return -1; - if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) + if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect)) { double posKey, posValue; pixelsToCoords(pos, posKey, posValue); @@ -25924,7 +26681,7 @@ QCPRange QCPColorMap::getValueRange(bool &foundRange, QCP::SignDomain inSignDoma if (mMapData->keyRange().upper < inKeyRange.lower || mMapData->keyRange().lower > inKeyRange.upper) { foundRange = false; - return QCPRange(); + return {}; } } @@ -25970,8 +26727,8 @@ void QCPColorMap::updateMapImage() const QImage::Format format = QImage::Format_ARGB32_Premultiplied; const int keySize = mMapData->keySize(); const int valueSize = mMapData->valueSize(); - int keyOversamplingFactor = mInterpolate ? 1 : (int)(1.0+100.0/(double)keySize); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on - int valueOversamplingFactor = mInterpolate ? 1 : (int)(1.0+100.0/(double)valueSize); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on + int keyOversamplingFactor = mInterpolate ? 1 : int(1.0+100.0/double(keySize)); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on + int valueOversamplingFactor = mInterpolate ? 1 : int(1.0+100.0/double(valueSize)); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on // resize mMapImage to correct dimensions including possible oversampling factors, according to key/value axes orientation: if (keyAxis->orientation() == Qt::Horizontal && (mMapImage.width() != keySize*keyOversamplingFactor || mMapImage.height() != valueSize*valueOversamplingFactor)) @@ -26072,15 +26829,15 @@ void QCPColorMap::draw(QCPPainter *painter) if (keyAxis()->orientation() == Qt::Horizontal) { if (mMapData->keySize() > 1) - halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->keySize()-1); + halfCellWidth = 0.5*imageRect.width()/double(mMapData->keySize()-1); if (mMapData->valueSize() > 1) - halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->valueSize()-1); + halfCellHeight = 0.5*imageRect.height()/double(mMapData->valueSize()-1); } else // keyAxis orientation is Qt::Vertical { if (mMapData->keySize() > 1) - halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->keySize()-1); + halfCellHeight = 0.5*imageRect.height()/double(mMapData->keySize()-1); if (mMapData->valueSize() > 1) - halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->valueSize()-1); + halfCellWidth = 0.5*imageRect.width()/double(mMapData->valueSize()-1); } imageRect.adjust(-halfCellWidth, -halfCellHeight, halfCellWidth, halfCellHeight); const bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed(); @@ -26129,8 +26886,8 @@ void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const /* end of 'src/plottables/plottable-colormap.cpp' */ -/* including file 'src/plottables/plottable-financial.cpp', size 42827 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/plottables/plottable-financial.cpp' */ +/* modified 2021-03-29T02:30:44, size 42914 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPFinancialData @@ -26529,7 +27286,7 @@ QCPDataSelection QCPFinancial::selectTestRect(const QRectF &rect, bool onlySelec for (QCPFinancialDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it) { if (rect.intersects(selectionHitBox(it))) - result.addDataRange(QCPDataRange(it-mDataContainer->constBegin(), it-mDataContainer->constBegin()+1), false); + result.addDataRange(QCPDataRange(int(it-mDataContainer->constBegin()), int(it-mDataContainer->constBegin()+1)), false); } result.simplify(); return result; @@ -26551,7 +27308,7 @@ double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVarian if (!mKeyAxis || !mValueAxis) return -1; - if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) + if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect)) { // get visible data range: QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd; @@ -26568,7 +27325,7 @@ double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVarian } if (details) { - int pointIndex = closestDataPoint-mDataContainer->constBegin(); + int pointIndex = int(closestDataPoint-mDataContainer->constBegin()); details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1))); } return result; @@ -27077,7 +27834,7 @@ QRectF QCPFinancial::selectionHitBox(QCPFinancialDataContainer::const_iterator i { QCPAxis *keyAxis = mKeyAxis.data(); QCPAxis *valueAxis = mValueAxis.data(); - if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QRectF(); } + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return {}; } double keyPixel = keyAxis->coordToPixel(it->key); double highPixel = valueAxis->coordToPixel(it->high); @@ -27091,8 +27848,8 @@ QRectF QCPFinancial::selectionHitBox(QCPFinancialDataContainer::const_iterator i /* end of 'src/plottables/plottable-financial.cpp' */ -/* including file 'src/plottables/plottable-errorbar.cpp', size 37570 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/plottables/plottable-errorbar.cpp' */ +/* modified 2021-03-29T02:30:44, size 37679 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPErrorBarsData @@ -27286,13 +28043,13 @@ void QCPErrorBars::setDataPlottable(QCPAbstractPlottable *plottable) { if (plottable && qobject_cast(plottable)) { - mDataPlottable = 0; + mDataPlottable = nullptr; qDebug() << Q_FUNC_INFO << "can't set another QCPErrorBars instance as data plottable"; return; } if (plottable && !plottable->interface1D()) { - mDataPlottable = 0; + mDataPlottable = nullptr; qDebug() << Q_FUNC_INFO << "passed plottable doesn't implement 1d interface, can't associate with QCPErrorBars"; return; } @@ -27434,13 +28191,13 @@ QCPRange QCPErrorBars::dataValueRange(int index) const { const double value = mDataPlottable->interface1D()->dataMainValue(index); if (index >= 0 && index < mDataContainer->size() && mErrorType == etValueError) - return QCPRange(value-mDataContainer->at(index).errorMinus, value+mDataContainer->at(index).errorPlus); + return {value-mDataContainer->at(index).errorMinus, value+mDataContainer->at(index).errorPlus}; else - return QCPRange(value, value); + return {value, value}; } else { qDebug() << Q_FUNC_INFO << "no data plottable set"; - return QCPRange(); + return {}; } } @@ -27451,7 +28208,7 @@ QPointF QCPErrorBars::dataPixelPosition(int index) const return mDataPlottable->interface1D()->dataPixelPosition(index); else qDebug() << Q_FUNC_INFO << "no data plottable set"; - return QPointF(); + return {}; } /* inherits documentation from base class */ @@ -27489,11 +28246,11 @@ QCPDataSelection QCPErrorBars::selectTestRect(const QRectF &rect, bool onlySelec backbones.clear(); whiskers.clear(); getErrorBarLines(it, backbones, whiskers); - for (int i=0; iconstBegin(), it-mDataContainer->constBegin()+1), false); + result.addDataRange(QCPDataRange(int(it-mDataContainer->constBegin()), int(it-mDataContainer->constBegin()+1)), false); break; } } @@ -27551,13 +28308,13 @@ double QCPErrorBars::selectTest(const QPointF &pos, bool onlySelectable, QVarian if (!mKeyAxis || !mValueAxis) return -1; - if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) + if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect)) { QCPErrorBarsDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd(); double result = pointDistance(pos, closestDataPoint); if (details) { - int pointIndex = closestDataPoint-mDataContainer->constBegin(); + int pointIndex = int(closestDataPoint-mDataContainer->constBegin()); details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1))); } return result; @@ -27615,7 +28372,7 @@ void QCPErrorBars::draw(QCPPainter *painter) whiskers.clear(); for (QCPErrorBarsDataContainer::const_iterator it=begin; it!=end; ++it) { - if (!checkPointVisibility || errorBarVisible(it-mDataContainer->constBegin())) + if (!checkPointVisibility || errorBarVisible(int(it-mDataContainer->constBegin()))) getErrorBarLines(it, backbones, whiskers); } painter->drawLines(backbones); @@ -27651,7 +28408,7 @@ QCPRange QCPErrorBars::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomai if (!mDataPlottable) { foundRange = false; - return QCPRange(); + return {}; } QCPRange range; @@ -27663,7 +28420,7 @@ QCPRange QCPErrorBars::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomai if (mErrorType == etValueError) { // error bar doesn't extend in key dimension (except whisker but we ignore that here), so only use data point center - const double current = mDataPlottable->interface1D()->dataMainKey(it-mDataContainer->constBegin()); + const double current = mDataPlottable->interface1D()->dataMainKey(int(it-mDataContainer->constBegin())); if (qIsNaN(current)) continue; if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0)) { @@ -27680,7 +28437,7 @@ QCPRange QCPErrorBars::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomai } } else // mErrorType == etKeyError { - const double dataKey = mDataPlottable->interface1D()->dataMainKey(it-mDataContainer->constBegin()); + const double dataKey = mDataPlottable->interface1D()->dataMainKey(int(it-mDataContainer->constBegin())); if (qIsNaN(dataKey)) continue; // plus error: double current = dataKey + (qIsNaN(it->errorPlus) ? 0 : it->errorPlus); @@ -27725,7 +28482,7 @@ QCPRange QCPErrorBars::getValueRange(bool &foundRange, QCP::SignDomain inSignDom if (!mDataPlottable) { foundRange = false; - return QCPRange(); + return {}; } QCPRange range; @@ -27736,20 +28493,20 @@ QCPRange QCPErrorBars::getValueRange(bool &foundRange, QCP::SignDomain inSignDom QCPErrorBarsDataContainer::const_iterator itEnd = mDataContainer->constEnd(); if (mDataPlottable->interface1D()->sortKeyIsMainKey() && restrictKeyRange) { - itBegin = mDataContainer->constBegin()+findBegin(inKeyRange.lower); - itEnd = mDataContainer->constBegin()+findEnd(inKeyRange.upper); + itBegin = mDataContainer->constBegin()+findBegin(inKeyRange.lower, false); + itEnd = mDataContainer->constBegin()+findEnd(inKeyRange.upper, false); } for (QCPErrorBarsDataContainer::const_iterator it = itBegin; it != itEnd; ++it) { if (restrictKeyRange) { - const double dataKey = mDataPlottable->interface1D()->dataMainKey(it-mDataContainer->constBegin()); + const double dataKey = mDataPlottable->interface1D()->dataMainKey(int(it-mDataContainer->constBegin())); if (dataKey < inKeyRange.lower || dataKey > inKeyRange.upper) continue; } if (mErrorType == etValueError) { - const double dataValue = mDataPlottable->interface1D()->dataMainValue(it-mDataContainer->constBegin()); + const double dataValue = mDataPlottable->interface1D()->dataMainValue(int(it-mDataContainer->constBegin())); if (qIsNaN(dataValue)) continue; // plus error: double current = dataValue + (qIsNaN(it->errorPlus) ? 0 : it->errorPlus); @@ -27774,7 +28531,7 @@ QCPRange QCPErrorBars::getValueRange(bool &foundRange, QCP::SignDomain inSignDom } else // mErrorType == etKeyError { // error bar doesn't extend in value dimension (except whisker but we ignore that here), so only use data point center - const double current = mDataPlottable->interface1D()->dataMainValue(it-mDataContainer->constBegin()); + const double current = mDataPlottable->interface1D()->dataMainValue(int(it-mDataContainer->constBegin())); if (qIsNaN(current)) continue; if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0)) { @@ -27821,7 +28578,7 @@ void QCPErrorBars::getErrorBarLines(QCPErrorBarsDataContainer::const_iterator it { if (!mDataPlottable) return; - int index = it-mDataContainer->constBegin(); + int index = int(it-mDataContainer->constBegin()); QPointF centerPixel = mDataPlottable->interface1D()->dataPixelPosition(index); if (qIsNaN(centerPixel.x()) || qIsNaN(centerPixel.y())) return; @@ -27965,9 +28722,9 @@ double QCPErrorBars::pointDistance(const QPointF &pixelPoint, QCPErrorBarsDataCo for (QCPErrorBarsDataContainer::const_iterator it=begin; it!=end; ++it) { getErrorBarLines(it, backbones, whiskers); - for (int i=0; i &selectedSegments, QList< range. This method assumes for performance reasons without checking that the key axis, the value axis, - and the data plottable (\ref setDataPlottable) are not zero and that \a index is within valid - bounds of this \ref QCPErrorBars instance and the bounds of the data plottable. + and the data plottable (\ref setDataPlottable) are not \c nullptr and that \a index is within + valid bounds of this \ref QCPErrorBars instance and the bounds of the data plottable. */ bool QCPErrorBars::errorBarVisible(int index) const { @@ -28059,8 +28816,8 @@ bool QCPErrorBars::rectIntersectsLine(const QRectF &pixelRect, const QLineF &lin /* end of 'src/plottables/plottable-errorbar.cpp' */ -/* including file 'src/items/item-straightline.cpp', size 7592 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/items/item-straightline.cpp' */ +/* modified 2021-03-29T02:30:44, size 7596 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemStraightLine @@ -28132,7 +28889,7 @@ void QCPItemStraightLine::draw(QCPPainter *painter) QCPVector2D start(point1->pixelPosition()); QCPVector2D end(point2->pixelPosition()); // get visible segment of straight line inside clipRect: - double clipPad = mainPen().widthF(); + int clipPad = qCeil(mainPen().widthF()); QLineF line = getRectClippedStraightLine(start, end-start, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad)); // paint visible segment, if existent: if (!line.isNull()) @@ -28240,8 +28997,8 @@ QPen QCPItemStraightLine::mainPen() const /* end of 'src/items/item-straightline.cpp' */ -/* including file 'src/items/item-line.cpp', size 8498 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/items/item-line.cpp' */ +/* modified 2021-03-29T02:30:44, size 8525 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemLine @@ -28343,8 +29100,8 @@ void QCPItemLine::draw(QCPPainter *painter) if (qFuzzyIsNull((startVec-endVec).lengthSquared())) return; // get visible segment of straight line inside clipRect: - double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance()); - clipPad = qMax(clipPad, (double)mainPen().widthF()); + int clipPad = int(qMax(mHead.boundingDistance(), mTail.boundingDistance())); + clipPad = qMax(clipPad, qCeil(mainPen().widthF())); QLineF line = getRectClippedLine(startVec, endVec, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad)); // paint visible segment, if existent: if (!line.isNull()) @@ -28368,10 +29125,10 @@ void QCPItemLine::draw(QCPPainter *painter) */ QLineF QCPItemLine::getRectClippedLine(const QCPVector2D &start, const QCPVector2D &end, const QRect &rect) const { - bool containsStart = rect.contains(start.x(), start.y()); - bool containsEnd = rect.contains(end.x(), end.y()); + bool containsStart = rect.contains(qRound(start.x()), qRound(start.y())); + bool containsEnd = rect.contains(qRound(end.x()), qRound(end.y())); if (containsStart && containsEnd) - return QLineF(start.toPointF(), end.toPointF()); + return {start.toPointF(), end.toPointF()}; QCPVector2D base = start; QCPVector2D vec = end-start; @@ -28471,8 +29228,8 @@ QPen QCPItemLine::mainPen() const /* end of 'src/items/item-line.cpp' */ -/* including file 'src/items/item-curve.cpp', size 7248 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/items/item-curve.cpp' */ +/* modified 2021-03-29T02:30:44, size 7273 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemCurve @@ -28611,7 +29368,8 @@ void QCPItemCurve::draw(QCPPainter *painter) cubicPath.cubicTo(startDirVec.toPointF(), endDirVec.toPointF(), endVec.toPointF()); // paint visible segment, if existent: - QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF()); + const int clipEnlarge = qCeil(mainPen().widthF()); + QRect clip = clipRect().adjusted(-clipEnlarge, -clipEnlarge, clipEnlarge, clipEnlarge); QRect cubicRect = cubicPath.controlPointRect().toRect(); if (cubicRect.isEmpty()) // may happen when start and end exactly on same x or y position cubicRect.adjust(0, 0, 1, 1); @@ -28639,8 +29397,8 @@ QPen QCPItemCurve::mainPen() const /* end of 'src/items/item-curve.cpp' */ -/* including file 'src/items/item-rect.cpp', size 6479 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/items/item-rect.cpp' */ +/* modified 2021-03-29T02:30:44, size 6472 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemRect @@ -28771,7 +29529,7 @@ QPointF QCPItemRect::anchorPixelPosition(int anchorId) const } qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId; - return QPointF(); + return {}; } /*! \internal @@ -28796,8 +29554,8 @@ QBrush QCPItemRect::mainBrush() const /* end of 'src/items/item-rect.cpp' */ -/* including file 'src/items/item-text.cpp', size 13338 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/items/item-text.cpp' */ +/* modified 2021-03-29T02:30:44, size 13335 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemText @@ -29025,7 +29783,7 @@ void QCPItemText::draw(QCPPainter *painter) QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation textRect.moveTopLeft(textPos.toPoint()+QPoint(mPadding.left(), mPadding.top())); textBoxRect.moveTopLeft(textPos.toPoint()); - double clipPad = mainPen().widthF(); + int clipPad = qCeil(mainPen().widthF()); QRect boundingRect = textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad); if (transform.mapRect(boundingRect).intersects(painter->transform().mapRect(clipRect()))) { @@ -29072,7 +29830,7 @@ QPointF QCPItemText::anchorPixelPosition(int anchorId) const } qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId; - return QPointF(); + return {}; } /*! \internal @@ -29144,8 +29902,8 @@ QBrush QCPItemText::mainBrush() const /* end of 'src/items/item-text.cpp' */ -/* including file 'src/items/item-ellipse.cpp', size 7863 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/items/item-ellipse.cpp' */ +/* modified 2021-03-29T02:30:44, size 7881 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemEllipse @@ -29269,7 +30027,8 @@ void QCPItemEllipse::draw(QCPPainter *painter) if (p1.toPoint() == p2.toPoint()) return; QRectF ellipseRect = QRectF(p1, p2).normalized(); - QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF()); + const int clipEnlarge = qCeil(mainPen().widthF()); + QRect clip = clipRect().adjusted(-clipEnlarge, -clipEnlarge, clipEnlarge, clipEnlarge); if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse is visible in cliprect { painter->setPen(mainPen()); @@ -29307,7 +30066,7 @@ QPointF QCPItemEllipse::anchorPixelPosition(int anchorId) const } qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId; - return QPointF(); + return {}; } /*! \internal @@ -29332,8 +30091,8 @@ QBrush QCPItemEllipse::mainBrush() const /* end of 'src/items/item-ellipse.cpp' */ -/* including file 'src/items/item-pixmap.cpp', size 10615 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/items/item-pixmap.cpp' */ +/* modified 2021-03-29T02:30:44, size 10622 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemPixmap @@ -29444,7 +30203,7 @@ void QCPItemPixmap::draw(QCPPainter *painter) bool flipHorz = false; bool flipVert = false; QRect rect = getFinalRect(&flipHorz, &flipVert); - double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF(); + int clipPad = mainPen().style() == Qt::NoPen ? 0 : qCeil(mainPen().widthF()); QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad); if (boundingRect.intersects(clipRect())) { @@ -29463,8 +30222,8 @@ void QCPItemPixmap::draw(QCPPainter *painter) /* inherits documentation from base class */ QPointF QCPItemPixmap::anchorPixelPosition(int anchorId) const { - bool flipHorz; - bool flipVert; + bool flipHorz = false; + bool flipVert = false; QRect rect = getFinalRect(&flipHorz, &flipVert); // we actually want denormal rects (negative width/height) here, so restore // the flipped state: @@ -29480,11 +30239,11 @@ QPointF QCPItemPixmap::anchorPixelPosition(int anchorId) const case aiRight: return (rect.topRight()+rect.bottomRight())*0.5; case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5; case aiBottomLeft: return rect.bottomLeft(); - case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;; + case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5; } qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId; - return QPointF(); + return {}; } /*! \internal @@ -29550,7 +30309,7 @@ QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const QPoint p1 = topLeft->pixelPosition().toPoint(); QPoint p2 = bottomRight->pixelPosition().toPoint(); if (p1 == p2) - return QRect(p1, QSize(0, 0)); + return {p1, QSize(0, 0)}; if (mScaled) { QSize newSize = QSize(p2.x()-p1.x(), p2.y()-p1.y()); @@ -29602,8 +30361,8 @@ QPen QCPItemPixmap::mainPen() const /* end of 'src/items/item-pixmap.cpp' */ -/* including file 'src/items/item-tracer.cpp', size 14624 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/items/item-tracer.cpp' */ +/* modified 2021-03-29T02:30:44, size 14645 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemTracer @@ -29650,7 +30409,7 @@ QCPItemTracer::QCPItemTracer(QCustomPlot *parentPlot) : position(createPosition(QLatin1String("position"))), mSize(6), mStyle(tsCrosshair), - mGraph(0), + mGraph(nullptr), mGraphKey(0), mInterpolating(false) { @@ -29730,9 +30489,9 @@ void QCPItemTracer::setStyle(QCPItemTracer::TracerStyle style) Sets the QCPGraph this tracer sticks to. The tracer \a position will be set to type QCPItemPosition::ptPlotCoords and the axes will be set to the axes of \a graph. - To free the tracer from any graph, set \a graph to 0. The tracer \a position can then be placed - freely like any other item position. This is the state the tracer will assume when its graph gets - deleted while still attached to it. + To free the tracer from any graph, set \a graph to \c nullptr. The tracer \a position can then be + placed freely like any other item position. This is the state the tracer will assume when its + graph gets deleted while still attached to it. \see setGraphKey */ @@ -29750,7 +30509,7 @@ void QCPItemTracer::setGraph(QCPGraph *graph) qDebug() << Q_FUNC_INFO << "graph isn't in same QCustomPlot instance as this item"; } else { - mGraph = 0; + mGraph = nullptr; } } @@ -29925,7 +30684,7 @@ void QCPItemTracer::updatePosition() { // interpolate between iterators around mGraphKey: double slope = 0; - if (!qFuzzyCompare((double)it->key, (double)prevIt->key)) + if (!qFuzzyCompare(double(it->key), double(prevIt->key))) slope = (it->value-prevIt->value)/(it->key-prevIt->key); position->setCoords(mGraphKey, (mGraphKey-prevIt->key)*slope+prevIt->value); } else @@ -29972,8 +30731,8 @@ QBrush QCPItemTracer::mainBrush() const /* end of 'src/items/item-tracer.cpp' */ -/* including file 'src/items/item-bracket.cpp', size 10687 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/items/item-bracket.cpp' */ +/* modified 2021-03-29T02:30:44, size 10705 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemBracket @@ -30020,195 +30779,4718 @@ QCPItemBracket::QCPItemBracket(QCustomPlot *parentPlot) : setSelectedPen(QPen(Qt::blue, 2)); } -QCPItemBracket::~QCPItemBracket() +QCPItemBracket::~QCPItemBracket() +{ +} + +/*! + Sets the pen that will be used to draw the bracket. + + Note that when the style is \ref bsCalligraphic, only the color will be taken from the pen, the + stroke and width are ignored. To change the apparent stroke width of a calligraphic bracket, use + \ref setLength, which has a similar effect. + + \see setSelectedPen +*/ +void QCPItemBracket::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the pen that will be used to draw the bracket when selected + + \see setPen, setSelected +*/ +void QCPItemBracket::setSelectedPen(const QPen &pen) +{ + mSelectedPen = pen; +} + +/*! + Sets the \a length in pixels how far the bracket extends in the direction towards the embraced + span of the bracket (i.e. perpendicular to the left-right-direction) + + \image html QCPItemBracket-length.png +
Demonstrating the effect of different values for \ref setLength, for styles \ref + bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.
+*/ +void QCPItemBracket::setLength(double length) +{ + mLength = length; +} + +/*! + Sets the style of the bracket, i.e. the shape/visual appearance. + + \see setPen +*/ +void QCPItemBracket::setStyle(QCPItemBracket::BracketStyle style) +{ + mStyle = style; +} + +/* inherits documentation from base class */ +double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + + QCPVector2D p(pos); + QCPVector2D leftVec(left->pixelPosition()); + QCPVector2D rightVec(right->pixelPosition()); + if (leftVec.toPoint() == rightVec.toPoint()) + return -1; + + QCPVector2D widthVec = (rightVec-leftVec)*0.5; + QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength; + QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec; + + switch (mStyle) + { + case QCPItemBracket::bsSquare: + case QCPItemBracket::bsRound: + { + double a = p.distanceSquaredToLine(centerVec-widthVec, centerVec+widthVec); + double b = p.distanceSquaredToLine(centerVec-widthVec+lengthVec, centerVec-widthVec); + double c = p.distanceSquaredToLine(centerVec+widthVec+lengthVec, centerVec+widthVec); + return qSqrt(qMin(qMin(a, b), c)); + } + case QCPItemBracket::bsCurly: + case QCPItemBracket::bsCalligraphic: + { + double a = p.distanceSquaredToLine(centerVec-widthVec*0.75+lengthVec*0.15, centerVec+lengthVec*0.3); + double b = p.distanceSquaredToLine(centerVec-widthVec+lengthVec*0.7, centerVec-widthVec*0.75+lengthVec*0.15); + double c = p.distanceSquaredToLine(centerVec+widthVec*0.75+lengthVec*0.15, centerVec+lengthVec*0.3); + double d = p.distanceSquaredToLine(centerVec+widthVec+lengthVec*0.7, centerVec+widthVec*0.75+lengthVec*0.15); + return qSqrt(qMin(qMin(a, b), qMin(c, d))); + } + } + return -1; +} + +/* inherits documentation from base class */ +void QCPItemBracket::draw(QCPPainter *painter) +{ + QCPVector2D leftVec(left->pixelPosition()); + QCPVector2D rightVec(right->pixelPosition()); + if (leftVec.toPoint() == rightVec.toPoint()) + return; + + QCPVector2D widthVec = (rightVec-leftVec)*0.5; + QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength; + QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec; + + QPolygon boundingPoly; + boundingPoly << leftVec.toPoint() << rightVec.toPoint() + << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint(); + const int clipEnlarge = qCeil(mainPen().widthF()); + QRect clip = clipRect().adjusted(-clipEnlarge, -clipEnlarge, clipEnlarge, clipEnlarge); + if (clip.intersects(boundingPoly.boundingRect())) + { + painter->setPen(mainPen()); + switch (mStyle) + { + case bsSquare: + { + painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF()); + painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF()); + painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF()); + break; + } + case bsRound: + { + painter->setBrush(Qt::NoBrush); + QPainterPath path; + path.moveTo((centerVec+widthVec+lengthVec).toPointF()); + path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF()); + path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF()); + painter->drawPath(path); + break; + } + case bsCurly: + { + painter->setBrush(Qt::NoBrush); + QPainterPath path; + path.moveTo((centerVec+widthVec+lengthVec).toPointF()); + path.cubicTo((centerVec+widthVec-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+lengthVec).toPointF(), centerVec.toPointF()); + path.cubicTo((centerVec-0.4*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF()); + painter->drawPath(path); + break; + } + case bsCalligraphic: + { + painter->setPen(Qt::NoPen); + painter->setBrush(QBrush(mainPen().color())); + QPainterPath path; + path.moveTo((centerVec+widthVec+lengthVec).toPointF()); + + path.cubicTo((centerVec+widthVec-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+0.8*lengthVec).toPointF(), centerVec.toPointF()); + path.cubicTo((centerVec-0.4*widthVec+0.8*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF()); + + path.cubicTo((centerVec-widthVec-lengthVec*0.5).toPointF(), (centerVec-0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+lengthVec*0.2).toPointF()); + path.cubicTo((centerVec+0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5).toPointF(), (centerVec+widthVec+lengthVec).toPointF()); + + painter->drawPath(path); + break; + } + } + } +} + +/* inherits documentation from base class */ +QPointF QCPItemBracket::anchorPixelPosition(int anchorId) const +{ + QCPVector2D leftVec(left->pixelPosition()); + QCPVector2D rightVec(right->pixelPosition()); + if (leftVec.toPoint() == rightVec.toPoint()) + return leftVec.toPointF(); + + QCPVector2D widthVec = (rightVec-leftVec)*0.5; + QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength; + QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec; + + switch (anchorId) + { + case aiCenter: + return centerVec.toPointF(); + } + qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId; + return {}; +} + +/*! \internal + + Returns the pen that should be used for drawing lines. Returns mPen when the + item is not selected and mSelectedPen when it is. +*/ +QPen QCPItemBracket::mainPen() const +{ + return mSelected ? mSelectedPen : mPen; +} +/* end of 'src/items/item-bracket.cpp' */ + + +/* including file 'src/polar/radialaxis.cpp' */ +/* modified 2021-03-29T02:30:44, size 49415 */ + + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPPolarAxisRadial +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPPolarAxisRadial + \brief The radial axis inside a radial plot + + \warning In this QCustomPlot version, polar plots are a tech preview. Expect documentation and + functionality to be incomplete, as well as changing public interfaces in the future. + + Each axis holds an instance of QCPAxisTicker which is used to generate the tick coordinates and + tick labels. You can access the currently installed \ref ticker or set a new one (possibly one of + the specialized subclasses, or your own subclass) via \ref setTicker. For details, see the + documentation of QCPAxisTicker. +*/ + +/* start of documentation of inline functions */ + +/*! \fn QSharedPointer QCPPolarAxisRadial::ticker() const + + Returns a modifiable shared pointer to the currently installed axis ticker. The axis ticker is + responsible for generating the tick positions and tick labels of this axis. You can access the + \ref QCPAxisTicker with this method and modify basic properties such as the approximate tick count + (\ref QCPAxisTicker::setTickCount). + + You can gain more control over the axis ticks by setting a different \ref QCPAxisTicker subclass, see + the documentation there. A new axis ticker can be set with \ref setTicker. + + Since the ticker is stored in the axis as a shared pointer, multiple axes may share the same axis + ticker simply by passing the same shared pointer to multiple axes. + + \see setTicker +*/ + +/* end of documentation of inline functions */ +/* start of documentation of signals */ + +/*! \fn void QCPPolarAxisRadial::rangeChanged(const QCPRange &newRange) + + This signal is emitted when the range of this axis has changed. You can connect it to the \ref + setRange slot of another axis to communicate the new range to the other axis, in order for it to + be synchronized. + + You may also manipulate/correct the range with \ref setRange in a slot connected to this signal. + This is useful if for example a maximum range span shall not be exceeded, or if the lower/upper + range shouldn't go beyond certain values (see \ref QCPRange::bounded). For example, the following + slot would limit the x axis to ranges between 0 and 10: + \code + customPlot->xAxis->setRange(newRange.bounded(0, 10)) + \endcode +*/ + +/*! \fn void QCPPolarAxisRadial::rangeChanged(const QCPRange &newRange, const QCPRange &oldRange) + \overload + + Additionally to the new range, this signal also provides the previous range held by the axis as + \a oldRange. +*/ + +/*! \fn void QCPPolarAxisRadial::scaleTypeChanged(QCPPolarAxisRadial::ScaleType scaleType); + + This signal is emitted when the scale type changes, by calls to \ref setScaleType +*/ + +/*! \fn void QCPPolarAxisRadial::selectionChanged(QCPPolarAxisRadial::SelectableParts selection) + + This signal is emitted when the selection state of this axis has changed, either by user interaction + or by a direct call to \ref setSelectedParts. +*/ + +/*! \fn void QCPPolarAxisRadial::selectableChanged(const QCPPolarAxisRadial::SelectableParts &parts); + + This signal is emitted when the selectability changes, by calls to \ref setSelectableParts +*/ + +/* end of documentation of signals */ + +/*! + Constructs an Axis instance of Type \a type for the axis rect \a parent. + + Usually it isn't necessary to instantiate axes directly, because you can let QCustomPlot create + them for you with \ref QCPAxisRect::addAxis. If you want to use own QCPAxis-subclasses however, + create them manually and then inject them also via \ref QCPAxisRect::addAxis. +*/ +QCPPolarAxisRadial::QCPPolarAxisRadial(QCPPolarAxisAngular *parent) : + QCPLayerable(parent->parentPlot(), QString(), parent), + mRangeDrag(true), + mRangeZoom(true), + mRangeZoomFactor(0.85), + // axis base: + mAngularAxis(parent), + mAngle(45), + mAngleReference(arAngularAxis), + mSelectableParts(spAxis | spTickLabels | spAxisLabel), + mSelectedParts(spNone), + mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + mSelectedBasePen(QPen(Qt::blue, 2)), + // axis label: + mLabelPadding(0), + mLabel(), + mLabelFont(mParentPlot->font()), + mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)), + mLabelColor(Qt::black), + mSelectedLabelColor(Qt::blue), + // tick labels: + // mTickLabelPadding(0), in label painter + mTickLabels(true), + // mTickLabelRotation(0), in label painter + mTickLabelFont(mParentPlot->font()), + mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)), + mTickLabelColor(Qt::black), + mSelectedTickLabelColor(Qt::blue), + mNumberPrecision(6), + mNumberFormatChar('g'), + mNumberBeautifulPowers(true), + mNumberMultiplyCross(false), + // ticks and subticks: + mTicks(true), + mSubTicks(true), + mTickLengthIn(5), + mTickLengthOut(0), + mSubTickLengthIn(2), + mSubTickLengthOut(0), + mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + mSelectedTickPen(QPen(Qt::blue, 2)), + mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + mSelectedSubTickPen(QPen(Qt::blue, 2)), + // scale and range: + mRange(0, 5), + mRangeReversed(false), + mScaleType(stLinear), + // internal members: + mRadius(1), // non-zero initial value, will be overwritten in ::update() according to inner rect + mTicker(new QCPAxisTicker), + mLabelPainter(mParentPlot) +{ + setParent(parent); + setAntialiased(true); + + setTickLabelPadding(5); + setTickLabelRotation(0); + setTickLabelMode(lmUpright); + mLabelPainter.setAnchorReferenceType(QCPLabelPainterPrivate::artTangent); + mLabelPainter.setAbbreviateDecimalPowers(false); +} + +QCPPolarAxisRadial::~QCPPolarAxisRadial() +{ +} + +QCPPolarAxisRadial::LabelMode QCPPolarAxisRadial::tickLabelMode() const +{ + switch (mLabelPainter.anchorMode()) + { + case QCPLabelPainterPrivate::amSkewedUpright: return lmUpright; + case QCPLabelPainterPrivate::amSkewedRotated: return lmRotated; + default: qDebug() << Q_FUNC_INFO << "invalid mode for polar axis"; break; + } + return lmUpright; +} + +/* No documentation as it is a property getter */ +QString QCPPolarAxisRadial::numberFormat() const +{ + QString result; + result.append(mNumberFormatChar); + if (mNumberBeautifulPowers) + { + result.append(QLatin1Char('b')); + if (mNumberMultiplyCross) + result.append(QLatin1Char('c')); + } + return result; +} + +/* No documentation as it is a property getter */ +int QCPPolarAxisRadial::tickLengthIn() const +{ + return mTickLengthIn; +} + +/* No documentation as it is a property getter */ +int QCPPolarAxisRadial::tickLengthOut() const +{ + return mTickLengthOut; +} + +/* No documentation as it is a property getter */ +int QCPPolarAxisRadial::subTickLengthIn() const +{ + return mSubTickLengthIn; +} + +/* No documentation as it is a property getter */ +int QCPPolarAxisRadial::subTickLengthOut() const +{ + return mSubTickLengthOut; +} + +/* No documentation as it is a property getter */ +int QCPPolarAxisRadial::labelPadding() const +{ + return mLabelPadding; +} + +void QCPPolarAxisRadial::setRangeDrag(bool enabled) +{ + mRangeDrag = enabled; +} + +void QCPPolarAxisRadial::setRangeZoom(bool enabled) +{ + mRangeZoom = enabled; +} + +void QCPPolarAxisRadial::setRangeZoomFactor(double factor) +{ + mRangeZoomFactor = factor; +} + +/*! + Sets whether the axis uses a linear scale or a logarithmic scale. + + Note that this method controls the coordinate transformation. For logarithmic scales, you will + likely also want to use a logarithmic tick spacing and labeling, which can be achieved by setting + the axis ticker to an instance of \ref QCPAxisTickerLog : + + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpaxisticker-log-creation + + See the documentation of \ref QCPAxisTickerLog about the details of logarithmic axis tick + creation. + + \ref setNumberPrecision +*/ +void QCPPolarAxisRadial::setScaleType(QCPPolarAxisRadial::ScaleType type) +{ + if (mScaleType != type) + { + mScaleType = type; + if (mScaleType == stLogarithmic) + setRange(mRange.sanitizedForLogScale()); + //mCachedMarginValid = false; + emit scaleTypeChanged(mScaleType); + } +} + +/*! + Sets the range of the axis. + + This slot may be connected with the \ref rangeChanged signal of another axis so this axis + is always synchronized with the other axis range, when it changes. + + To invert the direction of an axis, use \ref setRangeReversed. +*/ +void QCPPolarAxisRadial::setRange(const QCPRange &range) +{ + if (range.lower == mRange.lower && range.upper == mRange.upper) + return; + + if (!QCPRange::validRange(range)) return; + QCPRange oldRange = mRange; + if (mScaleType == stLogarithmic) + { + mRange = range.sanitizedForLogScale(); + } else + { + mRange = range.sanitizedForLinScale(); + } + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface. + (When \ref QCustomPlot::setInteractions contains iSelectAxes.) + + However, even when \a selectable is set to a value not allowing the selection of a specific part, + it is still possible to set the selection of this part manually, by calling \ref setSelectedParts + directly. + + \see SelectablePart, setSelectedParts +*/ +void QCPPolarAxisRadial::setSelectableParts(const SelectableParts &selectable) +{ + if (mSelectableParts != selectable) + { + mSelectableParts = selectable; + emit selectableChanged(mSelectableParts); + } +} + +/*! + Sets the selected state of the respective axis parts described by \ref SelectablePart. When a part + is selected, it uses a different pen/font. + + The entire selection mechanism for axes is handled automatically when \ref + QCustomPlot::setInteractions contains iSelectAxes. You only need to call this function when you + wish to change the selection state manually. + + This function can change the selection state of a part, independent of the \ref setSelectableParts setting. + + emits the \ref selectionChanged signal when \a selected is different from the previous selection state. + + \see SelectablePart, setSelectableParts, selectTest, setSelectedBasePen, setSelectedTickPen, setSelectedSubTickPen, + setSelectedTickLabelFont, setSelectedLabelFont, setSelectedTickLabelColor, setSelectedLabelColor +*/ +void QCPPolarAxisRadial::setSelectedParts(const SelectableParts &selected) +{ + if (mSelectedParts != selected) + { + mSelectedParts = selected; + emit selectionChanged(mSelectedParts); + } +} + +/*! + \overload + + Sets the lower and upper bound of the axis range. + + To invert the direction of an axis, use \ref setRangeReversed. + + There is also a slot to set a range, see \ref setRange(const QCPRange &range). +*/ +void QCPPolarAxisRadial::setRange(double lower, double upper) +{ + if (lower == mRange.lower && upper == mRange.upper) + return; + + if (!QCPRange::validRange(lower, upper)) return; + QCPRange oldRange = mRange; + mRange.lower = lower; + mRange.upper = upper; + if (mScaleType == stLogarithmic) + { + mRange = mRange.sanitizedForLogScale(); + } else + { + mRange = mRange.sanitizedForLinScale(); + } + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + \overload + + Sets the range of the axis. + + The \a position coordinate indicates together with the \a alignment parameter, where the new + range will be positioned. \a size defines the size of the new axis range. \a alignment may be + Qt::AlignLeft, Qt::AlignRight or Qt::AlignCenter. This will cause the left border, right border, + or center of the range to be aligned with \a position. Any other values of \a alignment will + default to Qt::AlignCenter. +*/ +void QCPPolarAxisRadial::setRange(double position, double size, Qt::AlignmentFlag alignment) +{ + if (alignment == Qt::AlignLeft) + setRange(position, position+size); + else if (alignment == Qt::AlignRight) + setRange(position-size, position); + else // alignment == Qt::AlignCenter + setRange(position-size/2.0, position+size/2.0); +} + +/*! + Sets the lower bound of the axis range. The upper bound is not changed. + \see setRange +*/ +void QCPPolarAxisRadial::setRangeLower(double lower) +{ + if (mRange.lower == lower) + return; + + QCPRange oldRange = mRange; + mRange.lower = lower; + if (mScaleType == stLogarithmic) + { + mRange = mRange.sanitizedForLogScale(); + } else + { + mRange = mRange.sanitizedForLinScale(); + } + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Sets the upper bound of the axis range. The lower bound is not changed. + \see setRange +*/ +void QCPPolarAxisRadial::setRangeUpper(double upper) +{ + if (mRange.upper == upper) + return; + + QCPRange oldRange = mRange; + mRange.upper = upper; + if (mScaleType == stLogarithmic) + { + mRange = mRange.sanitizedForLogScale(); + } else + { + mRange = mRange.sanitizedForLinScale(); + } + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Sets whether the axis range (direction) is displayed reversed. Normally, the values on horizontal + axes increase left to right, on vertical axes bottom to top. When \a reversed is set to true, the + direction of increasing values is inverted. + + Note that the range and data interface stays the same for reversed axes, e.g. the \a lower part + of the \ref setRange interface will still reference the mathematically smaller number than the \a + upper part. +*/ +void QCPPolarAxisRadial::setRangeReversed(bool reversed) +{ + mRangeReversed = reversed; +} + +void QCPPolarAxisRadial::setAngle(double degrees) +{ + mAngle = degrees; +} + +void QCPPolarAxisRadial::setAngleReference(AngleReference reference) +{ + mAngleReference = reference; +} + +/*! + The axis ticker is responsible for generating the tick positions and tick labels. See the + documentation of QCPAxisTicker for details on how to work with axis tickers. + + You can change the tick positioning/labeling behaviour of this axis by setting a different + QCPAxisTicker subclass using this method. If you only wish to modify the currently installed axis + ticker, access it via \ref ticker. + + Since the ticker is stored in the axis as a shared pointer, multiple axes may share the same axis + ticker simply by passing the same shared pointer to multiple axes. + + \see ticker +*/ +void QCPPolarAxisRadial::setTicker(QSharedPointer ticker) +{ + if (ticker) + mTicker = ticker; + else + qDebug() << Q_FUNC_INFO << "can not set 0 as axis ticker"; + // no need to invalidate margin cache here because produced tick labels are checked for changes in setupTickVector +} + +/*! + Sets whether tick marks are displayed. + + Note that setting \a show to false does not imply that tick labels are invisible, too. To achieve + that, see \ref setTickLabels. + + \see setSubTicks +*/ +void QCPPolarAxisRadial::setTicks(bool show) +{ + if (mTicks != show) + { + mTicks = show; + //mCachedMarginValid = false; + } +} + +/*! + Sets whether tick labels are displayed. Tick labels are the numbers drawn next to tick marks. +*/ +void QCPPolarAxisRadial::setTickLabels(bool show) +{ + if (mTickLabels != show) + { + mTickLabels = show; + //mCachedMarginValid = false; + if (!mTickLabels) + mTickVectorLabels.clear(); + } +} + +/*! + Sets the distance between the axis base line (including any outward ticks) and the tick labels. + \see setLabelPadding, setPadding +*/ +void QCPPolarAxisRadial::setTickLabelPadding(int padding) +{ + mLabelPainter.setPadding(padding); +} + +/*! + Sets the font of the tick labels. + + \see setTickLabels, setTickLabelColor +*/ +void QCPPolarAxisRadial::setTickLabelFont(const QFont &font) +{ + if (font != mTickLabelFont) + { + mTickLabelFont = font; + //mCachedMarginValid = false; + } +} + +/*! + Sets the color of the tick labels. + + \see setTickLabels, setTickLabelFont +*/ +void QCPPolarAxisRadial::setTickLabelColor(const QColor &color) +{ + mTickLabelColor = color; +} + +/*! + Sets the rotation of the tick labels. If \a degrees is zero, the labels are drawn normally. Else, + the tick labels are drawn rotated by \a degrees clockwise. The specified angle is bound to values + from -90 to 90 degrees. + + If \a degrees is exactly -90, 0 or 90, the tick labels are centered on the tick coordinate. For + other angles, the label is drawn with an offset such that it seems to point toward or away from + the tick mark. +*/ +void QCPPolarAxisRadial::setTickLabelRotation(double degrees) +{ + mLabelPainter.setRotation(degrees); +} + +void QCPPolarAxisRadial::setTickLabelMode(LabelMode mode) +{ + switch (mode) + { + case lmUpright: mLabelPainter.setAnchorMode(QCPLabelPainterPrivate::amSkewedUpright); break; + case lmRotated: mLabelPainter.setAnchorMode(QCPLabelPainterPrivate::amSkewedRotated); break; + } +} + +/*! + Sets the number format for the numbers in tick labels. This \a formatCode is an extended version + of the format code used e.g. by QString::number() and QLocale::toString(). For reference about + that, see the "Argument Formats" section in the detailed description of the QString class. + + \a formatCode is a string of one, two or three characters. The first character is identical to + the normal format code used by Qt. In short, this means: 'e'/'E' scientific format, 'f' fixed + format, 'g'/'G' scientific or fixed, whichever is shorter. + + The second and third characters are optional and specific to QCustomPlot:\n + If the first char was 'e' or 'g', numbers are/might be displayed in the scientific format, e.g. + "5.5e9", which is ugly in a plot. So when the second char of \a formatCode is set to 'b' (for + "beautiful"), those exponential numbers are formatted in a more natural way, i.e. "5.5 + [multiplication sign] 10 [superscript] 9". By default, the multiplication sign is a centered dot. + If instead a cross should be shown (as is usual in the USA), the third char of \a formatCode can + be set to 'c'. The inserted multiplication signs are the UTF-8 characters 215 (0xD7) for the + cross and 183 (0xB7) for the dot. + + Examples for \a formatCode: + \li \c g normal format code behaviour. If number is small, fixed format is used, if number is large, + normal scientific format is used + \li \c gb If number is small, fixed format is used, if number is large, scientific format is used with + beautifully typeset decimal powers and a dot as multiplication sign + \li \c ebc All numbers are in scientific format with beautifully typeset decimal power and a cross as + multiplication sign + \li \c fb illegal format code, since fixed format doesn't support (or need) beautifully typeset decimal + powers. Format code will be reduced to 'f'. + \li \c hello illegal format code, since first char is not 'e', 'E', 'f', 'g' or 'G'. Current format + code will not be changed. +*/ +void QCPPolarAxisRadial::setNumberFormat(const QString &formatCode) +{ + if (formatCode.isEmpty()) + { + qDebug() << Q_FUNC_INFO << "Passed formatCode is empty"; + return; + } + //mCachedMarginValid = false; + + // interpret first char as number format char: + QString allowedFormatChars(QLatin1String("eEfgG")); + if (allowedFormatChars.contains(formatCode.at(0))) + { + mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1()); + } else + { + qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode; + return; + } + + if (formatCode.length() < 2) + { + mNumberBeautifulPowers = false; + mNumberMultiplyCross = false; + } else + { + // interpret second char as indicator for beautiful decimal powers: + if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g'))) + mNumberBeautifulPowers = true; + else + qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode; + + if (formatCode.length() < 3) + { + mNumberMultiplyCross = false; + } else + { + // interpret third char as indicator for dot or cross multiplication symbol: + if (formatCode.at(2) == QLatin1Char('c')) + mNumberMultiplyCross = true; + else if (formatCode.at(2) == QLatin1Char('d')) + mNumberMultiplyCross = false; + else + qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode; + } + } + mLabelPainter.setSubstituteExponent(mNumberBeautifulPowers); + mLabelPainter.setMultiplicationSymbol(mNumberMultiplyCross ? QCPLabelPainterPrivate::SymbolCross : QCPLabelPainterPrivate::SymbolDot); +} + +/*! + Sets the precision of the tick label numbers. See QLocale::toString(double i, char f, int prec) + for details. The effect of precisions are most notably for number Formats starting with 'e', see + \ref setNumberFormat +*/ +void QCPPolarAxisRadial::setNumberPrecision(int precision) +{ + if (mNumberPrecision != precision) + { + mNumberPrecision = precision; + //mCachedMarginValid = false; + } +} + +/*! + Sets the length of the ticks in pixels. \a inside is the length the ticks will reach inside the + plot and \a outside is the length they will reach outside the plot. If \a outside is greater than + zero, the tick labels and axis label will increase their distance to the axis accordingly, so + they won't collide with the ticks. + + \see setSubTickLength, setTickLengthIn, setTickLengthOut +*/ +void QCPPolarAxisRadial::setTickLength(int inside, int outside) +{ + setTickLengthIn(inside); + setTickLengthOut(outside); +} + +/*! + Sets the length of the inward ticks in pixels. \a inside is the length the ticks will reach + inside the plot. + + \see setTickLengthOut, setTickLength, setSubTickLength +*/ +void QCPPolarAxisRadial::setTickLengthIn(int inside) +{ + if (mTickLengthIn != inside) + { + mTickLengthIn = inside; + } +} + +/*! + Sets the length of the outward ticks in pixels. \a outside is the length the ticks will reach + outside the plot. If \a outside is greater than zero, the tick labels and axis label will + increase their distance to the axis accordingly, so they won't collide with the ticks. + + \see setTickLengthIn, setTickLength, setSubTickLength +*/ +void QCPPolarAxisRadial::setTickLengthOut(int outside) +{ + if (mTickLengthOut != outside) + { + mTickLengthOut = outside; + //mCachedMarginValid = false; // only outside tick length can change margin + } +} + +/*! + Sets whether sub tick marks are displayed. + + Sub ticks are only potentially visible if (major) ticks are also visible (see \ref setTicks) + + \see setTicks +*/ +void QCPPolarAxisRadial::setSubTicks(bool show) +{ + if (mSubTicks != show) + { + mSubTicks = show; + //mCachedMarginValid = false; + } +} + +/*! + Sets the length of the subticks in pixels. \a inside is the length the subticks will reach inside + the plot and \a outside is the length they will reach outside the plot. If \a outside is greater + than zero, the tick labels and axis label will increase their distance to the axis accordingly, + so they won't collide with the ticks. + + \see setTickLength, setSubTickLengthIn, setSubTickLengthOut +*/ +void QCPPolarAxisRadial::setSubTickLength(int inside, int outside) +{ + setSubTickLengthIn(inside); + setSubTickLengthOut(outside); +} + +/*! + Sets the length of the inward subticks in pixels. \a inside is the length the subticks will reach inside + the plot. + + \see setSubTickLengthOut, setSubTickLength, setTickLength +*/ +void QCPPolarAxisRadial::setSubTickLengthIn(int inside) +{ + if (mSubTickLengthIn != inside) + { + mSubTickLengthIn = inside; + } +} + +/*! + Sets the length of the outward subticks in pixels. \a outside is the length the subticks will reach + outside the plot. If \a outside is greater than zero, the tick labels will increase their + distance to the axis accordingly, so they won't collide with the ticks. + + \see setSubTickLengthIn, setSubTickLength, setTickLength +*/ +void QCPPolarAxisRadial::setSubTickLengthOut(int outside) +{ + if (mSubTickLengthOut != outside) + { + mSubTickLengthOut = outside; + //mCachedMarginValid = false; // only outside tick length can change margin + } +} + +/*! + Sets the pen, the axis base line is drawn with. + + \see setTickPen, setSubTickPen +*/ +void QCPPolarAxisRadial::setBasePen(const QPen &pen) +{ + mBasePen = pen; +} + +/*! + Sets the pen, tick marks will be drawn with. + + \see setTickLength, setBasePen +*/ +void QCPPolarAxisRadial::setTickPen(const QPen &pen) +{ + mTickPen = pen; +} + +/*! + Sets the pen, subtick marks will be drawn with. + + \see setSubTickCount, setSubTickLength, setBasePen +*/ +void QCPPolarAxisRadial::setSubTickPen(const QPen &pen) +{ + mSubTickPen = pen; +} + +/*! + Sets the font of the axis label. + + \see setLabelColor +*/ +void QCPPolarAxisRadial::setLabelFont(const QFont &font) +{ + if (mLabelFont != font) + { + mLabelFont = font; + //mCachedMarginValid = false; + } +} + +/*! + Sets the color of the axis label. + + \see setLabelFont +*/ +void QCPPolarAxisRadial::setLabelColor(const QColor &color) +{ + mLabelColor = color; +} + +/*! + Sets the text of the axis label that will be shown below/above or next to the axis, depending on + its orientation. To disable axis labels, pass an empty string as \a str. +*/ +void QCPPolarAxisRadial::setLabel(const QString &str) +{ + if (mLabel != str) + { + mLabel = str; + //mCachedMarginValid = false; + } +} + +/*! + Sets the distance between the tick labels and the axis label. + + \see setTickLabelPadding, setPadding +*/ +void QCPPolarAxisRadial::setLabelPadding(int padding) +{ + if (mLabelPadding != padding) + { + mLabelPadding = padding; + //mCachedMarginValid = false; + } +} + +/*! + Sets the font that is used for tick labels when they are selected. + + \see setTickLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisRadial::setSelectedTickLabelFont(const QFont &font) +{ + if (font != mSelectedTickLabelFont) + { + mSelectedTickLabelFont = font; + // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts + } +} + +/*! + Sets the font that is used for the axis label when it is selected. + + \see setLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisRadial::setSelectedLabelFont(const QFont &font) +{ + mSelectedLabelFont = font; + // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts +} + +/*! + Sets the color that is used for tick labels when they are selected. + + \see setTickLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisRadial::setSelectedTickLabelColor(const QColor &color) +{ + if (color != mSelectedTickLabelColor) + { + mSelectedTickLabelColor = color; + } +} + +/*! + Sets the color that is used for the axis label when it is selected. + + \see setLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisRadial::setSelectedLabelColor(const QColor &color) +{ + mSelectedLabelColor = color; +} + +/*! + Sets the pen that is used to draw the axis base line when selected. + + \see setBasePen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisRadial::setSelectedBasePen(const QPen &pen) +{ + mSelectedBasePen = pen; +} + +/*! + Sets the pen that is used to draw the (major) ticks when selected. + + \see setTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisRadial::setSelectedTickPen(const QPen &pen) +{ + mSelectedTickPen = pen; +} + +/*! + Sets the pen that is used to draw the subticks when selected. + + \see setSubTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisRadial::setSelectedSubTickPen(const QPen &pen) +{ + mSelectedSubTickPen = pen; +} + +/*! + If the scale type (\ref setScaleType) is \ref stLinear, \a diff is added to the lower and upper + bounds of the range. The range is simply moved by \a diff. + + If the scale type is \ref stLogarithmic, the range bounds are multiplied by \a diff. This + corresponds to an apparent "linear" move in logarithmic scaling by a distance of log(diff). +*/ +void QCPPolarAxisRadial::moveRange(double diff) +{ + QCPRange oldRange = mRange; + if (mScaleType == stLinear) + { + mRange.lower += diff; + mRange.upper += diff; + } else // mScaleType == stLogarithmic + { + mRange.lower *= diff; + mRange.upper *= diff; + } + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Scales the range of this axis by \a factor around the center of the current axis range. For + example, if \a factor is 2.0, then the axis range will double its size, and the point at the axis + range center won't have changed its position in the QCustomPlot widget (i.e. coordinates around + the center will have moved symmetrically closer). + + If you wish to scale around a different coordinate than the current axis range center, use the + overload \ref scaleRange(double factor, double center). +*/ +void QCPPolarAxisRadial::scaleRange(double factor) +{ + scaleRange(factor, range().center()); +} + +/*! \overload + + Scales the range of this axis by \a factor around the coordinate \a center. For example, if \a + factor is 2.0, \a center is 1.0, then the axis range will double its size, and the point at + coordinate 1.0 won't have changed its position in the QCustomPlot widget (i.e. coordinates + around 1.0 will have moved symmetrically closer to 1.0). + + \see scaleRange(double factor) +*/ +void QCPPolarAxisRadial::scaleRange(double factor, double center) +{ + QCPRange oldRange = mRange; + if (mScaleType == stLinear) + { + QCPRange newRange; + newRange.lower = (mRange.lower-center)*factor + center; + newRange.upper = (mRange.upper-center)*factor + center; + if (QCPRange::validRange(newRange)) + mRange = newRange.sanitizedForLinScale(); + } else // mScaleType == stLogarithmic + { + if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range + { + QCPRange newRange; + newRange.lower = qPow(mRange.lower/center, factor)*center; + newRange.upper = qPow(mRange.upper/center, factor)*center; + if (QCPRange::validRange(newRange)) + mRange = newRange.sanitizedForLogScale(); + } else + qDebug() << Q_FUNC_INFO << "Center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center; + } + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Changes the axis range such that all plottables associated with this axis are fully visible in + that dimension. + + \see QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes +*/ +void QCPPolarAxisRadial::rescale(bool onlyVisiblePlottables) +{ + Q_UNUSED(onlyVisiblePlottables) + /* TODO + QList p = plottables(); + QCPRange newRange; + bool haveRange = false; + for (int i=0; irealVisibility() && onlyVisiblePlottables) + continue; + QCPRange plottableRange; + bool currentFoundRange; + QCP::SignDomain signDomain = QCP::sdBoth; + if (mScaleType == stLogarithmic) + signDomain = (mRange.upper < 0 ? QCP::sdNegative : QCP::sdPositive); + if (p.at(i)->keyAxis() == this) + plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain); + else + plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain); + if (currentFoundRange) + { + if (!haveRange) + newRange = plottableRange; + else + newRange.expand(plottableRange); + haveRange = true; + } + } + if (haveRange) + { + if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable + { + double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason + if (mScaleType == stLinear) + { + newRange.lower = center-mRange.size()/2.0; + newRange.upper = center+mRange.size()/2.0; + } else // mScaleType == stLogarithmic + { + newRange.lower = center/qSqrt(mRange.upper/mRange.lower); + newRange.upper = center*qSqrt(mRange.upper/mRange.lower); + } + } + setRange(newRange); + } + */ +} + +/*! + Transforms \a value, in pixel coordinates of the QCustomPlot widget, to axis coordinates. +*/ +void QCPPolarAxisRadial::pixelToCoord(QPointF pixelPos, double &angleCoord, double &radiusCoord) const +{ + QCPVector2D posVector(pixelPos-mCenter); + radiusCoord = radiusToCoord(posVector.length()); + angleCoord = mAngularAxis->angleRadToCoord(posVector.angle()); +} + +/*! + Transforms \a value, in coordinates of the axis, to pixel coordinates of the QCustomPlot widget. +*/ +QPointF QCPPolarAxisRadial::coordToPixel(double angleCoord, double radiusCoord) const +{ + const double radiusPixel = coordToRadius(radiusCoord); + const double angleRad = mAngularAxis->coordToAngleRad(angleCoord); + return QPointF(mCenter.x()+qCos(angleRad)*radiusPixel, mCenter.y()+qSin(angleRad)*radiusPixel); +} + +double QCPPolarAxisRadial::coordToRadius(double coord) const +{ + if (mScaleType == stLinear) + { + if (!mRangeReversed) + return (coord-mRange.lower)/mRange.size()*mRadius; + else + return (mRange.upper-coord)/mRange.size()*mRadius; + } else // mScaleType == stLogarithmic + { + if (coord >= 0.0 && mRange.upper < 0.0) // invalid value for logarithmic scale, just return outside visible range + return !mRangeReversed ? mRadius+200 : mRadius-200; + else if (coord <= 0.0 && mRange.upper >= 0.0) // invalid value for logarithmic scale, just return outside visible range + return !mRangeReversed ? mRadius-200 :mRadius+200; + else + { + if (!mRangeReversed) + return qLn(coord/mRange.lower)/qLn(mRange.upper/mRange.lower)*mRadius; + else + return qLn(mRange.upper/coord)/qLn(mRange.upper/mRange.lower)*mRadius; + } + } +} + +double QCPPolarAxisRadial::radiusToCoord(double radius) const +{ + if (mScaleType == stLinear) + { + if (!mRangeReversed) + return (radius)/mRadius*mRange.size()+mRange.lower; + else + return -(radius)/mRadius*mRange.size()+mRange.upper; + } else // mScaleType == stLogarithmic + { + if (!mRangeReversed) + return qPow(mRange.upper/mRange.lower, (radius)/mRadius)*mRange.lower; + else + return qPow(mRange.upper/mRange.lower, (-radius)/mRadius)*mRange.upper; + } +} + + +/*! + Returns the part of the axis that is hit by \a pos (in pixels). The return value of this function + is independent of the user-selectable parts defined with \ref setSelectableParts. Further, this + function does not change the current selection state of the axis. + + If the axis is not visible (\ref setVisible), this function always returns \ref spNone. + + \see setSelectedParts, setSelectableParts, QCustomPlot::setInteractions +*/ +QCPPolarAxisRadial::SelectablePart QCPPolarAxisRadial::getPartAt(const QPointF &pos) const +{ + Q_UNUSED(pos) // TODO remove later + if (!mVisible) + return spNone; + + /* + TODO: + if (mAxisPainter->axisSelectionBox().contains(pos.toPoint())) + return spAxis; + else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint())) + return spTickLabels; + else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint())) + return spAxisLabel; + else */ + return spNone; +} + +/* inherits documentation from base class */ +double QCPPolarAxisRadial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + if (!mParentPlot) return -1; + SelectablePart part = getPartAt(pos); + if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone) + return -1; + + if (details) + details->setValue(part); + return mParentPlot->selectionTolerance()*0.99; +} + +/* inherits documentation from base class */ +void QCPPolarAxisRadial::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) +{ + Q_UNUSED(event) + SelectablePart part = details.value(); + if (mSelectableParts.testFlag(part)) + { + SelectableParts selBefore = mSelectedParts; + setSelectedParts(additive ? mSelectedParts^part : part); + if (selectionStateChanged) + *selectionStateChanged = mSelectedParts != selBefore; + } +} + +/* inherits documentation from base class */ +void QCPPolarAxisRadial::deselectEvent(bool *selectionStateChanged) +{ + SelectableParts selBefore = mSelectedParts; + setSelectedParts(mSelectedParts & ~mSelectableParts); + if (selectionStateChanged) + *selectionStateChanged = mSelectedParts != selBefore; +} + +/*! \internal + + This mouse event reimplementation provides the functionality to let the user drag individual axes + exclusively, by startig the drag on top of the axis. + + For the axis to accept this event and perform the single axis drag, the parent \ref QCPAxisRect + must be configured accordingly, i.e. it must allow range dragging in the orientation of this axis + (\ref QCPAxisRect::setRangeDrag) and this axis must be a draggable axis (\ref + QCPAxisRect::setRangeDragAxes) + + \seebaseclassmethod + + \note The dragging of possibly multiple axes at once by starting the drag anywhere in the axis + rect is handled by the axis rect's mouse event, e.g. \ref QCPAxisRect::mousePressEvent. +*/ +void QCPPolarAxisRadial::mousePressEvent(QMouseEvent *event, const QVariant &details) +{ + Q_UNUSED(details) + if (!mParentPlot->interactions().testFlag(QCP::iRangeDrag)) + { + event->ignore(); + return; + } + + if (event->buttons() & Qt::LeftButton) + { + mDragging = true; + // initialize antialiasing backup in case we start dragging: + if (mParentPlot->noAntialiasingOnDrag()) + { + mAADragBackup = mParentPlot->antialiasedElements(); + mNotAADragBackup = mParentPlot->notAntialiasedElements(); + } + // Mouse range dragging interaction: + if (mParentPlot->interactions().testFlag(QCP::iRangeDrag)) + mDragStartRange = mRange; + } +} + +/*! \internal + + This mouse event reimplementation provides the functionality to let the user drag individual axes + exclusively, by startig the drag on top of the axis. + + \seebaseclassmethod + + \note The dragging of possibly multiple axes at once by starting the drag anywhere in the axis + rect is handled by the axis rect's mouse event, e.g. \ref QCPAxisRect::mousePressEvent. + + \see QCPAxis::mousePressEvent +*/ +void QCPPolarAxisRadial::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) +{ + Q_UNUSED(event) // TODO remove later + Q_UNUSED(startPos) // TODO remove later + if (mDragging) + { + /* TODO + const double startPixel = orientation() == Qt::Horizontal ? startPos.x() : startPos.y(); + const double currentPixel = orientation() == Qt::Horizontal ? event->pos().x() : event->pos().y(); + if (mScaleType == QCPPolarAxisRadial::stLinear) + { + const double diff = pixelToCoord(startPixel) - pixelToCoord(currentPixel); + setRange(mDragStartRange.lower+diff, mDragStartRange.upper+diff); + } else if (mScaleType == QCPPolarAxisRadial::stLogarithmic) + { + const double diff = pixelToCoord(startPixel) / pixelToCoord(currentPixel); + setRange(mDragStartRange.lower*diff, mDragStartRange.upper*diff); + } + */ + + if (mParentPlot->noAntialiasingOnDrag()) + mParentPlot->setNotAntialiasedElements(QCP::aeAll); + mParentPlot->replot(QCustomPlot::rpQueuedReplot); + } +} + +/*! \internal + + This mouse event reimplementation provides the functionality to let the user drag individual axes + exclusively, by startig the drag on top of the axis. + + \seebaseclassmethod + + \note The dragging of possibly multiple axes at once by starting the drag anywhere in the axis + rect is handled by the axis rect's mouse event, e.g. \ref QCPAxisRect::mousePressEvent. + + \see QCPAxis::mousePressEvent +*/ +void QCPPolarAxisRadial::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) +{ + Q_UNUSED(event) + Q_UNUSED(startPos) + mDragging = false; + if (mParentPlot->noAntialiasingOnDrag()) + { + mParentPlot->setAntialiasedElements(mAADragBackup); + mParentPlot->setNotAntialiasedElements(mNotAADragBackup); + } +} + +/*! \internal + + This mouse event reimplementation provides the functionality to let the user zoom individual axes + exclusively, by performing the wheel event on top of the axis. + + For the axis to accept this event and perform the single axis zoom, the parent \ref QCPAxisRect + must be configured accordingly, i.e. it must allow range zooming in the orientation of this axis + (\ref QCPAxisRect::setRangeZoom) and this axis must be a zoomable axis (\ref + QCPAxisRect::setRangeZoomAxes) + + \seebaseclassmethod + + \note The zooming of possibly multiple axes at once by performing the wheel event anywhere in the + axis rect is handled by the axis rect's mouse event, e.g. \ref QCPAxisRect::wheelEvent. +*/ +void QCPPolarAxisRadial::wheelEvent(QWheelEvent *event) +{ + // Mouse range zooming interaction: + if (!mParentPlot->interactions().testFlag(QCP::iRangeZoom)) + { + event->ignore(); + return; + } + + // TODO: + //const double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually + //const double factor = qPow(mRangeZoomFactor, wheelSteps); + //scaleRange(factor, pixelToCoord(orientation() == Qt::Horizontal ? event->pos().x() : event->pos().y())); + mParentPlot->replot(); +} + +void QCPPolarAxisRadial::updateGeometry(const QPointF ¢er, double radius) +{ + mCenter = center; + mRadius = radius; + if (mRadius < 1) mRadius = 1; +} + +/*! \internal + + A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter + before drawing axis lines. + + This is the antialiasing state the painter passed to the \ref draw method is in by default. + + This function takes into account the local setting of the antialiasing flag as well as the + overrides set with \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. + + \seebaseclassmethod + + \see setAntialiased +*/ +void QCPPolarAxisRadial::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes); +} + +/*! \internal + + Draws the axis with the specified \a painter, using the internal QCPAxisPainterPrivate instance. + + \seebaseclassmethod +*/ +void QCPPolarAxisRadial::draw(QCPPainter *painter) +{ + const double axisAngleRad = (mAngle+(mAngleReference==arAngularAxis ? mAngularAxis->angle() : 0))/180.0*M_PI; + const QPointF axisVector(qCos(axisAngleRad), qSin(axisAngleRad)); // semantically should be QCPVector2D, but we save time in loops when we keep it as QPointF + const QPointF tickNormal = QCPVector2D(axisVector).perpendicular().toPointF(); // semantically should be QCPVector2D, but we save time in loops when we keep it as QPointF + + // draw baseline: + painter->setPen(getBasePen()); + painter->drawLine(QLineF(mCenter, mCenter+axisVector*(mRadius-0.5))); + + // draw subticks: + if (!mSubTickVector.isEmpty()) + { + painter->setPen(getSubTickPen()); + for (int i=0; idrawLine(QLineF(tickPosition-tickNormal*mSubTickLengthIn, tickPosition+tickNormal*mSubTickLengthOut)); + } + } + + // draw ticks and labels: + if (!mTickVector.isEmpty()) + { + mLabelPainter.setAnchorReference(mCenter-axisVector); // subtract (normalized) axisVector, just to prevent degenerate tangents for tick label at exact lower axis range + mLabelPainter.setFont(getTickLabelFont()); + mLabelPainter.setColor(getTickLabelColor()); + const QPen ticksPen = getTickPen(); + painter->setPen(ticksPen); + for (int i=0; idrawLine(QLineF(tickPosition-tickNormal*mTickLengthIn, tickPosition+tickNormal*mTickLengthOut)); + // possibly draw tick labels: + if (!mTickVectorLabels.isEmpty()) + { + if ((!mRangeReversed && (i < mTickVectorLabels.count()-1 || mRadius-r > 10)) || + (mRangeReversed && (i > 0 || mRadius-r > 10))) // skip last label if it's closer than 10 pixels to angular axis + mLabelPainter.drawTickLabel(painter, tickPosition+tickNormal*mSubTickLengthOut, mTickVectorLabels.at(i)); + } + } + } +} + +/*! \internal + + Prepares the internal tick vector, sub tick vector and tick label vector. This is done by calling + QCPAxisTicker::generate on the currently installed ticker. + + If a change in the label text/count is detected, the cached axis margin is invalidated to make + sure the next margin calculation recalculates the label sizes and returns an up-to-date value. +*/ +void QCPPolarAxisRadial::setupTickVectors() +{ + if (!mParentPlot) return; + if ((!mTicks && !mTickLabels) || mRange.size() <= 0) return; + + mTicker->generate(mRange, mParentPlot->locale(), mNumberFormatChar, mNumberPrecision, mTickVector, mSubTicks ? &mSubTickVector : 0, mTickLabels ? &mTickVectorLabels : 0); +} + +/*! \internal + + Returns the pen that is used to draw the axis base line. Depending on the selection state, this + is either mSelectedBasePen or mBasePen. +*/ +QPen QCPPolarAxisRadial::getBasePen() const +{ + return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen; +} + +/*! \internal + + Returns the pen that is used to draw the (major) ticks. Depending on the selection state, this + is either mSelectedTickPen or mTickPen. +*/ +QPen QCPPolarAxisRadial::getTickPen() const +{ + return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen; +} + +/*! \internal + + Returns the pen that is used to draw the subticks. Depending on the selection state, this + is either mSelectedSubTickPen or mSubTickPen. +*/ +QPen QCPPolarAxisRadial::getSubTickPen() const +{ + return mSelectedParts.testFlag(spAxis) ? mSelectedSubTickPen : mSubTickPen; +} + +/*! \internal + + Returns the font that is used to draw the tick labels. Depending on the selection state, this + is either mSelectedTickLabelFont or mTickLabelFont. +*/ +QFont QCPPolarAxisRadial::getTickLabelFont() const +{ + return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelFont : mTickLabelFont; +} + +/*! \internal + + Returns the font that is used to draw the axis label. Depending on the selection state, this + is either mSelectedLabelFont or mLabelFont. +*/ +QFont QCPPolarAxisRadial::getLabelFont() const +{ + return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelFont : mLabelFont; +} + +/*! \internal + + Returns the color that is used to draw the tick labels. Depending on the selection state, this + is either mSelectedTickLabelColor or mTickLabelColor. +*/ +QColor QCPPolarAxisRadial::getTickLabelColor() const +{ + return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelColor : mTickLabelColor; +} + +/*! \internal + + Returns the color that is used to draw the axis label. Depending on the selection state, this + is either mSelectedLabelColor or mLabelColor. +*/ +QColor QCPPolarAxisRadial::getLabelColor() const +{ + return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelColor : mLabelColor; +} + + +/* inherits documentation from base class */ +QCP::Interaction QCPPolarAxisRadial::selectionCategory() const +{ + return QCP::iSelectAxes; +} +/* end of 'src/polar/radialaxis.cpp' */ + + +/* including file 'src/polar/layoutelement-angularaxis.cpp' */ +/* modified 2021-03-29T02:30:44, size 57266 */ + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPPolarAxisAngular +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPPolarAxisAngular + \brief The main container for polar plots, representing the angular axis as a circle + + \warning In this QCustomPlot version, polar plots are a tech preview. Expect documentation and + functionality to be incomplete, as well as changing public interfaces in the future. +*/ + +/* start documentation of inline functions */ + +/*! \fn QCPLayoutInset *QCPPolarAxisAngular::insetLayout() const + + Returns the inset layout of this axis rect. It can be used to place other layout elements (or + even layouts with multiple other elements) inside/on top of an axis rect. + + \see QCPLayoutInset +*/ + +/*! \fn int QCPPolarAxisAngular::left() const + + Returns the pixel position of the left border of this axis rect. Margins are not taken into + account here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn int QCPPolarAxisAngular::right() const + + Returns the pixel position of the right border of this axis rect. Margins are not taken into + account here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn int QCPPolarAxisAngular::top() const + + Returns the pixel position of the top border of this axis rect. Margins are not taken into + account here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn int QCPPolarAxisAngular::bottom() const + + Returns the pixel position of the bottom border of this axis rect. Margins are not taken into + account here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn int QCPPolarAxisAngular::width() const + + Returns the pixel width of this axis rect. Margins are not taken into account here, so the + returned value is with respect to the inner \ref rect. +*/ + +/*! \fn int QCPPolarAxisAngular::height() const + + Returns the pixel height of this axis rect. Margins are not taken into account here, so the + returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QSize QCPPolarAxisAngular::size() const + + Returns the pixel size of this axis rect. Margins are not taken into account here, so the + returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QPoint QCPPolarAxisAngular::topLeft() const + + Returns the top left corner of this axis rect in pixels. Margins are not taken into account here, + so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QPoint QCPPolarAxisAngular::topRight() const + + Returns the top right corner of this axis rect in pixels. Margins are not taken into account + here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QPoint QCPPolarAxisAngular::bottomLeft() const + + Returns the bottom left corner of this axis rect in pixels. Margins are not taken into account + here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QPoint QCPPolarAxisAngular::bottomRight() const + + Returns the bottom right corner of this axis rect in pixels. Margins are not taken into account + here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QPoint QCPPolarAxisAngular::center() const + + Returns the center of this axis rect in pixels. Margins are not taken into account here, so the + returned value is with respect to the inner \ref rect. +*/ + +/* end documentation of inline functions */ + +/*! + Creates a QCPPolarAxis instance and sets default values. An axis is added for each of the four + sides, the top and right axes are set invisible initially. +*/ +QCPPolarAxisAngular::QCPPolarAxisAngular(QCustomPlot *parentPlot) : + QCPLayoutElement(parentPlot), + mBackgroundBrush(Qt::NoBrush), + mBackgroundScaled(true), + mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding), + mInsetLayout(new QCPLayoutInset), + mRangeDrag(false), + mRangeZoom(false), + mRangeZoomFactor(0.85), + // axis base: + mAngle(-90), + mAngleRad(mAngle/180.0*M_PI), + mSelectableParts(spAxis | spTickLabels | spAxisLabel), + mSelectedParts(spNone), + mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + mSelectedBasePen(QPen(Qt::blue, 2)), + // axis label: + mLabelPadding(0), + mLabel(), + mLabelFont(mParentPlot->font()), + mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)), + mLabelColor(Qt::black), + mSelectedLabelColor(Qt::blue), + // tick labels: + //mTickLabelPadding(0), in label painter + mTickLabels(true), + //mTickLabelRotation(0), in label painter + mTickLabelFont(mParentPlot->font()), + mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)), + mTickLabelColor(Qt::black), + mSelectedTickLabelColor(Qt::blue), + mNumberPrecision(6), + mNumberFormatChar('g'), + mNumberBeautifulPowers(true), + mNumberMultiplyCross(false), + // ticks and subticks: + mTicks(true), + mSubTicks(true), + mTickLengthIn(5), + mTickLengthOut(0), + mSubTickLengthIn(2), + mSubTickLengthOut(0), + mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + mSelectedTickPen(QPen(Qt::blue, 2)), + mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + mSelectedSubTickPen(QPen(Qt::blue, 2)), + // scale and range: + mRange(0, 360), + mRangeReversed(false), + // internal members: + mRadius(1), // non-zero initial value, will be overwritten in ::update() according to inner rect + mGrid(new QCPPolarGrid(this)), + mTicker(new QCPAxisTickerFixed), + mDragging(false), + mLabelPainter(parentPlot) +{ + // TODO: + //mInsetLayout->initializeParentPlot(mParentPlot); + //mInsetLayout->setParentLayerable(this); + //mInsetLayout->setParent(this); + + if (QCPAxisTickerFixed *fixedTicker = mTicker.dynamicCast().data()) + { + fixedTicker->setTickStep(30); + } + setAntialiased(true); + setLayer(mParentPlot->currentLayer()); // it's actually on that layer already, but we want it in front of the grid, so we place it on there again + + setTickLabelPadding(5); + setTickLabelRotation(0); + setTickLabelMode(lmUpright); + mLabelPainter.setAnchorReferenceType(QCPLabelPainterPrivate::artNormal); + mLabelPainter.setAbbreviateDecimalPowers(false); + mLabelPainter.setCacheSize(24); // so we can cache up to 15-degree intervals, polar angular axis uses a bit larger cache than normal axes + + setMinimumSize(50, 50); + setMinimumMargins(QMargins(30, 30, 30, 30)); + + addRadialAxis(); + mGrid->setRadialAxis(radialAxis()); +} + +QCPPolarAxisAngular::~QCPPolarAxisAngular() +{ + delete mGrid; // delete grid here instead of via parent ~QObject for better defined deletion order + mGrid = 0; + + delete mInsetLayout; + mInsetLayout = 0; + + QList radialAxesList = radialAxes(); + for (int i=0; i= 0 && index < mRadialAxes.size()) + { + return mRadialAxes.at(index); + } else + { + qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index; + return 0; + } +} + +/*! + Returns all axes on the axis rect sides specified with \a types. + + \a types may be a single \ref QCPAxis::AxisType or an or-combination, to get the axes of + multiple sides. + + \see axis +*/ +QList QCPPolarAxisAngular::radialAxes() const +{ + return mRadialAxes; +} + + +/*! + Adds a new axis to the axis rect side specified with \a type, and returns it. If \a axis is 0, a + new QCPAxis instance is created internally. QCustomPlot owns the returned axis, so if you want to + remove an axis, use \ref removeAxis instead of deleting it manually. + + You may inject QCPAxis instances (or subclasses of QCPAxis) by setting \a axis to an axis that was + previously created outside QCustomPlot. It is important to note that QCustomPlot takes ownership + of the axis, so you may not delete it afterwards. Further, the \a axis must have been created + with this axis rect as parent and with the same axis type as specified in \a type. If this is not + the case, a debug output is generated, the axis is not added, and the method returns 0. + + This method can not be used to move \a axis between axis rects. The same \a axis instance must + not be added multiple times to the same or different axis rects. + + If an axis rect side already contains one or more axes, the lower and upper endings of the new + axis (\ref QCPAxis::setLowerEnding, \ref QCPAxis::setUpperEnding) are set to \ref + QCPLineEnding::esHalfBar. + + \see addAxes, setupFullAxesBox +*/ +QCPPolarAxisRadial *QCPPolarAxisAngular::addRadialAxis(QCPPolarAxisRadial *axis) +{ + QCPPolarAxisRadial *newAxis = axis; + if (!newAxis) + { + newAxis = new QCPPolarAxisRadial(this); + } else // user provided existing axis instance, do some sanity checks + { + if (newAxis->angularAxis() != this) + { + qDebug() << Q_FUNC_INFO << "passed radial axis doesn't have this angular axis as parent angular axis"; + return 0; + } + if (radialAxes().contains(newAxis)) + { + qDebug() << Q_FUNC_INFO << "passed axis is already owned by this angular axis"; + return 0; + } + } + mRadialAxes.append(newAxis); + return newAxis; +} + +/*! + Removes the specified \a axis from the axis rect and deletes it. + + Returns true on success, i.e. if \a axis was a valid axis in this axis rect. + + \see addAxis +*/ +bool QCPPolarAxisAngular::removeRadialAxis(QCPPolarAxisRadial *radialAxis) +{ + if (mRadialAxes.contains(radialAxis)) + { + mRadialAxes.removeOne(radialAxis); + delete radialAxis; + return true; + } else + { + qDebug() << Q_FUNC_INFO << "Radial axis isn't associated with this angular axis:" << reinterpret_cast(radialAxis); + return false; + } +} + +QRegion QCPPolarAxisAngular::exactClipRegion() const +{ + return QRegion(mCenter.x()-mRadius, mCenter.y()-mRadius, qRound(2*mRadius), qRound(2*mRadius), QRegion::Ellipse); +} + +/*! + If the scale type (\ref setScaleType) is \ref stLinear, \a diff is added to the lower and upper + bounds of the range. The range is simply moved by \a diff. + + If the scale type is \ref stLogarithmic, the range bounds are multiplied by \a diff. This + corresponds to an apparent "linear" move in logarithmic scaling by a distance of log(diff). +*/ +void QCPPolarAxisAngular::moveRange(double diff) +{ + QCPRange oldRange = mRange; + mRange.lower += diff; + mRange.upper += diff; + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Scales the range of this axis by \a factor around the center of the current axis range. For + example, if \a factor is 2.0, then the axis range will double its size, and the point at the axis + range center won't have changed its position in the QCustomPlot widget (i.e. coordinates around + the center will have moved symmetrically closer). + + If you wish to scale around a different coordinate than the current axis range center, use the + overload \ref scaleRange(double factor, double center). +*/ +void QCPPolarAxisAngular::scaleRange(double factor) +{ + scaleRange(factor, range().center()); +} + +/*! \overload + + Scales the range of this axis by \a factor around the coordinate \a center. For example, if \a + factor is 2.0, \a center is 1.0, then the axis range will double its size, and the point at + coordinate 1.0 won't have changed its position in the QCustomPlot widget (i.e. coordinates + around 1.0 will have moved symmetrically closer to 1.0). + + \see scaleRange(double factor) +*/ +void QCPPolarAxisAngular::scaleRange(double factor, double center) +{ + QCPRange oldRange = mRange; + QCPRange newRange; + newRange.lower = (mRange.lower-center)*factor + center; + newRange.upper = (mRange.upper-center)*factor + center; + if (QCPRange::validRange(newRange)) + mRange = newRange.sanitizedForLinScale(); + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Changes the axis range such that all plottables associated with this axis are fully visible in + that dimension. + + \see QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes +*/ +void QCPPolarAxisAngular::rescale(bool onlyVisiblePlottables) +{ + QCPRange newRange; + bool haveRange = false; + for (int i=0; irealVisibility() && onlyVisiblePlottables) + continue; + QCPRange range; + bool currentFoundRange; + if (mGraphs.at(i)->keyAxis() == this) + range = mGraphs.at(i)->getKeyRange(currentFoundRange, QCP::sdBoth); + else + range = mGraphs.at(i)->getValueRange(currentFoundRange, QCP::sdBoth); + if (currentFoundRange) + { + if (!haveRange) + newRange = range; + else + newRange.expand(range); + haveRange = true; + } + } + if (haveRange) + { + if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable + { + double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason + newRange.lower = center-mRange.size()/2.0; + newRange.upper = center+mRange.size()/2.0; + } + setRange(newRange); + } +} + +/*! + Transforms \a value, in pixel coordinates of the QCustomPlot widget, to axis coordinates. +*/ +void QCPPolarAxisAngular::pixelToCoord(QPointF pixelPos, double &angleCoord, double &radiusCoord) const +{ + if (!mRadialAxes.isEmpty()) + mRadialAxes.first()->pixelToCoord(pixelPos, angleCoord, radiusCoord); + else + qDebug() << Q_FUNC_INFO << "no radial axis configured"; +} + +/*! + Transforms \a value, in coordinates of the axis, to pixel coordinates of the QCustomPlot widget. +*/ +QPointF QCPPolarAxisAngular::coordToPixel(double angleCoord, double radiusCoord) const +{ + if (!mRadialAxes.isEmpty()) + { + return mRadialAxes.first()->coordToPixel(angleCoord, radiusCoord); + } else + { + qDebug() << Q_FUNC_INFO << "no radial axis configured"; + return QPointF(); + } +} + +/*! + Returns the part of the axis that is hit by \a pos (in pixels). The return value of this function + is independent of the user-selectable parts defined with \ref setSelectableParts. Further, this + function does not change the current selection state of the axis. + + If the axis is not visible (\ref setVisible), this function always returns \ref spNone. + + \see setSelectedParts, setSelectableParts, QCustomPlot::setInteractions +*/ +QCPPolarAxisAngular::SelectablePart QCPPolarAxisAngular::getPartAt(const QPointF &pos) const +{ + Q_UNUSED(pos) // TODO remove later + + if (!mVisible) + return spNone; + + /* + TODO: + if (mAxisPainter->axisSelectionBox().contains(pos.toPoint())) + return spAxis; + else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint())) + return spTickLabels; + else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint())) + return spAxisLabel; + else */ + return spNone; +} + +/* inherits documentation from base class */ +double QCPPolarAxisAngular::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + /* + if (!mParentPlot) return -1; + SelectablePart part = getPartAt(pos); + if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone) + return -1; + + if (details) + details->setValue(part); + return mParentPlot->selectionTolerance()*0.99; + */ + + Q_UNUSED(details) + + if (onlySelectable) + return -1; + + if (QRectF(mOuterRect).contains(pos)) + { + if (mParentPlot) + return mParentPlot->selectionTolerance()*0.99; + else + { + qDebug() << Q_FUNC_INFO << "parent plot not defined"; + return -1; + } + } else + return -1; +} + +/*! + This method is called automatically upon replot and doesn't need to be called by users of + QCPPolarAxisAngular. + + Calls the base class implementation to update the margins (see \ref QCPLayoutElement::update), + and finally passes the \ref rect to the inset layout (\ref insetLayout) and calls its + QCPInsetLayout::update function. + + \seebaseclassmethod +*/ +void QCPPolarAxisAngular::update(UpdatePhase phase) +{ + QCPLayoutElement::update(phase); + + switch (phase) + { + case upPreparation: + { + setupTickVectors(); + for (int i=0; isetupTickVectors(); + break; + } + case upLayout: + { + mCenter = mRect.center(); + mRadius = 0.5*qMin(qAbs(mRect.width()), qAbs(mRect.height())); + if (mRadius < 1) mRadius = 1; // prevent cases where radius might become 0 which causes trouble + for (int i=0; iupdateGeometry(mCenter, mRadius); + + mInsetLayout->setOuterRect(rect()); + break; + } + default: break; + } + + // pass update call on to inset layout (doesn't happen automatically, because QCPPolarAxis doesn't derive from QCPLayout): + mInsetLayout->update(phase); +} + +/* inherits documentation from base class */ +QList QCPPolarAxisAngular::elements(bool recursive) const +{ + QList result; + if (mInsetLayout) + { + result << mInsetLayout; + if (recursive) + result << mInsetLayout->elements(recursive); + } + return result; +} + +bool QCPPolarAxisAngular::removeGraph(QCPPolarGraph *graph) +{ + if (!mGraphs.contains(graph)) + { + qDebug() << Q_FUNC_INFO << "graph not in list:" << reinterpret_cast(graph); + return false; + } + + // remove plottable from legend: + graph->removeFromLegend(); + // remove plottable: + delete graph; + mGraphs.removeOne(graph); + return true; +} + +/* inherits documentation from base class */ +void QCPPolarAxisAngular::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes); +} + +/* inherits documentation from base class */ +void QCPPolarAxisAngular::draw(QCPPainter *painter) +{ + drawBackground(painter, mCenter, mRadius); + + // draw baseline circle: + painter->setPen(getBasePen()); + painter->drawEllipse(mCenter, mRadius, mRadius); + + // draw subticks: + if (!mSubTickVector.isEmpty()) + { + painter->setPen(getSubTickPen()); + for (int i=0; idrawLine(mCenter+mSubTickVectorCosSin.at(i)*(mRadius-mSubTickLengthIn), + mCenter+mSubTickVectorCosSin.at(i)*(mRadius+mSubTickLengthOut)); + } + } + + // draw ticks and labels: + if (!mTickVector.isEmpty()) + { + mLabelPainter.setAnchorReference(mCenter); + mLabelPainter.setFont(getTickLabelFont()); + mLabelPainter.setColor(getTickLabelColor()); + const QPen ticksPen = getTickPen(); + painter->setPen(ticksPen); + for (int i=0; idrawLine(mCenter+mTickVectorCosSin.at(i)*(mRadius-mTickLengthIn), outerTick); + // draw tick labels: + if (!mTickVectorLabels.isEmpty()) + { + if (i < mTickVectorLabels.count()-1 || (mTickVectorCosSin.at(i)-mTickVectorCosSin.first()).manhattanLength() > 5/180.0*M_PI) // skip last label if it's closer than approx 5 degrees to first + mLabelPainter.drawTickLabel(painter, outerTick, mTickVectorLabels.at(i)); + } + } + } +} + +/* inherits documentation from base class */ +QCP::Interaction QCPPolarAxisAngular::selectionCategory() const +{ + return QCP::iSelectAxes; +} + + +/*! + Sets \a pm as the axis background pixmap. The axis background pixmap will be drawn inside the + axis rect. Since axis rects place themselves on the "background" layer by default, the axis rect + backgrounds are usually drawn below everything else. + + For cases where the provided pixmap doesn't have the same size as the axis rect, scaling can be + enabled with \ref setBackgroundScaled and the scaling mode (i.e. whether and how the aspect ratio + is preserved) can be set with \ref setBackgroundScaledMode. To set all these options in one call, + consider using the overloaded version of this function. + + Below the pixmap, the axis rect may be optionally filled with a brush, if specified with \ref + setBackground(const QBrush &brush). + + \see setBackgroundScaled, setBackgroundScaledMode, setBackground(const QBrush &brush) +*/ +void QCPPolarAxisAngular::setBackground(const QPixmap &pm) +{ + mBackgroundPixmap = pm; + mScaledBackgroundPixmap = QPixmap(); +} + +/*! \overload + + Sets \a brush as the background brush. The axis rect background will be filled with this brush. + Since axis rects place themselves on the "background" layer by default, the axis rect backgrounds + are usually drawn below everything else. + + The brush will be drawn before (under) any background pixmap, which may be specified with \ref + setBackground(const QPixmap &pm). + + To disable drawing of a background brush, set \a brush to Qt::NoBrush. + + \see setBackground(const QPixmap &pm) +*/ +void QCPPolarAxisAngular::setBackground(const QBrush &brush) +{ + mBackgroundBrush = brush; +} + +/*! \overload + + Allows setting the background pixmap of the axis rect, whether it shall be scaled and how it + shall be scaled in one call. + + \see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode +*/ +void QCPPolarAxisAngular::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode) +{ + mBackgroundPixmap = pm; + mScaledBackgroundPixmap = QPixmap(); + mBackgroundScaled = scaled; + mBackgroundScaledMode = mode; +} + +/*! + Sets whether the axis background pixmap shall be scaled to fit the axis rect or not. If \a scaled + is set to true, you may control whether and how the aspect ratio of the original pixmap is + preserved with \ref setBackgroundScaledMode. + + Note that the scaled version of the original pixmap is buffered, so there is no performance + penalty on replots. (Except when the axis rect dimensions are changed continuously.) + + \see setBackground, setBackgroundScaledMode +*/ +void QCPPolarAxisAngular::setBackgroundScaled(bool scaled) +{ + mBackgroundScaled = scaled; +} + +/*! + If scaling of the axis background pixmap is enabled (\ref setBackgroundScaled), use this function to + define whether and how the aspect ratio of the original pixmap passed to \ref setBackground is preserved. + \see setBackground, setBackgroundScaled +*/ +void QCPPolarAxisAngular::setBackgroundScaledMode(Qt::AspectRatioMode mode) +{ + mBackgroundScaledMode = mode; +} + +void QCPPolarAxisAngular::setRangeDrag(bool enabled) +{ + mRangeDrag = enabled; +} + +void QCPPolarAxisAngular::setRangeZoom(bool enabled) +{ + mRangeZoom = enabled; +} + +void QCPPolarAxisAngular::setRangeZoomFactor(double factor) +{ + mRangeZoomFactor = factor; +} + + + + + + + +/*! + Sets the range of the axis. + + This slot may be connected with the \ref rangeChanged signal of another axis so this axis + is always synchronized with the other axis range, when it changes. + + To invert the direction of an axis, use \ref setRangeReversed. +*/ +void QCPPolarAxisAngular::setRange(const QCPRange &range) +{ + if (range.lower == mRange.lower && range.upper == mRange.upper) + return; + + if (!QCPRange::validRange(range)) return; + QCPRange oldRange = mRange; + mRange = range.sanitizedForLinScale(); + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface. + (When \ref QCustomPlot::setInteractions contains iSelectAxes.) + + However, even when \a selectable is set to a value not allowing the selection of a specific part, + it is still possible to set the selection of this part manually, by calling \ref setSelectedParts + directly. + + \see SelectablePart, setSelectedParts +*/ +void QCPPolarAxisAngular::setSelectableParts(const SelectableParts &selectable) +{ + if (mSelectableParts != selectable) + { + mSelectableParts = selectable; + emit selectableChanged(mSelectableParts); + } +} + +/*! + Sets the selected state of the respective axis parts described by \ref SelectablePart. When a part + is selected, it uses a different pen/font. + + The entire selection mechanism for axes is handled automatically when \ref + QCustomPlot::setInteractions contains iSelectAxes. You only need to call this function when you + wish to change the selection state manually. + + This function can change the selection state of a part, independent of the \ref setSelectableParts setting. + + emits the \ref selectionChanged signal when \a selected is different from the previous selection state. + + \see SelectablePart, setSelectableParts, selectTest, setSelectedBasePen, setSelectedTickPen, setSelectedSubTickPen, + setSelectedTickLabelFont, setSelectedLabelFont, setSelectedTickLabelColor, setSelectedLabelColor +*/ +void QCPPolarAxisAngular::setSelectedParts(const SelectableParts &selected) +{ + if (mSelectedParts != selected) + { + mSelectedParts = selected; + emit selectionChanged(mSelectedParts); + } +} + +/*! + \overload + + Sets the lower and upper bound of the axis range. + + To invert the direction of an axis, use \ref setRangeReversed. + + There is also a slot to set a range, see \ref setRange(const QCPRange &range). +*/ +void QCPPolarAxisAngular::setRange(double lower, double upper) +{ + if (lower == mRange.lower && upper == mRange.upper) + return; + + if (!QCPRange::validRange(lower, upper)) return; + QCPRange oldRange = mRange; + mRange.lower = lower; + mRange.upper = upper; + mRange = mRange.sanitizedForLinScale(); + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + \overload + + Sets the range of the axis. + + The \a position coordinate indicates together with the \a alignment parameter, where the new + range will be positioned. \a size defines the size of the new axis range. \a alignment may be + Qt::AlignLeft, Qt::AlignRight or Qt::AlignCenter. This will cause the left border, right border, + or center of the range to be aligned with \a position. Any other values of \a alignment will + default to Qt::AlignCenter. +*/ +void QCPPolarAxisAngular::setRange(double position, double size, Qt::AlignmentFlag alignment) +{ + if (alignment == Qt::AlignLeft) + setRange(position, position+size); + else if (alignment == Qt::AlignRight) + setRange(position-size, position); + else // alignment == Qt::AlignCenter + setRange(position-size/2.0, position+size/2.0); +} + +/*! + Sets the lower bound of the axis range. The upper bound is not changed. + \see setRange +*/ +void QCPPolarAxisAngular::setRangeLower(double lower) +{ + if (mRange.lower == lower) + return; + + QCPRange oldRange = mRange; + mRange.lower = lower; + mRange = mRange.sanitizedForLinScale(); + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Sets the upper bound of the axis range. The lower bound is not changed. + \see setRange +*/ +void QCPPolarAxisAngular::setRangeUpper(double upper) +{ + if (mRange.upper == upper) + return; + + QCPRange oldRange = mRange; + mRange.upper = upper; + mRange = mRange.sanitizedForLinScale(); + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Sets whether the axis range (direction) is displayed reversed. Normally, the values on horizontal + axes increase left to right, on vertical axes bottom to top. When \a reversed is set to true, the + direction of increasing values is inverted. + + Note that the range and data interface stays the same for reversed axes, e.g. the \a lower part + of the \ref setRange interface will still reference the mathematically smaller number than the \a + upper part. +*/ +void QCPPolarAxisAngular::setRangeReversed(bool reversed) +{ + mRangeReversed = reversed; +} + +void QCPPolarAxisAngular::setAngle(double degrees) +{ + mAngle = degrees; + mAngleRad = mAngle/180.0*M_PI; +} + +/*! + The axis ticker is responsible for generating the tick positions and tick labels. See the + documentation of QCPAxisTicker for details on how to work with axis tickers. + + You can change the tick positioning/labeling behaviour of this axis by setting a different + QCPAxisTicker subclass using this method. If you only wish to modify the currently installed axis + ticker, access it via \ref ticker. + + Since the ticker is stored in the axis as a shared pointer, multiple axes may share the same axis + ticker simply by passing the same shared pointer to multiple axes. + + \see ticker +*/ +void QCPPolarAxisAngular::setTicker(QSharedPointer ticker) +{ + if (ticker) + mTicker = ticker; + else + qDebug() << Q_FUNC_INFO << "can not set 0 as axis ticker"; + // no need to invalidate margin cache here because produced tick labels are checked for changes in setupTickVector +} + +/*! + Sets whether tick marks are displayed. + + Note that setting \a show to false does not imply that tick labels are invisible, too. To achieve + that, see \ref setTickLabels. + + \see setSubTicks +*/ +void QCPPolarAxisAngular::setTicks(bool show) +{ + if (mTicks != show) + { + mTicks = show; + //mCachedMarginValid = false; + } +} + +/*! + Sets whether tick labels are displayed. Tick labels are the numbers drawn next to tick marks. +*/ +void QCPPolarAxisAngular::setTickLabels(bool show) +{ + if (mTickLabels != show) + { + mTickLabels = show; + //mCachedMarginValid = false; + if (!mTickLabels) + mTickVectorLabels.clear(); + } +} + +/*! + Sets the distance between the axis base line (including any outward ticks) and the tick labels. + \see setLabelPadding, setPadding +*/ +void QCPPolarAxisAngular::setTickLabelPadding(int padding) +{ + mLabelPainter.setPadding(padding); +} + +/*! + Sets the font of the tick labels. + + \see setTickLabels, setTickLabelColor +*/ +void QCPPolarAxisAngular::setTickLabelFont(const QFont &font) +{ + mTickLabelFont = font; +} + +/*! + Sets the color of the tick labels. + + \see setTickLabels, setTickLabelFont +*/ +void QCPPolarAxisAngular::setTickLabelColor(const QColor &color) +{ + mTickLabelColor = color; +} + +/*! + Sets the rotation of the tick labels. If \a degrees is zero, the labels are drawn normally. Else, + the tick labels are drawn rotated by \a degrees clockwise. The specified angle is bound to values + from -90 to 90 degrees. + + If \a degrees is exactly -90, 0 or 90, the tick labels are centered on the tick coordinate. For + other angles, the label is drawn with an offset such that it seems to point toward or away from + the tick mark. +*/ +void QCPPolarAxisAngular::setTickLabelRotation(double degrees) +{ + mLabelPainter.setRotation(degrees); +} + +void QCPPolarAxisAngular::setTickLabelMode(LabelMode mode) +{ + switch (mode) + { + case lmUpright: mLabelPainter.setAnchorMode(QCPLabelPainterPrivate::amSkewedUpright); break; + case lmRotated: mLabelPainter.setAnchorMode(QCPLabelPainterPrivate::amSkewedRotated); break; + } +} + +/*! + Sets the number format for the numbers in tick labels. This \a formatCode is an extended version + of the format code used e.g. by QString::number() and QLocale::toString(). For reference about + that, see the "Argument Formats" section in the detailed description of the QString class. + + \a formatCode is a string of one, two or three characters. The first character is identical to + the normal format code used by Qt. In short, this means: 'e'/'E' scientific format, 'f' fixed + format, 'g'/'G' scientific or fixed, whichever is shorter. + + The second and third characters are optional and specific to QCustomPlot:\n If the first char was + 'e' or 'g', numbers are/might be displayed in the scientific format, e.g. "5.5e9", which might be + visually unappealing in a plot. So when the second char of \a formatCode is set to 'b' (for + "beautiful"), those exponential numbers are formatted in a more natural way, i.e. "5.5 + [multiplication sign] 10 [superscript] 9". By default, the multiplication sign is a centered dot. + If instead a cross should be shown (as is usual in the USA), the third char of \a formatCode can + be set to 'c'. The inserted multiplication signs are the UTF-8 characters 215 (0xD7) for the + cross and 183 (0xB7) for the dot. + + Examples for \a formatCode: + \li \c g normal format code behaviour. If number is small, fixed format is used, if number is large, + normal scientific format is used + \li \c gb If number is small, fixed format is used, if number is large, scientific format is used with + beautifully typeset decimal powers and a dot as multiplication sign + \li \c ebc All numbers are in scientific format with beautifully typeset decimal power and a cross as + multiplication sign + \li \c fb illegal format code, since fixed format doesn't support (or need) beautifully typeset decimal + powers. Format code will be reduced to 'f'. + \li \c hello illegal format code, since first char is not 'e', 'E', 'f', 'g' or 'G'. Current format + code will not be changed. +*/ +void QCPPolarAxisAngular::setNumberFormat(const QString &formatCode) +{ + if (formatCode.isEmpty()) + { + qDebug() << Q_FUNC_INFO << "Passed formatCode is empty"; + return; + } + //mCachedMarginValid = false; + + // interpret first char as number format char: + QString allowedFormatChars(QLatin1String("eEfgG")); + if (allowedFormatChars.contains(formatCode.at(0))) + { + mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1()); + } else + { + qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode; + return; + } + + if (formatCode.length() < 2) + { + mNumberBeautifulPowers = false; + mNumberMultiplyCross = false; + } else + { + // interpret second char as indicator for beautiful decimal powers: + if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g'))) + mNumberBeautifulPowers = true; + else + qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode; + + if (formatCode.length() < 3) + { + mNumberMultiplyCross = false; + } else + { + // interpret third char as indicator for dot or cross multiplication symbol: + if (formatCode.at(2) == QLatin1Char('c')) + mNumberMultiplyCross = true; + else if (formatCode.at(2) == QLatin1Char('d')) + mNumberMultiplyCross = false; + else + qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode; + } + } + mLabelPainter.setSubstituteExponent(mNumberBeautifulPowers); + mLabelPainter.setMultiplicationSymbol(mNumberMultiplyCross ? QCPLabelPainterPrivate::SymbolCross : QCPLabelPainterPrivate::SymbolDot); +} + +/*! + Sets the precision of the tick label numbers. See QLocale::toString(double i, char f, int prec) + for details. The effect of precisions are most notably for number Formats starting with 'e', see + \ref setNumberFormat +*/ +void QCPPolarAxisAngular::setNumberPrecision(int precision) +{ + if (mNumberPrecision != precision) + { + mNumberPrecision = precision; + //mCachedMarginValid = false; + } +} + +/*! + Sets the length of the ticks in pixels. \a inside is the length the ticks will reach inside the + plot and \a outside is the length they will reach outside the plot. If \a outside is greater than + zero, the tick labels and axis label will increase their distance to the axis accordingly, so + they won't collide with the ticks. + + \see setSubTickLength, setTickLengthIn, setTickLengthOut +*/ +void QCPPolarAxisAngular::setTickLength(int inside, int outside) +{ + setTickLengthIn(inside); + setTickLengthOut(outside); +} + +/*! + Sets the length of the inward ticks in pixels. \a inside is the length the ticks will reach + inside the plot. + + \see setTickLengthOut, setTickLength, setSubTickLength +*/ +void QCPPolarAxisAngular::setTickLengthIn(int inside) +{ + if (mTickLengthIn != inside) + { + mTickLengthIn = inside; + } +} + +/*! + Sets the length of the outward ticks in pixels. \a outside is the length the ticks will reach + outside the plot. If \a outside is greater than zero, the tick labels and axis label will + increase their distance to the axis accordingly, so they won't collide with the ticks. + + \see setTickLengthIn, setTickLength, setSubTickLength +*/ +void QCPPolarAxisAngular::setTickLengthOut(int outside) +{ + if (mTickLengthOut != outside) + { + mTickLengthOut = outside; + //mCachedMarginValid = false; // only outside tick length can change margin + } +} + +/*! + Sets whether sub tick marks are displayed. + + Sub ticks are only potentially visible if (major) ticks are also visible (see \ref setTicks) + + \see setTicks +*/ +void QCPPolarAxisAngular::setSubTicks(bool show) +{ + if (mSubTicks != show) + { + mSubTicks = show; + //mCachedMarginValid = false; + } +} + +/*! + Sets the length of the subticks in pixels. \a inside is the length the subticks will reach inside + the plot and \a outside is the length they will reach outside the plot. If \a outside is greater + than zero, the tick labels and axis label will increase their distance to the axis accordingly, + so they won't collide with the ticks. + + \see setTickLength, setSubTickLengthIn, setSubTickLengthOut +*/ +void QCPPolarAxisAngular::setSubTickLength(int inside, int outside) +{ + setSubTickLengthIn(inside); + setSubTickLengthOut(outside); +} + +/*! + Sets the length of the inward subticks in pixels. \a inside is the length the subticks will reach inside + the plot. + + \see setSubTickLengthOut, setSubTickLength, setTickLength +*/ +void QCPPolarAxisAngular::setSubTickLengthIn(int inside) +{ + if (mSubTickLengthIn != inside) + { + mSubTickLengthIn = inside; + } +} + +/*! + Sets the length of the outward subticks in pixels. \a outside is the length the subticks will reach + outside the plot. If \a outside is greater than zero, the tick labels will increase their + distance to the axis accordingly, so they won't collide with the ticks. + + \see setSubTickLengthIn, setSubTickLength, setTickLength +*/ +void QCPPolarAxisAngular::setSubTickLengthOut(int outside) +{ + if (mSubTickLengthOut != outside) + { + mSubTickLengthOut = outside; + //mCachedMarginValid = false; // only outside tick length can change margin + } +} + +/*! + Sets the pen, the axis base line is drawn with. + + \see setTickPen, setSubTickPen +*/ +void QCPPolarAxisAngular::setBasePen(const QPen &pen) +{ + mBasePen = pen; +} + +/*! + Sets the pen, tick marks will be drawn with. + + \see setTickLength, setBasePen +*/ +void QCPPolarAxisAngular::setTickPen(const QPen &pen) +{ + mTickPen = pen; +} + +/*! + Sets the pen, subtick marks will be drawn with. + + \see setSubTickCount, setSubTickLength, setBasePen +*/ +void QCPPolarAxisAngular::setSubTickPen(const QPen &pen) +{ + mSubTickPen = pen; +} + +/*! + Sets the font of the axis label. + + \see setLabelColor +*/ +void QCPPolarAxisAngular::setLabelFont(const QFont &font) +{ + if (mLabelFont != font) + { + mLabelFont = font; + //mCachedMarginValid = false; + } +} + +/*! + Sets the color of the axis label. + + \see setLabelFont +*/ +void QCPPolarAxisAngular::setLabelColor(const QColor &color) +{ + mLabelColor = color; +} + +/*! + Sets the text of the axis label that will be shown below/above or next to the axis, depending on + its orientation. To disable axis labels, pass an empty string as \a str. +*/ +void QCPPolarAxisAngular::setLabel(const QString &str) +{ + if (mLabel != str) + { + mLabel = str; + //mCachedMarginValid = false; + } +} + +/*! + Sets the distance between the tick labels and the axis label. + + \see setTickLabelPadding, setPadding +*/ +void QCPPolarAxisAngular::setLabelPadding(int padding) +{ + if (mLabelPadding != padding) + { + mLabelPadding = padding; + //mCachedMarginValid = false; + } +} + +/*! + Sets the font that is used for tick labels when they are selected. + + \see setTickLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisAngular::setSelectedTickLabelFont(const QFont &font) +{ + if (font != mSelectedTickLabelFont) + { + mSelectedTickLabelFont = font; + // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts + } +} + +/*! + Sets the font that is used for the axis label when it is selected. + + \see setLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisAngular::setSelectedLabelFont(const QFont &font) +{ + mSelectedLabelFont = font; + // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts +} + +/*! + Sets the color that is used for tick labels when they are selected. + + \see setTickLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisAngular::setSelectedTickLabelColor(const QColor &color) +{ + if (color != mSelectedTickLabelColor) + { + mSelectedTickLabelColor = color; + } +} + +/*! + Sets the color that is used for the axis label when it is selected. + + \see setLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisAngular::setSelectedLabelColor(const QColor &color) +{ + mSelectedLabelColor = color; +} + +/*! + Sets the pen that is used to draw the axis base line when selected. + + \see setBasePen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisAngular::setSelectedBasePen(const QPen &pen) +{ + mSelectedBasePen = pen; +} + +/*! + Sets the pen that is used to draw the (major) ticks when selected. + + \see setTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisAngular::setSelectedTickPen(const QPen &pen) +{ + mSelectedTickPen = pen; +} + +/*! + Sets the pen that is used to draw the subticks when selected. + + \see setSubTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisAngular::setSelectedSubTickPen(const QPen &pen) +{ + mSelectedSubTickPen = pen; +} + +/*! \internal + + Draws the background of this axis rect. It may consist of a background fill (a QBrush) and a + pixmap. + + If a brush was given via \ref setBackground(const QBrush &brush), this function first draws an + according filling inside the axis rect with the provided \a painter. + + Then, if a pixmap was provided via \ref setBackground, this function buffers the scaled version + depending on \ref setBackgroundScaled and \ref setBackgroundScaledMode and then draws it inside + the axis rect with the provided \a painter. The scaled version is buffered in + mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when + the axis rect has changed in a way that requires a rescale of the background pixmap (this is + dependent on the \ref setBackgroundScaledMode), or when a differend axis background pixmap was + set. + + \see setBackground, setBackgroundScaled, setBackgroundScaledMode +*/ +void QCPPolarAxisAngular::drawBackground(QCPPainter *painter, const QPointF ¢er, double radius) +{ + // draw background fill (don't use circular clip, looks bad): + if (mBackgroundBrush != Qt::NoBrush) + { + QPainterPath ellipsePath; + ellipsePath.addEllipse(center, radius, radius); + painter->fillPath(ellipsePath, mBackgroundBrush); + } + + // draw background pixmap (on top of fill, if brush specified): + if (!mBackgroundPixmap.isNull()) + { + QRegion clipCircle(center.x()-radius, center.y()-radius, qRound(2*radius), qRound(2*radius), QRegion::Ellipse); + QRegion originalClip = painter->clipRegion(); + painter->setClipRegion(clipCircle); + if (mBackgroundScaled) + { + // check whether mScaledBackground needs to be updated: + QSize scaledSize(mBackgroundPixmap.size()); + scaledSize.scale(mRect.size(), mBackgroundScaledMode); + if (mScaledBackgroundPixmap.size() != scaledSize) + mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation); + painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect()); + } else + { + painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height())); + } + painter->setClipRegion(originalClip); + } +} + +/*! \internal + + Prepares the internal tick vector, sub tick vector and tick label vector. This is done by calling + QCPAxisTicker::generate on the currently installed ticker. + + If a change in the label text/count is detected, the cached axis margin is invalidated to make + sure the next margin calculation recalculates the label sizes and returns an up-to-date value. +*/ +void QCPPolarAxisAngular::setupTickVectors() +{ + if (!mParentPlot) return; + if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return; + + mSubTickVector.clear(); // since we might not pass it to mTicker->generate(), and we don't want old data in there + mTicker->generate(mRange, mParentPlot->locale(), mNumberFormatChar, mNumberPrecision, mTickVector, mSubTicks ? &mSubTickVector : 0, mTickLabels ? &mTickVectorLabels : 0); + + // fill cos/sin buffers which will be used by draw() and QCPPolarGrid::draw(), so we don't have to calculate it twice: + mTickVectorCosSin.resize(mTickVector.size()); + for (int i=0; ibuttons() & Qt::LeftButton) + { + mDragging = true; + // initialize antialiasing backup in case we start dragging: + if (mParentPlot->noAntialiasingOnDrag()) + { + mAADragBackup = mParentPlot->antialiasedElements(); + mNotAADragBackup = mParentPlot->notAntialiasedElements(); + } + // Mouse range dragging interaction: + if (mParentPlot->interactions().testFlag(QCP::iRangeDrag)) + { + mDragAngularStart = range(); + mDragRadialStart.clear(); + for (int i=0; irange()); + } + } +} + +/*! \internal + + Event handler for when the mouse is moved on the axis rect. If range dragging was activated in a + preceding \ref mousePressEvent, the range is moved accordingly. + + \see mousePressEvent, mouseReleaseEvent +*/ +void QCPPolarAxisAngular::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) +{ + Q_UNUSED(startPos) + bool doReplot = false; + // Mouse range dragging interaction: + if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag)) + { + if (mRangeDrag) + { + doReplot = true; + double angleCoordStart, radiusCoordStart; + double angleCoord, radiusCoord; + pixelToCoord(startPos, angleCoordStart, radiusCoordStart); + pixelToCoord(event->pos(), angleCoord, radiusCoord); + double diff = angleCoordStart - angleCoord; + setRange(mDragAngularStart.lower+diff, mDragAngularStart.upper+diff); + } + + for (int i=0; irangeDrag()) + continue; + doReplot = true; + double angleCoordStart, radiusCoordStart; + double angleCoord, radiusCoord; + ax->pixelToCoord(startPos, angleCoordStart, radiusCoordStart); + ax->pixelToCoord(event->pos(), angleCoord, radiusCoord); + if (ax->scaleType() == QCPPolarAxisRadial::stLinear) + { + double diff = radiusCoordStart - radiusCoord; + ax->setRange(mDragRadialStart.at(i).lower+diff, mDragRadialStart.at(i).upper+diff); + } else if (ax->scaleType() == QCPPolarAxisRadial::stLogarithmic) + { + if (radiusCoord != 0) + { + double diff = radiusCoordStart/radiusCoord; + ax->setRange(mDragRadialStart.at(i).lower*diff, mDragRadialStart.at(i).upper*diff); + } + } + } + + if (doReplot) // if either vertical or horizontal drag was enabled, do a replot + { + if (mParentPlot->noAntialiasingOnDrag()) + mParentPlot->setNotAntialiasedElements(QCP::aeAll); + mParentPlot->replot(QCustomPlot::rpQueuedReplot); + } + } +} + +/* inherits documentation from base class */ +void QCPPolarAxisAngular::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) +{ + Q_UNUSED(event) + Q_UNUSED(startPos) + mDragging = false; + if (mParentPlot->noAntialiasingOnDrag()) + { + mParentPlot->setAntialiasedElements(mAADragBackup); + mParentPlot->setNotAntialiasedElements(mNotAADragBackup); + } +} + +/*! \internal + + Event handler for mouse wheel events. If rangeZoom is Qt::Horizontal, Qt::Vertical or both, the + ranges of the axes defined as rangeZoomHorzAxis and rangeZoomVertAxis are scaled. The center of + the scaling operation is the current cursor position inside the axis rect. The scaling factor is + dependent on the mouse wheel delta (which direction the wheel was rotated) to provide a natural + zooming feel. The Strength of the zoom can be controlled via \ref setRangeZoomFactor. + + Note, that event->delta() is usually +/-120 for single rotation steps. However, if the mouse + wheel is turned rapidly, many steps may bunch up to one event, so the event->delta() may then be + multiples of 120. This is taken into account here, by calculating \a wheelSteps and using it as + exponent of the range zoom factor. This takes care of the wheel direction automatically, by + inverting the factor, when the wheel step is negative (f^-1 = 1/f). +*/ +void QCPPolarAxisAngular::wheelEvent(QWheelEvent *event) +{ + bool doReplot = false; + // Mouse range zooming interaction: + if (mParentPlot->interactions().testFlag(QCP::iRangeZoom)) + { +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) + const double delta = event->delta(); +#else + const double delta = event->angleDelta().y(); +#endif + +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + const QPointF pos = event->pos(); +#else + const QPointF pos = event->position(); +#endif + const double wheelSteps = delta/120.0; // a single step delta is +/-120 usually + if (mRangeZoom) + { + double angleCoord, radiusCoord; + pixelToCoord(pos, angleCoord, radiusCoord); + scaleRange(qPow(mRangeZoomFactor, wheelSteps), angleCoord); + } + + for (int i=0; irangeZoom()) + continue; + doReplot = true; + double angleCoord, radiusCoord; + ax->pixelToCoord(pos, angleCoord, radiusCoord); + ax->scaleRange(qPow(ax->rangeZoomFactor(), wheelSteps), radiusCoord); + } + } + if (doReplot) + mParentPlot->replot(); +} + +bool QCPPolarAxisAngular::registerPolarGraph(QCPPolarGraph *graph) +{ + if (mGraphs.contains(graph)) + { + qDebug() << Q_FUNC_INFO << "plottable already added:" << reinterpret_cast(graph); + return false; + } + if (graph->keyAxis() != this) + { + qDebug() << Q_FUNC_INFO << "plottable not created with this as axis:" << reinterpret_cast(graph); + return false; + } + + mGraphs.append(graph); + // possibly add plottable to legend: + if (mParentPlot->autoAddPlottableToLegend()) + graph->addToLegend(); + if (!graph->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor) + graph->setLayer(mParentPlot->currentLayer()); + return true; +} +/* end of 'src/polar/layoutelement-angularaxis.cpp' */ + + +/* including file 'src/polar/polargrid.cpp' */ +/* modified 2021-03-29T02:30:44, size 7493 */ + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPPolarGrid +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPPolarGrid + \brief The grid in both angular and radial dimensions for polar plots + + \warning In this QCustomPlot version, polar plots are a tech preview. Expect documentation and + functionality to be incomplete, as well as changing public interfaces in the future. +*/ + +/*! + Creates a QCPPolarGrid instance and sets default values. + + You shouldn't instantiate grids on their own, since every axis brings its own grid. +*/ +QCPPolarGrid::QCPPolarGrid(QCPPolarAxisAngular *parentAxis) : + QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis), + mType(gtNone), + mSubGridType(gtNone), + mAntialiasedSubGrid(true), + mAntialiasedZeroLine(true), + mParentAxis(parentAxis) +{ + // warning: this is called in QCPPolarAxisAngular constructor, so parentAxis members should not be accessed/called + setParent(parentAxis); + setType(gtAll); + setSubGridType(gtNone); + + setAngularPen(QPen(QColor(200,200,200), 0, Qt::DotLine)); + setAngularSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine)); + + setRadialPen(QPen(QColor(200,200,200), 0, Qt::DotLine)); + setRadialSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine)); + setRadialZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine)); + + setAntialiased(true); +} + +void QCPPolarGrid::setRadialAxis(QCPPolarAxisRadial *axis) +{ + mRadialAxis = axis; +} + +void QCPPolarGrid::setType(GridTypes type) +{ + mType = type; +} + +void QCPPolarGrid::setSubGridType(GridTypes type) +{ + mSubGridType = type; +} + +/*! + Sets whether sub grid lines are drawn antialiased. +*/ +void QCPPolarGrid::setAntialiasedSubGrid(bool enabled) +{ + mAntialiasedSubGrid = enabled; +} + +/*! + Sets whether zero lines are drawn antialiased. +*/ +void QCPPolarGrid::setAntialiasedZeroLine(bool enabled) +{ + mAntialiasedZeroLine = enabled; +} + +/*! + Sets the pen with which (major) grid lines are drawn. +*/ +void QCPPolarGrid::setAngularPen(const QPen &pen) +{ + mAngularPen = pen; +} + +/*! + Sets the pen with which sub grid lines are drawn. +*/ +void QCPPolarGrid::setAngularSubGridPen(const QPen &pen) +{ + mAngularSubGridPen = pen; +} + +void QCPPolarGrid::setRadialPen(const QPen &pen) +{ + mRadialPen = pen; +} + +void QCPPolarGrid::setRadialSubGridPen(const QPen &pen) +{ + mRadialSubGridPen = pen; +} + +void QCPPolarGrid::setRadialZeroLinePen(const QPen &pen) +{ + mRadialZeroLinePen = pen; +} + +/*! \internal + + A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter + before drawing the major grid lines. + + This is the antialiasing state the painter passed to the \ref draw method is in by default. + + This function takes into account the local setting of the antialiasing flag as well as the + overrides set with \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. + + \see setAntialiased +*/ +void QCPPolarGrid::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aeGrid); +} + +/*! \internal + + Draws grid lines and sub grid lines at the positions of (sub) ticks of the parent axis, spanning + over the complete axis rect. Also draws the zero line, if appropriate (\ref setZeroLinePen). +*/ +void QCPPolarGrid::draw(QCPPainter *painter) +{ + if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; } + + const QPointF center = mParentAxis->mCenter; + const double radius = mParentAxis->mRadius; + + painter->setBrush(Qt::NoBrush); + // draw main angular grid: + if (mType.testFlag(gtAngular)) + drawAngularGrid(painter, center, radius, mParentAxis->mTickVectorCosSin, mAngularPen); + // draw main radial grid: + if (mType.testFlag(gtRadial) && mRadialAxis) + drawRadialGrid(painter, center, mRadialAxis->tickVector(), mRadialPen, mRadialZeroLinePen); + + applyAntialiasingHint(painter, mAntialiasedSubGrid, QCP::aeGrid); + // draw sub angular grid: + if (mSubGridType.testFlag(gtAngular)) + drawAngularGrid(painter, center, radius, mParentAxis->mSubTickVectorCosSin, mAngularSubGridPen); + // draw sub radial grid: + if (mSubGridType.testFlag(gtRadial) && mRadialAxis) + drawRadialGrid(painter, center, mRadialAxis->subTickVector(), mRadialSubGridPen); +} + +void QCPPolarGrid::drawRadialGrid(QCPPainter *painter, const QPointF ¢er, const QVector &coords, const QPen &pen, const QPen &zeroPen) +{ + if (!mRadialAxis) return; + if (coords.isEmpty()) return; + const bool drawZeroLine = zeroPen != Qt::NoPen; + const double zeroLineEpsilon = qAbs(coords.last()-coords.first())*1e-6; + + painter->setPen(pen); + for (int i=0; icoordToRadius(coords.at(i)); + if (drawZeroLine && qAbs(coords.at(i)) < zeroLineEpsilon) + { + applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine); + painter->setPen(zeroPen); + painter->drawEllipse(center, r, r); + painter->setPen(pen); + applyDefaultAntialiasingHint(painter); + } else + { + painter->drawEllipse(center, r, r); + } + } +} + +void QCPPolarGrid::drawAngularGrid(QCPPainter *painter, const QPointF ¢er, double radius, const QVector &ticksCosSin, const QPen &pen) +{ + if (ticksCosSin.isEmpty()) return; + + painter->setPen(pen); + for (int i=0; idrawLine(center, center+ticksCosSin.at(i)*radius); +} +/* end of 'src/polar/polargrid.cpp' */ + + +/* including file 'src/polar/polargraph.cpp' */ +/* modified 2021-03-29T02:30:44, size 44035 */ + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPPolarLegendItem +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPPolarLegendItem + \brief A legend item for polar plots + + \warning In this QCustomPlot version, polar plots are a tech preview. Expect documentation and + functionality to be incomplete, as well as changing public interfaces in the future. +*/ +QCPPolarLegendItem::QCPPolarLegendItem(QCPLegend *parent, QCPPolarGraph *graph) : + QCPAbstractLegendItem(parent), + mPolarGraph(graph) +{ + setAntialiased(false); +} + +void QCPPolarLegendItem::draw(QCPPainter *painter) +{ + if (!mPolarGraph) return; + painter->setFont(getFont()); + painter->setPen(QPen(getTextColor())); + QSizeF iconSize = mParentLegend->iconSize(); + QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPolarGraph->name()); + QRectF iconRect(mRect.topLeft(), iconSize); + int textHeight = qMax(textRect.height(), iconSize.height()); // if text has smaller height than icon, center text vertically in icon height, else align tops + painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPolarGraph->name()); + // draw icon: + painter->save(); + painter->setClipRect(iconRect, Qt::IntersectClip); + mPolarGraph->drawLegendIcon(painter, iconRect); + painter->restore(); + // draw icon border: + if (getIconBorderPen().style() != Qt::NoPen) + { + painter->setPen(getIconBorderPen()); + painter->setBrush(Qt::NoBrush); + int halfPen = qCeil(painter->pen().widthF()*0.5)+1; + painter->setClipRect(mOuterRect.adjusted(-halfPen, -halfPen, halfPen, halfPen)); // extend default clip rect so thicker pens (especially during selection) are not clipped + painter->drawRect(iconRect); + } +} + +QSize QCPPolarLegendItem::minimumOuterSizeHint() const +{ + if (!mPolarGraph) return QSize(); + QSize result(0, 0); + QRect textRect; + QFontMetrics fontMetrics(getFont()); + QSize iconSize = mParentLegend->iconSize(); + textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPolarGraph->name()); + result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width()); + result.setHeight(qMax(textRect.height(), iconSize.height())); + result.rwidth() += mMargins.left()+mMargins.right(); + result.rheight() += mMargins.top()+mMargins.bottom(); + return result; +} + +QPen QCPPolarLegendItem::getIconBorderPen() const +{ + return mSelected ? mParentLegend->selectedIconBorderPen() : mParentLegend->iconBorderPen(); +} + +QColor QCPPolarLegendItem::getTextColor() const +{ + return mSelected ? mSelectedTextColor : mTextColor; +} + +QFont QCPPolarLegendItem::getFont() const +{ + return mSelected ? mSelectedFont : mFont; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPPolarGraph +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPPolarGraph + \brief A radial graph used to display data in polar plots + + \warning In this QCustomPlot version, polar plots are a tech preview. Expect documentation and + functionality to be incomplete, as well as changing public interfaces in the future. +*/ + +/* start of documentation of inline functions */ + +// TODO + +/* end of documentation of inline functions */ + +/*! + Constructs a graph which uses \a keyAxis as its angular and \a valueAxis as its radial axis. \a + keyAxis and \a valueAxis must reside in the same QCustomPlot, and the radial axis must be + associated with the angular axis. If either of these restrictions is violated, a corresponding + message is printed to the debug output (qDebug), the construction is not aborted, though. + + The created QCPPolarGraph is automatically registered with the QCustomPlot instance inferred from + \a keyAxis. This QCustomPlot instance takes ownership of the QCPPolarGraph, so do not delete it + manually but use QCPPolarAxisAngular::removeGraph() instead. + + To directly create a QCPPolarGraph inside a plot, you shoud use the QCPPolarAxisAngular::addGraph + method. +*/ +QCPPolarGraph::QCPPolarGraph(QCPPolarAxisAngular *keyAxis, QCPPolarAxisRadial *valueAxis) : + QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis), + mDataContainer(new QCPGraphDataContainer), + mName(), + mAntialiasedFill(true), + mAntialiasedScatters(true), + mPen(Qt::black), + mBrush(Qt::NoBrush), + mPeriodic(true), + mKeyAxis(keyAxis), + mValueAxis(valueAxis), + mSelectable(QCP::stWhole) + //mSelectionDecorator(0) // TODO +{ + if (keyAxis->parentPlot() != valueAxis->parentPlot()) + qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis."; + + mKeyAxis->registerPolarGraph(this); + + //setSelectionDecorator(new QCPSelectionDecorator); // TODO + + setPen(QPen(Qt::blue, 0)); + setBrush(Qt::NoBrush); + setLineStyle(lsLine); +} + +QCPPolarGraph::~QCPPolarGraph() +{ + /* TODO + if (mSelectionDecorator) + { + delete mSelectionDecorator; + mSelectionDecorator = 0; + } + */ +} + +/*! + The name is the textual representation of this plottable as it is displayed in the legend + (\ref QCPLegend). It may contain any UTF-8 characters, including newlines. +*/ +void QCPPolarGraph::setName(const QString &name) +{ + mName = name; +} + +/*! + Sets whether fills of this plottable are drawn antialiased or not. + + Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. +*/ +void QCPPolarGraph::setAntialiasedFill(bool enabled) +{ + mAntialiasedFill = enabled; +} + +/*! + Sets whether the scatter symbols of this plottable are drawn antialiased or not. + + Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. +*/ +void QCPPolarGraph::setAntialiasedScatters(bool enabled) +{ + mAntialiasedScatters = enabled; +} + +/*! + The pen is used to draw basic lines that make up the plottable representation in the + plot. + + For example, the \ref QCPGraph subclass draws its graph lines with this pen. + + \see setBrush +*/ +void QCPPolarGraph::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + The brush is used to draw basic fills of the plottable representation in the + plot. The Fill can be a color, gradient or texture, see the usage of QBrush. + + For example, the \ref QCPGraph subclass draws the fill under the graph with this brush, when + it's not set to Qt::NoBrush. + + \see setPen +*/ +void QCPPolarGraph::setBrush(const QBrush &brush) +{ + mBrush = brush; +} + +void QCPPolarGraph::setPeriodic(bool enabled) +{ + mPeriodic = enabled; +} + +/*! + The key axis of a plottable can be set to any axis of a QCustomPlot, as long as it is orthogonal + to the plottable's value axis. This function performs no checks to make sure this is the case. + The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and the + y-axis (QCustomPlot::yAxis) as value axis. + + Normally, the key and value axes are set in the constructor of the plottable (or \ref + QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface). + + \see setValueAxis +*/ +void QCPPolarGraph::setKeyAxis(QCPPolarAxisAngular *axis) +{ + mKeyAxis = axis; +} + +/*! + The value axis of a plottable can be set to any axis of a QCustomPlot, as long as it is + orthogonal to the plottable's key axis. This function performs no checks to make sure this is the + case. The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and + the y-axis (QCustomPlot::yAxis) as value axis. + + Normally, the key and value axes are set in the constructor of the plottable (or \ref + QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface). + + \see setKeyAxis +*/ +void QCPPolarGraph::setValueAxis(QCPPolarAxisRadial *axis) +{ + mValueAxis = axis; +} + +/*! + Sets whether and to which granularity this plottable can be selected. + + A selection can happen by clicking on the QCustomPlot surface (When \ref + QCustomPlot::setInteractions contains \ref QCP::iSelectPlottables), by dragging a selection rect + (When \ref QCustomPlot::setSelectionRectMode is \ref QCP::srmSelect), or programmatically by + calling \ref setSelection. + + \see setSelection, QCP::SelectionType +*/ +void QCPPolarGraph::setSelectable(QCP::SelectionType selectable) +{ + if (mSelectable != selectable) + { + mSelectable = selectable; + QCPDataSelection oldSelection = mSelection; + mSelection.enforceType(mSelectable); + emit selectableChanged(mSelectable); + if (mSelection != oldSelection) + { + emit selectionChanged(selected()); + emit selectionChanged(mSelection); + } + } +} + +/*! + Sets which data ranges of this plottable are selected. Selected data ranges are drawn differently + (e.g. color) in the plot. This can be controlled via the selection decorator (see \ref + selectionDecorator). + + The entire selection mechanism for plottables is handled automatically when \ref + QCustomPlot::setInteractions contains iSelectPlottables. You only need to call this function when + you wish to change the selection state programmatically. + + Using \ref setSelectable you can further specify for each plottable whether and to which + granularity it is selectable. If \a selection is not compatible with the current \ref + QCP::SelectionType set via \ref setSelectable, the resulting selection will be adjusted + accordingly (see \ref QCPDataSelection::enforceType). + + emits the \ref selectionChanged signal when \a selected is different from the previous selection state. + + \see setSelectable, selectTest +*/ +void QCPPolarGraph::setSelection(QCPDataSelection selection) +{ + selection.enforceType(mSelectable); + if (mSelection != selection) + { + mSelection = selection; + emit selectionChanged(selected()); + emit selectionChanged(mSelection); + } +} + +/*! \overload + + Replaces the current data container with the provided \a data container. + + Since a QSharedPointer is used, multiple QCPPolarGraphs may share the same data container safely. + Modifying the data in the container will then affect all graphs that share the container. Sharing + can be achieved by simply exchanging the data containers wrapped in shared pointers: + \snippet documentation/doc-code-snippets/mainwindow.cpp QCPPolarGraph-datasharing-1 + + If you do not wish to share containers, but create a copy from an existing container, rather use + the \ref QCPDataContainer::set method on the graph's data container directly: + \snippet documentation/doc-code-snippets/mainwindow.cpp QCPPolarGraph-datasharing-2 + + \see addData +*/ +void QCPPolarGraph::setData(QSharedPointer data) +{ + mDataContainer = data; +} + +/*! \overload + + Replaces the current data with the provided points in \a keys and \a values. The provided + vectors should have equal length. Else, the number of added points will be the size of the + smallest vector. + + If you can guarantee that the passed data points are sorted by \a keys in ascending order, you + can set \a alreadySorted to true, to improve performance by saving a sorting run. + + \see addData +*/ +void QCPPolarGraph::setData(const QVector &keys, const QVector &values, bool alreadySorted) +{ + mDataContainer->clear(); + addData(keys, values, alreadySorted); +} + +/*! + Sets how the single data points are connected in the plot. For scatter-only plots, set \a ls to + \ref lsNone and \ref setScatterStyle to the desired scatter style. + + \see setScatterStyle +*/ +void QCPPolarGraph::setLineStyle(LineStyle ls) +{ + mLineStyle = ls; +} + +/*! + Sets the visual appearance of single data points in the plot. If set to \ref QCPScatterStyle::ssNone, no scatter points + are drawn (e.g. for line-only-plots with appropriate line style). + + \see QCPScatterStyle, setLineStyle +*/ +void QCPPolarGraph::setScatterStyle(const QCPScatterStyle &style) +{ + mScatterStyle = style; +} + +void QCPPolarGraph::addData(const QVector &keys, const QVector &values, bool alreadySorted) +{ + if (keys.size() != values.size()) + qDebug() << Q_FUNC_INFO << "keys and values have different sizes:" << keys.size() << values.size(); + const int n = qMin(keys.size(), values.size()); + QVector tempData(n); + QVector::iterator it = tempData.begin(); + const QVector::iterator itEnd = tempData.end(); + int i = 0; + while (it != itEnd) + { + it->key = keys[i]; + it->value = values[i]; + ++it; + ++i; + } + mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write +} + +void QCPPolarGraph::addData(double key, double value) +{ + mDataContainer->add(QCPGraphData(key, value)); +} + +/*! + Use this method to set an own QCPSelectionDecorator (subclass) instance. This allows you to + customize the visual representation of selected data ranges further than by using the default + QCPSelectionDecorator. + + The plottable takes ownership of the \a decorator. + + The currently set decorator can be accessed via \ref selectionDecorator. +*/ +/* +void QCPPolarGraph::setSelectionDecorator(QCPSelectionDecorator *decorator) +{ + if (decorator) + { + if (decorator->registerWithPlottable(this)) + { + if (mSelectionDecorator) // delete old decorator if necessary + delete mSelectionDecorator; + mSelectionDecorator = decorator; + } + } else if (mSelectionDecorator) // just clear decorator + { + delete mSelectionDecorator; + mSelectionDecorator = 0; + } +} +*/ + +void QCPPolarGraph::coordsToPixels(double key, double value, double &x, double &y) const +{ + if (mValueAxis) + { + const QPointF point = mValueAxis->coordToPixel(key, value); + x = point.x(); + y = point.y(); + } else + { + qDebug() << Q_FUNC_INFO << "invalid key or value axis"; + } +} + +const QPointF QCPPolarGraph::coordsToPixels(double key, double value) const +{ + if (mValueAxis) + { + return mValueAxis->coordToPixel(key, value); + } else + { + qDebug() << Q_FUNC_INFO << "invalid key or value axis"; + return QPointF(); + } +} + +void QCPPolarGraph::pixelsToCoords(double x, double y, double &key, double &value) const +{ + if (mValueAxis) + { + mValueAxis->pixelToCoord(QPointF(x, y), key, value); + } else + { + qDebug() << Q_FUNC_INFO << "invalid key or value axis"; + } +} + +void QCPPolarGraph::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const +{ + if (mValueAxis) + { + mValueAxis->pixelToCoord(pixelPos, key, value); + } else + { + qDebug() << Q_FUNC_INFO << "invalid key or value axis"; + } +} + +void QCPPolarGraph::rescaleAxes(bool onlyEnlarge) const +{ + rescaleKeyAxis(onlyEnlarge); + rescaleValueAxis(onlyEnlarge); +} + +void QCPPolarGraph::rescaleKeyAxis(bool onlyEnlarge) const +{ + QCPPolarAxisAngular *keyAxis = mKeyAxis.data(); + if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; } + + bool foundRange; + QCPRange newRange = getKeyRange(foundRange, QCP::sdBoth); + if (foundRange) + { + if (onlyEnlarge) + newRange.expand(keyAxis->range()); + if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable + { + double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason + newRange.lower = center-keyAxis->range().size()/2.0; + newRange.upper = center+keyAxis->range().size()/2.0; + } + keyAxis->setRange(newRange); + } +} + +void QCPPolarGraph::rescaleValueAxis(bool onlyEnlarge, bool inKeyRange) const +{ + QCPPolarAxisAngular *keyAxis = mKeyAxis.data(); + QCPPolarAxisRadial *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + + QCP::SignDomain signDomain = QCP::sdBoth; + if (valueAxis->scaleType() == QCPPolarAxisRadial::stLogarithmic) + signDomain = (valueAxis->range().upper < 0 ? QCP::sdNegative : QCP::sdPositive); + + bool foundRange; + QCPRange newRange = getValueRange(foundRange, signDomain, inKeyRange ? keyAxis->range() : QCPRange()); + if (foundRange) + { + if (onlyEnlarge) + newRange.expand(valueAxis->range()); + if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable + { + double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason + if (valueAxis->scaleType() == QCPPolarAxisRadial::stLinear) + { + newRange.lower = center-valueAxis->range().size()/2.0; + newRange.upper = center+valueAxis->range().size()/2.0; + } else // scaleType() == stLogarithmic + { + newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower); + newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower); + } + } + valueAxis->setRange(newRange); + } +} + +bool QCPPolarGraph::addToLegend(QCPLegend *legend) +{ + if (!legend) + { + qDebug() << Q_FUNC_INFO << "passed legend is null"; + return false; + } + if (legend->parentPlot() != mParentPlot) + { + qDebug() << Q_FUNC_INFO << "passed legend isn't in the same QCustomPlot as this plottable"; + return false; + } + + //if (!legend->hasItemWithPlottable(this)) // TODO + //{ + legend->addItem(new QCPPolarLegendItem(legend, this)); + return true; + //} else + // return false; +} + +bool QCPPolarGraph::addToLegend() +{ + if (!mParentPlot || !mParentPlot->legend) + return false; + else + return addToLegend(mParentPlot->legend); +} + +bool QCPPolarGraph::removeFromLegend(QCPLegend *legend) const +{ + if (!legend) + { + qDebug() << Q_FUNC_INFO << "passed legend is null"; + return false; + } + + + QCPPolarLegendItem *removableItem = 0; + for (int i=0; iitemCount(); ++i) // TODO: reduce this to code in QCPAbstractPlottable::removeFromLegend once unified + { + if (QCPPolarLegendItem *pli = qobject_cast(legend->item(i))) + { + if (pli->polarGraph() == this) + { + removableItem = pli; + break; + } + } + } + + if (removableItem) + return legend->removeItem(removableItem); + else + return false; +} + +bool QCPPolarGraph::removeFromLegend() const +{ + if (!mParentPlot || !mParentPlot->legend) + return false; + else + return removeFromLegend(mParentPlot->legend); +} + +double QCPPolarGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty()) + return -1; + if (!mKeyAxis || !mValueAxis) + return -1; + + if (mKeyAxis->rect().contains(pos.toPoint())) + { + QCPGraphDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd(); + double result = pointDistance(pos, closestDataPoint); + if (details) + { + int pointIndex = closestDataPoint-mDataContainer->constBegin(); + details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1))); + } + return result; + } else + return -1; +} + +/* inherits documentation from base class */ +QCPRange QCPPolarGraph::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const +{ + return mDataContainer->keyRange(foundRange, inSignDomain); +} + +/* inherits documentation from base class */ +QCPRange QCPPolarGraph::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const +{ + return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange); +} + +/* inherits documentation from base class */ +QRect QCPPolarGraph::clipRect() const +{ + if (mKeyAxis) + return mKeyAxis.data()->rect(); + else + return QRect(); +} + +void QCPPolarGraph::draw(QCPPainter *painter) +{ + if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + if (mKeyAxis.data()->range().size() <= 0 || mDataContainer->isEmpty()) return; + if (mLineStyle == lsNone && mScatterStyle.isNone()) return; + + painter->setClipRegion(mKeyAxis->exactClipRegion()); + + QVector lines, scatters; // line and (if necessary) scatter pixel coordinates will be stored here while iterating over segments + + // loop over and draw segments of unselected/selected data: + QList selectedSegments, unselectedSegments, allSegments; + getDataSegments(selectedSegments, unselectedSegments); + allSegments << unselectedSegments << selectedSegments; + for (int i=0; i= unselectedSegments.size(); + // get line pixel points appropriate to line style: + QCPDataRange lineDataRange = isSelectedSegment ? allSegments.at(i) : allSegments.at(i).adjusted(-1, 1); // unselected segments extend lines to bordering selected data point (safe to exceed total data bounds in first/last segment, getLines takes care) + getLines(&lines, lineDataRange); + + // check data validity if flag set: +#ifdef QCUSTOMPLOT_CHECK_DATA + QCPGraphDataContainer::const_iterator it; + for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it) + { + if (QCP::isInvalidData(it->key, it->value)) + qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "invalid." << "Plottable name:" << name(); + } +#endif + + // draw fill of graph: + //if (isSelectedSegment && mSelectionDecorator) + // mSelectionDecorator->applyBrush(painter); + //else + painter->setBrush(mBrush); + painter->setPen(Qt::NoPen); + drawFill(painter, &lines); + + + // draw line: + if (mLineStyle != lsNone) + { + //if (isSelectedSegment && mSelectionDecorator) + // mSelectionDecorator->applyPen(painter); + //else + painter->setPen(mPen); + painter->setBrush(Qt::NoBrush); + drawLinePlot(painter, lines); + } + + // draw scatters: + + QCPScatterStyle finalScatterStyle = mScatterStyle; + //if (isSelectedSegment && mSelectionDecorator) + // finalScatterStyle = mSelectionDecorator->getFinalScatterStyle(mScatterStyle); + if (!finalScatterStyle.isNone()) + { + getScatters(&scatters, allSegments.at(i)); + drawScatterPlot(painter, scatters, finalScatterStyle); + } + } + + // draw other selection decoration that isn't just line/scatter pens and brushes: + //if (mSelectionDecorator) + // mSelectionDecorator->drawDecoration(painter, selection()); +} + +QCP::Interaction QCPPolarGraph::selectionCategory() const +{ + return QCP::iSelectPlottables; +} + +void QCPPolarGraph::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aePlottables); +} + +/* inherits documentation from base class */ +void QCPPolarGraph::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) +{ + Q_UNUSED(event) + + if (mSelectable != QCP::stNone) + { + QCPDataSelection newSelection = details.value(); + QCPDataSelection selectionBefore = mSelection; + if (additive) + { + if (mSelectable == QCP::stWhole) // in whole selection mode, we toggle to no selection even if currently unselected point was hit + { + if (selected()) + setSelection(QCPDataSelection()); + else + setSelection(newSelection); + } else // in all other selection modes we toggle selections of homogeneously selected/unselected segments + { + if (mSelection.contains(newSelection)) // if entire newSelection is already selected, toggle selection + setSelection(mSelection-newSelection); + else + setSelection(mSelection+newSelection); + } + } else + setSelection(newSelection); + if (selectionStateChanged) + *selectionStateChanged = mSelection != selectionBefore; + } +} + +/* inherits documentation from base class */ +void QCPPolarGraph::deselectEvent(bool *selectionStateChanged) +{ + if (mSelectable != QCP::stNone) + { + QCPDataSelection selectionBefore = mSelection; + setSelection(QCPDataSelection()); + if (selectionStateChanged) + *selectionStateChanged = mSelection != selectionBefore; + } +} + +/*! \internal + + Draws lines between the points in \a lines, given in pixel coordinates. + + \see drawScatterPlot, drawImpulsePlot, QCPAbstractPlottable1D::drawPolyline +*/ +void QCPPolarGraph::drawLinePlot(QCPPainter *painter, const QVector &lines) const +{ + if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0) + { + applyDefaultAntialiasingHint(painter); + drawPolyline(painter, lines); + } +} + +/*! \internal + + Draws the fill of the graph using the specified \a painter, with the currently set brush. + + Depending on whether a normal fill or a channel fill (\ref setChannelFillGraph) is needed, \ref + getFillPolygon or \ref getChannelFillPolygon are used to find the according fill polygons. + + In order to handle NaN Data points correctly (the fill needs to be split into disjoint areas), + this method first determines a list of non-NaN segments with \ref getNonNanSegments, on which to + operate. In the channel fill case, \ref getOverlappingSegments is used to consolidate the non-NaN + segments of the two involved graphs, before passing the overlapping pairs to \ref + getChannelFillPolygon. + + Pass the points of this graph's line as \a lines, in pixel coordinates. + + \see drawLinePlot, drawImpulsePlot, drawScatterPlot +*/ +void QCPPolarGraph::drawFill(QCPPainter *painter, QVector *lines) const +{ + applyFillAntialiasingHint(painter); + if (painter->brush().style() != Qt::NoBrush && painter->brush().color().alpha() != 0) + painter->drawPolygon(QPolygonF(*lines)); +} + +/*! \internal + + Draws scatter symbols at every point passed in \a scatters, given in pixel coordinates. The + scatters will be drawn with \a painter and have the appearance as specified in \a style. + + \see drawLinePlot, drawImpulsePlot +*/ +void QCPPolarGraph::drawScatterPlot(QCPPainter *painter, const QVector &scatters, const QCPScatterStyle &style) const +{ + applyScattersAntialiasingHint(painter); + style.applyTo(painter, mPen); + for (int i=0; ifillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush); + } + // draw line vertically centered: + if (mLineStyle != lsNone) + { + applyDefaultAntialiasingHint(painter); + painter->setPen(mPen); + painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens + } + // draw scatter symbol: + if (!mScatterStyle.isNone()) + { + applyScattersAntialiasingHint(painter); + // scale scatter pixmap if it's too large to fit in legend icon rect: + if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height())) + { + QCPScatterStyle scaledStyle(mScatterStyle); + scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); + scaledStyle.applyTo(painter, mPen); + scaledStyle.drawShape(painter, QRectF(rect).center()); + } else + { + mScatterStyle.applyTo(painter, mPen); + mScatterStyle.drawShape(painter, QRectF(rect).center()); + } + } +} + +void QCPPolarGraph::applyFillAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiasedFill, QCP::aeFills); +} + +void QCPPolarGraph::applyScattersAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiasedScatters, QCP::aeScatters); +} + +double QCPPolarGraph::pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer::const_iterator &closestData) const +{ + closestData = mDataContainer->constEnd(); + if (mDataContainer->isEmpty()) + return -1.0; + if (mLineStyle == lsNone && mScatterStyle.isNone()) + return -1.0; + + // calculate minimum distances to graph data points and find closestData iterator: + double minDistSqr = (std::numeric_limits::max)(); + // determine which key range comes into question, taking selection tolerance around pos into account: + double posKeyMin, posKeyMax, dummy; + pixelsToCoords(pixelPoint-QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMin, dummy); + pixelsToCoords(pixelPoint+QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMax, dummy); + if (posKeyMin > posKeyMax) + qSwap(posKeyMin, posKeyMax); + // iterate over found data points and then choose the one with the shortest distance to pos: + QCPGraphDataContainer::const_iterator begin = mDataContainer->findBegin(posKeyMin, true); + QCPGraphDataContainer::const_iterator end = mDataContainer->findEnd(posKeyMax, true); + for (QCPGraphDataContainer::const_iterator it=begin; it!=end; ++it) + { + const double currentDistSqr = QCPVector2D(coordsToPixels(it->key, it->value)-pixelPoint).lengthSquared(); + if (currentDistSqr < minDistSqr) + { + minDistSqr = currentDistSqr; + closestData = it; + } + } + + // calculate distance to graph line if there is one (if so, will probably be smaller than distance to closest data point): + if (mLineStyle != lsNone) + { + // line displayed, calculate distance to line segments: + QVector lineData; + getLines(&lineData, QCPDataRange(0, dataCount())); + QCPVector2D p(pixelPoint); + for (int i=0; isize(); +} + +void QCPPolarGraph::getDataSegments(QList &selectedSegments, QList &unselectedSegments) const +{ + selectedSegments.clear(); + unselectedSegments.clear(); + if (mSelectable == QCP::stWhole) // stWhole selection type draws the entire plottable with selected style if mSelection isn't empty + { + if (selected()) + selectedSegments << QCPDataRange(0, dataCount()); + else + unselectedSegments << QCPDataRange(0, dataCount()); + } else + { + QCPDataSelection sel(selection()); + sel.simplify(); + selectedSegments = sel.dataRanges(); + unselectedSegments = sel.inverse(QCPDataRange(0, dataCount())).dataRanges(); + } +} + +void QCPPolarGraph::drawPolyline(QCPPainter *painter, const QVector &lineData) const { + // if drawing solid line and not in PDF, use much faster line drawing instead of polyline: + if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) && + painter->pen().style() == Qt::SolidLine && + !painter->modes().testFlag(QCPPainter::pmVectorized) && + !painter->modes().testFlag(QCPPainter::pmNoCaching)) + { + int i = 0; + bool lastIsNan = false; + const int lineDataSize = lineData.size(); + while (i < lineDataSize && (qIsNaN(lineData.at(i).y()) || qIsNaN(lineData.at(i).x()))) // make sure first point is not NaN + ++i; + ++i; // because drawing works in 1 point retrospect + while (i < lineDataSize) + { + if (!qIsNaN(lineData.at(i).y()) && !qIsNaN(lineData.at(i).x())) // NaNs create a gap in the line + { + if (!lastIsNan) + painter->drawLine(lineData.at(i-1), lineData.at(i)); + else + lastIsNan = false; + } else + lastIsNan = true; + ++i; + } + } else + { + int segmentStart = 0; + int i = 0; + const int lineDataSize = lineData.size(); + while (i < lineDataSize) + { + if (qIsNaN(lineData.at(i).y()) || qIsNaN(lineData.at(i).x()) || qIsInf(lineData.at(i).y())) // NaNs create a gap in the line. Also filter Infs which make drawPolyline block + { + painter->drawPolyline(lineData.constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point + segmentStart = i+1; + } + ++i; + } + // draw last segment: + painter->drawPolyline(lineData.constData()+segmentStart, lineDataSize-segmentStart); + } } -/*! - Sets the pen that will be used to draw the bracket. - - Note that when the style is \ref bsCalligraphic, only the color will be taken from the pen, the - stroke and width are ignored. To change the apparent stroke width of a calligraphic bracket, use - \ref setLength, which has a similar effect. - - \see setSelectedPen -*/ -void QCPItemBracket::setPen(const QPen &pen) +void QCPPolarGraph::getVisibleDataBounds(QCPGraphDataContainer::const_iterator &begin, QCPGraphDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const { - mPen = pen; + if (rangeRestriction.isEmpty()) + { + end = mDataContainer->constEnd(); + begin = end; + } else + { + QCPPolarAxisAngular *keyAxis = mKeyAxis.data(); + QCPPolarAxisRadial *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + // get visible data range: + if (mPeriodic) + { + begin = mDataContainer->constBegin(); + end = mDataContainer->constEnd(); + } else + { + begin = mDataContainer->findBegin(keyAxis->range().lower); + end = mDataContainer->findEnd(keyAxis->range().upper); + } + // limit lower/upperEnd to rangeRestriction: + mDataContainer->limitIteratorsToDataRange(begin, end, rangeRestriction); // this also ensures rangeRestriction outside data bounds doesn't break anything + } } -/*! - Sets the pen that will be used to draw the bracket when selected - - \see setPen, setSelected -*/ -void QCPItemBracket::setSelectedPen(const QPen &pen) -{ - mSelectedPen = pen; -} +/*! \internal -/*! - Sets the \a length in pixels how far the bracket extends in the direction towards the embraced - span of the bracket (i.e. perpendicular to the left-right-direction) - - \image html QCPItemBracket-length.png -
Demonstrating the effect of different values for \ref setLength, for styles \ref - bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.
-*/ -void QCPItemBracket::setLength(double length) -{ - mLength = length; -} + This method retrieves an optimized set of data points via \ref getOptimizedLineData, an branches + out to the line style specific functions such as \ref dataToLines, \ref dataToStepLeftLines, etc. + according to the line style of the graph. -/*! - Sets the style of the bracket, i.e. the shape/visual appearance. - - \see setPen + \a lines will be filled with points in pixel coordinates, that can be drawn with the according + draw functions like \ref drawLinePlot and \ref drawImpulsePlot. The points returned in \a lines + aren't necessarily the original data points. For example, step line styles require additional + points to form the steps when drawn. If the line style of the graph is \ref lsNone, the \a + lines vector will be empty. + + \a dataRange specifies the beginning and ending data indices that will be taken into account for + conversion. In this function, the specified range may exceed the total data bounds without harm: + a correspondingly trimmed data range will be used. This takes the burden off the user of this + function to check for valid indices in \a dataRange, e.g. when extending ranges coming from \ref + getDataSegments. + + \see getScatters */ -void QCPItemBracket::setStyle(QCPItemBracket::BracketStyle style) +void QCPPolarGraph::getLines(QVector *lines, const QCPDataRange &dataRange) const { - mStyle = style; + if (!lines) return; + QCPGraphDataContainer::const_iterator begin, end; + getVisibleDataBounds(begin, end, dataRange); + if (begin == end) + { + lines->clear(); + return; + } + + QVector lineData; + if (mLineStyle != lsNone) + getOptimizedLineData(&lineData, begin, end); + + switch (mLineStyle) + { + case lsNone: lines->clear(); break; + case lsLine: *lines = dataToLines(lineData); break; + } } -/* inherits documentation from base class */ -double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +void QCPPolarGraph::getScatters(QVector *scatters, const QCPDataRange &dataRange) const { - Q_UNUSED(details) - if (onlySelectable && !mSelectable) - return -1; + QCPPolarAxisAngular *keyAxis = mKeyAxis.data(); + QCPPolarAxisRadial *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } - QCPVector2D p(pos); - QCPVector2D leftVec(left->pixelPosition()); - QCPVector2D rightVec(right->pixelPosition()); - if (leftVec.toPoint() == rightVec.toPoint()) - return -1; + if (!scatters) return; + QCPGraphDataContainer::const_iterator begin, end; + getVisibleDataBounds(begin, end, dataRange); + if (begin == end) + { + scatters->clear(); + return; + } - QCPVector2D widthVec = (rightVec-leftVec)*0.5; - QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength; - QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec; + QVector data; + getOptimizedScatterData(&data, begin, end); - switch (mStyle) + scatters->resize(data.size()); + for (int i=0; icoordToPixel(data.at(i).key, data.at(i).value); } - return -1; } -/* inherits documentation from base class */ -void QCPItemBracket::draw(QCPPainter *painter) +void QCPPolarGraph::getOptimizedLineData(QVector *lineData, const QCPGraphDataContainer::const_iterator &begin, const QCPGraphDataContainer::const_iterator &end) const { - QCPVector2D leftVec(left->pixelPosition()); - QCPVector2D rightVec(right->pixelPosition()); - if (leftVec.toPoint() == rightVec.toPoint()) - return; + lineData->clear(); - QCPVector2D widthVec = (rightVec-leftVec)*0.5; - QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength; - QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec; - - QPolygon boundingPoly; - boundingPoly << leftVec.toPoint() << rightVec.toPoint() - << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint(); - QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF()); - if (clip.intersects(boundingPoly.boundingRect())) + // TODO: fix for log axes and thick line style + + const QCPRange range = mValueAxis->range(); + bool reversed = mValueAxis->rangeReversed(); + const double clipMargin = range.size()*0.05; // extra distance from visible circle, so optimized outside lines can cover more angle before having to place a dummy point to prevent tangents + const double upperClipValue = range.upper + (reversed ? 0 : range.size()*0.05+clipMargin); // clip slightly outside of actual range to avoid line thicknesses to peek into visible circle + const double lowerClipValue = range.lower - (reversed ? range.size()*0.05+clipMargin : 0); // clip slightly outside of actual range to avoid line thicknesses to peek into visible circle + const double maxKeySkip = qAsin(qSqrt(clipMargin*(clipMargin+2*range.size()))/(range.size()+clipMargin))/M_PI*mKeyAxis->range().size(); // the maximum angle between two points on outer circle (r=clipValue+clipMargin) before connecting line becomes tangent to inner circle (r=clipValue) + double skipBegin = 0; + bool belowRange = false; + bool aboveRange = false; + QCPGraphDataContainer::const_iterator it = begin; + while (it != end) { - painter->setPen(mainPen()); - switch (mStyle) + if (it->value < lowerClipValue) { - case bsSquare: + if (aboveRange) // jumped directly from above to below visible range, draw previous point so entry angle is correct { - painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF()); - painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF()); - painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF()); - break; + aboveRange = false; + if (!reversed) // TODO: with inner radius, we'll need else case here with projected border point + lineData->append(*(it-1)); } - case bsRound: + if (!belowRange) { - painter->setBrush(Qt::NoBrush); - QPainterPath path; - path.moveTo((centerVec+widthVec+lengthVec).toPointF()); - path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF()); - path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF()); - painter->drawPath(path); - break; + skipBegin = it->key; + lineData->append(QCPGraphData(it->key, lowerClipValue)); + belowRange = true; } - case bsCurly: + if (it->key-skipBegin > maxKeySkip) // add dummy point if we're exceeding the maximum skippable angle (to prevent unintentional intersections with visible circle) { - painter->setBrush(Qt::NoBrush); - QPainterPath path; - path.moveTo((centerVec+widthVec+lengthVec).toPointF()); - path.cubicTo((centerVec+widthVec-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+lengthVec).toPointF(), centerVec.toPointF()); - path.cubicTo((centerVec-0.4*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF()); - painter->drawPath(path); - break; + skipBegin += maxKeySkip; + lineData->append(QCPGraphData(skipBegin, lowerClipValue)); } - case bsCalligraphic: + } else if (it->value > upperClipValue) + { + if (belowRange) // jumped directly from below to above visible range, draw previous point so entry angle is correct (if lower means outer, so if reversed axis) { - painter->setPen(Qt::NoPen); - painter->setBrush(QBrush(mainPen().color())); - QPainterPath path; - path.moveTo((centerVec+widthVec+lengthVec).toPointF()); - - path.cubicTo((centerVec+widthVec-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+0.8*lengthVec).toPointF(), centerVec.toPointF()); - path.cubicTo((centerVec-0.4*widthVec+0.8*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF()); - - path.cubicTo((centerVec-widthVec-lengthVec*0.5).toPointF(), (centerVec-0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+lengthVec*0.2).toPointF()); - path.cubicTo((centerVec+0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5).toPointF(), (centerVec+widthVec+lengthVec).toPointF()); - - painter->drawPath(path); - break; + belowRange = false; + if (reversed) + lineData->append(*(it-1)); } + if (!aboveRange) + { + skipBegin = it->key; + lineData->append(QCPGraphData(it->key, upperClipValue)); + aboveRange = true; + } + if (it->key-skipBegin > maxKeySkip) // add dummy point if we're exceeding the maximum skippable angle (to prevent unintentional intersections with visible circle) + { + skipBegin += maxKeySkip; + lineData->append(QCPGraphData(skipBegin, upperClipValue)); + } + } else // value within bounds where we don't optimize away points + { + if (aboveRange) + { + aboveRange = false; + if (!reversed) + lineData->append(*(it-1)); // just entered from above, draw previous point so entry angle is correct (if above means outer, so if not reversed axis) + } + if (belowRange) + { + belowRange = false; + if (reversed) + lineData->append(*(it-1)); // just entered from below, draw previous point so entry angle is correct (if below means outer, so if reversed axis) + } + lineData->append(*it); // inside visible circle, add point normally } + ++it; + } + // to make fill not erratic, add last point normally if it was outside visible circle: + if (aboveRange) + { + aboveRange = false; + if (!reversed) + lineData->append(*(it-1)); // just entered from above, draw previous point so entry angle is correct (if above means outer, so if not reversed axis) + } + if (belowRange) + { + belowRange = false; + if (reversed) + lineData->append(*(it-1)); // just entered from below, draw previous point so entry angle is correct (if below means outer, so if reversed axis) } } -/* inherits documentation from base class */ -QPointF QCPItemBracket::anchorPixelPosition(int anchorId) const +void QCPPolarGraph::getOptimizedScatterData(QVector *scatterData, QCPGraphDataContainer::const_iterator begin, QCPGraphDataContainer::const_iterator end) const { - QCPVector2D leftVec(left->pixelPosition()); - QCPVector2D rightVec(right->pixelPosition()); - if (leftVec.toPoint() == rightVec.toPoint()) - return leftVec.toPointF(); - - QCPVector2D widthVec = (rightVec-leftVec)*0.5; - QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength; - QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec; + scatterData->clear(); - switch (anchorId) + const QCPRange range = mValueAxis->range(); + bool reversed = mValueAxis->rangeReversed(); + const double clipMargin = range.size()*0.05; + const double upperClipValue = range.upper + (reversed ? 0 : clipMargin); // clip slightly outside of actual range to avoid scatter size to peek into visible circle + const double lowerClipValue = range.lower - (reversed ? clipMargin : 0); // clip slightly outside of actual range to avoid scatter size to peek into visible circle + QCPGraphDataContainer::const_iterator it = begin; + while (it != end) { - case aiCenter: - return centerVec.toPointF(); + if (it->value > lowerClipValue && it->value < upperClipValue) + scatterData->append(*it); + ++it; } - qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId; - return QPointF(); } /*! \internal - Returns the pen that should be used for drawing lines. Returns mPen when the - item is not selected and mSelectedPen when it is. + Takes raw data points in plot coordinates as \a data, and returns a vector containing pixel + coordinate points which are suitable for drawing the line style \ref lsLine. + + The source of \a data is usually \ref getOptimizedLineData, and this method is called in \a + getLines if the line style is set accordingly. + + \see dataToStepLeftLines, dataToStepRightLines, dataToStepCenterLines, dataToImpulseLines, getLines, drawLinePlot */ -QPen QCPItemBracket::mainPen() const +QVector QCPPolarGraph::dataToLines(const QVector &data) const { - return mSelected ? mSelectedPen : mPen; + QVector result; + QCPPolarAxisAngular *keyAxis = mKeyAxis.data(); + QCPPolarAxisRadial *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; } + + // transform data points to pixels: + result.resize(data.size()); + for (int i=0; icoordToPixel(data.at(i).key, data.at(i).value); + return result; } -/* end of 'src/items/item-bracket.cpp' */ +/* end of 'src/polar/polargraph.cpp' */ diff --git a/libs/qcustomplot-source/qcustomplot.h b/libs/qcustomplot-source/qcustomplot.h index 3b046e5d0..8f0f78b17 100644 --- a/libs/qcustomplot-source/qcustomplot.h +++ b/libs/qcustomplot-source/qcustomplot.h @@ -1,7 +1,7 @@ /*************************************************************************** ** ** ** QCustomPlot, an easy to use, modern plotting widget for Qt ** -** Copyright (C) 2011-2018 Emanuel Eichhammer ** +** Copyright (C) 2011-2021 Emanuel Eichhammer ** ** ** ** 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 ** @@ -19,8 +19,8 @@ **************************************************************************** ** Author: Emanuel Eichhammer ** ** Website/Contact: http://www.qcustomplot.com/ ** -** Date: 25.06.18 ** -** Version: 2.0.1 ** +** Date: 29.03.21 ** +** Version: 2.1.0 ** ****************************************************************************/ #ifndef QCUSTOMPLOT_H @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -70,7 +71,12 @@ #include #ifdef QCP_OPENGL_FBO # include -# include +# if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +# include +# else +# include +# include +# endif # ifdef QCP_OPENGL_OFFSCREENSURFACE # include # else @@ -90,6 +96,12 @@ # include # include #endif +#if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0) +# include +#endif +# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) +# include +#endif class QCPPainter; class QCustomPlot; @@ -111,12 +123,16 @@ class QCPSelectionRect; class QCPColorMap; class QCPColorScale; class QCPBars; +class QCPPolarAxisRadial; +class QCPPolarAxisAngular; +class QCPPolarGrid; +class QCPPolarGraph; -/* including file 'src/global.h', size 16357 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/global.h' */ +/* modified 2021-03-29T02:30:44, size 16981 */ -#define QCUSTOMPLOT_VERSION_STR "2.0.1" -#define QCUSTOMPLOT_VERSION 0x020001 +#define QCUSTOMPLOT_VERSION_STR "2.1.0" +#define QCUSTOMPLOT_VERSION 0x020100 // decl definitions for shared library compilation/usage: #if defined(QT_STATIC_BUILD) @@ -253,7 +269,8 @@ Q_DECLARE_FLAGS(PlottingHints, PlottingHint) \see QCustomPlot::setInteractions */ -enum Interaction { iRangeDrag = 0x001 ///< 0x001 Axis ranges are draggable (see \ref QCPAxisRect::setRangeDrag, \ref QCPAxisRect::setRangeDragAxes) +enum Interaction { iNone = 0x000 ///< 0x000 None of the interactions are possible + ,iRangeDrag = 0x001 ///< 0x001 Axis ranges are draggable (see \ref QCPAxisRect::setRangeDrag, \ref QCPAxisRect::setRangeDragAxes) ,iRangeZoom = 0x002 ///< 0x002 Axis ranges are zoomable with the mouse wheel (see \ref QCPAxisRect::setRangeZoom, \ref QCPAxisRect::setRangeZoomAxes) ,iMultiSelect = 0x004 ///< 0x004 The user can select multiple objects by holding the modifier set by \ref QCustomPlot::setMultiSelectModifier while clicking ,iSelectPlottables = 0x008 ///< 0x008 Plottables are selectable (e.g. graphs, curves, bars,... see QCPAbstractPlottable) @@ -261,6 +278,7 @@ enum Interaction { iRangeDrag = 0x001 ///< 0x001 Axis ranges ar ,iSelectLegend = 0x020 ///< 0x020 Legends are selectable (or their child items, see QCPLegend::setSelectableParts) ,iSelectItems = 0x040 ///< 0x040 Items are selectable (Rectangles, Arrows, Textitems, etc. see \ref QCPAbstractItem) ,iSelectOther = 0x080 ///< 0x080 All other objects are selectable (e.g. your own derived layerables, other layout elements,...) + ,iSelectPlottablesBeyondAxisRect = 0x100 ///< 0x100 When performing plottable selection/hit tests, this flag extends the sensitive area beyond the axis rect }; Q_DECLARE_FLAGS(Interactions, Interaction) @@ -381,8 +399,8 @@ Q_DECLARE_METATYPE(QCP::SelectionType) /* end of 'src/global.h' */ -/* including file 'src/vector2d.h', size 4928 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/vector2d.h' */ +/* modified 2021-03-29T02:30:44, size 4988 */ class QCP_LIB_DECL QCPVector2D { @@ -405,7 +423,8 @@ class QCP_LIB_DECL QCPVector2D // non-virtual methods: double length() const { return qSqrt(mX*mX+mY*mY); } double lengthSquared() const { return mX*mX+mY*mY; } - QPoint toPoint() const { return QPoint(mX, mY); } + double angle() const { return qAtan2(mY, mX); } + QPoint toPoint() const { return QPoint(int(mX), int(mY)); } QPointF toPointF() const { return QPointF(mX, mY); } bool isNull() const { return qIsNull(mX) && qIsNull(mY); } @@ -455,8 +474,8 @@ inline QDebug operator<< (QDebug d, const QCPVector2D &vec) /* end of 'src/vector2d.h' */ -/* including file 'src/painter.h', size 4035 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/painter.h' */ +/* modified 2021-03-29T02:30:44, size 4035 */ class QCP_LIB_DECL QCPPainter : public QPainter { @@ -514,8 +533,8 @@ Q_DECLARE_METATYPE(QCPPainter::PainterMode) /* end of 'src/painter.h' */ -/* including file 'src/paintbuffer.h', size 4958 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/paintbuffer.h' */ +/* modified 2021-03-29T02:30:44, size 5006 */ class QCP_LIB_DECL QCPAbstractPaintBuffer { @@ -556,7 +575,7 @@ class QCP_LIB_DECL QCPPaintBufferPixmap : public QCPAbstractPaintBuffer { public: explicit QCPPaintBufferPixmap(const QSize &size, double devicePixelRatio); - virtual ~QCPPaintBufferPixmap(); + virtual ~QCPPaintBufferPixmap() Q_DECL_OVERRIDE; // reimplemented virtual methods: virtual QCPPainter *startPainting() Q_DECL_OVERRIDE; @@ -577,7 +596,7 @@ class QCP_LIB_DECL QCPPaintBufferGlPbuffer : public QCPAbstractPaintBuffer { public: explicit QCPPaintBufferGlPbuffer(const QSize &size, double devicePixelRatio, int multisamples); - virtual ~QCPPaintBufferGlPbuffer(); + virtual ~QCPPaintBufferGlPbuffer() Q_DECL_OVERRIDE; // reimplemented virtual methods: virtual QCPPainter *startPainting() Q_DECL_OVERRIDE; @@ -600,7 +619,7 @@ class QCP_LIB_DECL QCPPaintBufferGlFbo : public QCPAbstractPaintBuffer { public: explicit QCPPaintBufferGlFbo(const QSize &size, double devicePixelRatio, QWeakPointer glContext, QWeakPointer glPaintDevice); - virtual ~QCPPaintBufferGlFbo(); + virtual ~QCPPaintBufferGlFbo() Q_DECL_OVERRIDE; // reimplemented virtual methods: virtual QCPPainter *startPainting() Q_DECL_OVERRIDE; @@ -622,8 +641,8 @@ class QCP_LIB_DECL QCPPaintBufferGlFbo : public QCPAbstractPaintBuffer /* end of 'src/paintbuffer.h' */ -/* including file 'src/layer.h', size 6885 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/layer.h' */ +/* modified 2021-03-29T02:30:44, size 7038 */ class QCP_LIB_DECL QCPLayer : public QObject { @@ -705,7 +724,7 @@ class QCP_LIB_DECL QCPLayerable : public QObject Q_PROPERTY(bool antialiased READ antialiased WRITE setAntialiased) /// \endcond public: - QCPLayerable(QCustomPlot *plot, QString targetLayer=QString(), QCPLayerable *parentLayerable=0); + QCPLayerable(QCustomPlot *plot, QString targetLayer=QString(), QCPLayerable *parentLayerable=nullptr); virtual ~QCPLayerable(); // getters: @@ -722,7 +741,7 @@ class QCP_LIB_DECL QCPLayerable : public QObject void setAntialiased(bool enabled); // introduced virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const; // non-property methods: bool realVisibility() const; @@ -771,8 +790,8 @@ class QCP_LIB_DECL QCPLayerable : public QObject /* end of 'src/layer.h' */ -/* including file 'src/axis/range.h', size 5280 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/axis/range.h' */ +/* modified 2021-03-29T02:30:44, size 5280 */ class QCP_LIB_DECL QCPRange { @@ -889,8 +908,8 @@ inline const QCPRange operator/(const QCPRange& range, double value) /* end of 'src/axis/range.h' */ -/* including file 'src/selection.h', size 8569 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/selection.h' */ +/* modified 2021-03-29T02:30:44, size 8569 */ class QCP_LIB_DECL QCPDataRange { @@ -1093,15 +1112,15 @@ inline QDebug operator<< (QDebug d, const QCPDataSelection &selection) /* end of 'src/selection.h' */ -/* including file 'src/selectionrect.h', size 3338 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/selectionrect.h' */ +/* modified 2021-03-29T02:30:44, size 3354 */ class QCP_LIB_DECL QCPSelectionRect : public QCPLayerable { Q_OBJECT public: explicit QCPSelectionRect(QCustomPlot *parentPlot); - virtual ~QCPSelectionRect(); + virtual ~QCPSelectionRect() Q_DECL_OVERRIDE; // getters: QRect rect() const { return mRect; } @@ -1147,8 +1166,8 @@ class QCP_LIB_DECL QCPSelectionRect : public QCPLayerable /* end of 'src/selectionrect.h' */ -/* including file 'src/layout.h', size 14224 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/layout.h' */ +/* modified 2021-03-29T02:30:44, size 14279 */ class QCP_LIB_DECL QCPMarginGroup : public QObject { @@ -1218,8 +1237,8 @@ class QCP_LIB_DECL QCPLayoutElement : public QCPLayerable }; Q_ENUMS(SizeConstraintRect) - explicit QCPLayoutElement(QCustomPlot *parentPlot=0); - virtual ~QCPLayoutElement(); + explicit QCPLayoutElement(QCustomPlot *parentPlot=nullptr); + virtual ~QCPLayoutElement() Q_DECL_OVERRIDE; // getters: QCPLayout *layout() const { return mParentLayout; } @@ -1231,7 +1250,7 @@ class QCP_LIB_DECL QCPLayoutElement : public QCPLayerable QSize minimumSize() const { return mMinimumSize; } QSize maximumSize() const { return mMaximumSize; } SizeConstraintRect sizeConstraintRect() const { return mSizeConstraintRect; } - QCPMarginGroup *marginGroup(QCP::MarginSide side) const { return mMarginGroups.value(side, (QCPMarginGroup*)0); } + QCPMarginGroup *marginGroup(QCP::MarginSide side) const { return mMarginGroups.value(side, nullptr); } QHash marginGroups() const { return mMarginGroups; } // setters: @@ -1253,7 +1272,7 @@ class QCP_LIB_DECL QCPLayoutElement : public QCPLayerable virtual QList elements(bool recursive) const; // reimplemented virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; protected: // property members: @@ -1352,7 +1371,7 @@ class QCP_LIB_DECL QCPLayoutGrid : public QCPLayout Q_ENUMS(FillOrder) explicit QCPLayoutGrid(); - virtual ~QCPLayoutGrid(); + virtual ~QCPLayoutGrid() Q_DECL_OVERRIDE; // getters: int rowCount() const { return mElements.size(); } @@ -1428,7 +1447,7 @@ class QCP_LIB_DECL QCPLayoutInset : public QCPLayout Q_ENUMS(InsetPlacement) explicit QCPLayoutInset(); - virtual ~QCPLayoutInset(); + virtual ~QCPLayoutInset() Q_DECL_OVERRIDE; // getters: InsetPlacement insetPlacement(int index) const; @@ -1447,7 +1466,7 @@ class QCP_LIB_DECL QCPLayoutInset : public QCPLayout virtual QCPLayoutElement* takeAt(int index) Q_DECL_OVERRIDE; virtual bool take(QCPLayoutElement* element) Q_DECL_OVERRIDE; virtual void simplify() Q_DECL_OVERRIDE {} - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; // non-virtual methods: void addElement(QCPLayoutElement *element, Qt::Alignment alignment); @@ -1468,8 +1487,8 @@ Q_DECLARE_METATYPE(QCPLayoutInset::InsetPlacement) /* end of 'src/layout.h' */ -/* including file 'src/lineending.h', size 4426 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/lineending.h' */ +/* modified 2021-03-29T02:30:44, size 4426 */ class QCP_LIB_DECL QCPLineEnding { @@ -1532,8 +1551,148 @@ Q_DECLARE_METATYPE(QCPLineEnding::EndingStyle) /* end of 'src/lineending.h' */ -/* including file 'src/axis/axisticker.h', size 4224 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/axis/labelpainter.h' */ +/* modified 2021-03-29T02:30:44, size 7086 */ + +class QCPLabelPainterPrivate +{ + Q_GADGET +public: + /*! + TODO + */ + enum AnchorMode { amRectangular ///< + ,amSkewedUpright ///< + ,amSkewedRotated ///< + }; + Q_ENUMS(AnchorMode) + + /*! + TODO + */ + enum AnchorReferenceType { artNormal ///< + ,artTangent ///< + }; + Q_ENUMS(AnchorReferenceType) + + /*! + TODO + */ + enum AnchorSide { asLeft ///< + ,asRight ///< + ,asTop ///< + ,asBottom ///< + ,asTopLeft + ,asTopRight + ,asBottomRight + ,asBottomLeft + }; + Q_ENUMS(AnchorSide) + + explicit QCPLabelPainterPrivate(QCustomPlot *parentPlot); + virtual ~QCPLabelPainterPrivate(); + + // setters: + void setAnchorSide(AnchorSide side); + void setAnchorMode(AnchorMode mode); + void setAnchorReference(const QPointF &pixelPoint); + void setAnchorReferenceType(AnchorReferenceType type); + void setFont(const QFont &font); + void setColor(const QColor &color); + void setPadding(int padding); + void setRotation(double rotation); + void setSubstituteExponent(bool enabled); + void setMultiplicationSymbol(QChar symbol); + void setAbbreviateDecimalPowers(bool enabled); + void setCacheSize(int labelCount); + + // getters: + AnchorMode anchorMode() const { return mAnchorMode; } + AnchorSide anchorSide() const { return mAnchorSide; } + QPointF anchorReference() const { return mAnchorReference; } + AnchorReferenceType anchorReferenceType() const { return mAnchorReferenceType; } + QFont font() const { return mFont; } + QColor color() const { return mColor; } + int padding() const { return mPadding; } + double rotation() const { return mRotation; } + bool substituteExponent() const { return mSubstituteExponent; } + QChar multiplicationSymbol() const { return mMultiplicationSymbol; } + bool abbreviateDecimalPowers() const { return mAbbreviateDecimalPowers; } + int cacheSize() const; + + //virtual int size() const; + + // non-property methods: + void drawTickLabel(QCPPainter *painter, const QPointF &tickPos, const QString &text); + void clearCache(); + + // constants that may be used with setMultiplicationSymbol: + static const QChar SymbolDot; + static const QChar SymbolCross; + +protected: + struct CachedLabel + { + QPoint offset; + QPixmap pixmap; + }; + struct LabelData + { + AnchorSide side; + double rotation; // angle in degrees + QTransform transform; // the transform about the label anchor which is at (0, 0). Does not contain final absolute x/y positioning on the plot/axis + QString basePart, expPart, suffixPart; + QRect baseBounds, expBounds, suffixBounds; + QRect totalBounds; // is in a coordinate system where label top left is at (0, 0) + QRect rotatedTotalBounds; // is in a coordinate system where the label anchor is at (0, 0) + QFont baseFont, expFont; + QColor color; + }; + + // property members: + AnchorMode mAnchorMode; + AnchorSide mAnchorSide; + QPointF mAnchorReference; + AnchorReferenceType mAnchorReferenceType; + QFont mFont; + QColor mColor; + int mPadding; + double mRotation; // this is the rotation applied uniformly to all labels, not the heterogeneous rotation in amCircularRotated mode + bool mSubstituteExponent; + QChar mMultiplicationSymbol; + bool mAbbreviateDecimalPowers; + // non-property members: + QCustomPlot *mParentPlot; + QByteArray mLabelParameterHash; // to determine whether mLabelCache needs to be cleared due to changed parameters + QCache mLabelCache; + QRect mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox; + int mLetterCapHeight, mLetterDescent; + + // introduced virtual methods: + virtual void drawLabelMaybeCached(QCPPainter *painter, const QFont &font, const QColor &color, const QPointF &pos, AnchorSide side, double rotation, const QString &text); + virtual QByteArray generateLabelParameterHash() const; // TODO: get rid of this in favor of invalidation flag upon setters? + + // non-virtual methods: + QPointF getAnchorPos(const QPointF &tickPos); + void drawText(QCPPainter *painter, const QPointF &pos, const LabelData &labelData) const; + LabelData getTickLabelData(const QFont &font, const QColor &color, double rotation, AnchorSide side, const QString &text) const; + void applyAnchorTransform(LabelData &labelData) const; + //void getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const; + CachedLabel *createCachedLabel(const LabelData &labelData) const; + QByteArray cacheKey(const QString &text, const QColor &color, double rotation, AnchorSide side) const; + AnchorSide skewedAnchorSide(const QPointF &tickPos, double sideExpandHorz, double sideExpandVert) const; + AnchorSide rotationCorrectedSide(AnchorSide side, double rotation) const; + void analyzeFontMetrics(); +}; +Q_DECLARE_METATYPE(QCPLabelPainterPrivate::AnchorMode) +Q_DECLARE_METATYPE(QCPLabelPainterPrivate::AnchorSide) + + +/* end of 'src/axis/labelpainter.h' */ + + +/* including file 'src/axis/axisticker.h' */ +/* modified 2021-03-29T02:30:44, size 4230 */ class QCP_LIB_DECL QCPAxisTicker { @@ -1584,7 +1743,7 @@ class QCP_LIB_DECL QCPAxisTicker // non-virtual methods: void trimTicks(const QCPRange &range, QVector &ticks, bool keepOneOutlier) const; double pickClosest(double target, const QVector &candidates) const; - double getMantissa(double input, double *magnitude=0) const; + double getMantissa(double input, double *magnitude=nullptr) const; double cleanMantissa(double input) const; private: @@ -1597,8 +1756,8 @@ Q_DECLARE_METATYPE(QSharedPointer) /* end of 'src/axis/axisticker.h' */ -/* including file 'src/axis/axistickerdatetime.h', size 3289 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/axis/axistickerdatetime.h' */ +/* modified 2021-03-29T02:30:44, size 3600 */ class QCP_LIB_DECL QCPAxisTickerDateTime : public QCPAxisTicker { @@ -1608,23 +1767,31 @@ class QCP_LIB_DECL QCPAxisTickerDateTime : public QCPAxisTicker // getters: QString dateTimeFormat() const { return mDateTimeFormat; } Qt::TimeSpec dateTimeSpec() const { return mDateTimeSpec; } +# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) + QTimeZone timeZone() const { return mTimeZone; } +#endif // setters: void setDateTimeFormat(const QString &format); void setDateTimeSpec(Qt::TimeSpec spec); +# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) + void setTimeZone(const QTimeZone &zone); +# endif void setTickOrigin(double origin); // hides base class method but calls baseclass implementation ("using" throws off IDEs and doxygen) void setTickOrigin(const QDateTime &origin); // static methods: static QDateTime keyToDateTime(double key); - static double dateTimeToKey(const QDateTime dateTime); - static double dateTimeToKey(const QDate date); + static double dateTimeToKey(const QDateTime &dateTime); + static double dateTimeToKey(const QDate &date, Qt::TimeSpec timeSpec=Qt::LocalTime); protected: // property members: QString mDateTimeFormat; Qt::TimeSpec mDateTimeSpec; - +# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) + QTimeZone mTimeZone; +# endif // non-property members: enum DateStrategy {dsNone, dsUniformTimeInDay, dsUniformDayInMonth} mDateStrategy; @@ -1638,8 +1805,8 @@ class QCP_LIB_DECL QCPAxisTickerDateTime : public QCPAxisTicker /* end of 'src/axis/axistickerdatetime.h' */ -/* including file 'src/axis/axistickertime.h', size 3542 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/axis/axistickertime.h' */ +/* modified 2021-03-29T02:30:44, size 3542 */ class QCP_LIB_DECL QCPAxisTickerTime : public QCPAxisTicker { @@ -1690,8 +1857,8 @@ Q_DECLARE_METATYPE(QCPAxisTickerTime::TimeUnit) /* end of 'src/axis/axistickertime.h' */ -/* including file 'src/axis/axistickerfixed.h', size 3308 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/axis/axistickerfixed.h' */ +/* modified 2021-03-29T02:30:44, size 3308 */ class QCP_LIB_DECL QCPAxisTickerFixed : public QCPAxisTicker { @@ -1732,8 +1899,8 @@ Q_DECLARE_METATYPE(QCPAxisTickerFixed::ScaleStrategy) /* end of 'src/axis/axistickerfixed.h' */ -/* including file 'src/axis/axistickertext.h', size 3090 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/axis/axistickertext.h' */ +/* modified 2021-03-29T02:30:44, size 3090 */ class QCP_LIB_DECL QCPAxisTickerText : public QCPAxisTicker { @@ -1770,8 +1937,8 @@ class QCP_LIB_DECL QCPAxisTickerText : public QCPAxisTicker /* end of 'src/axis/axistickertext.h' */ -/* including file 'src/axis/axistickerpi.h', size 3911 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/axis/axistickerpi.h' */ +/* modified 2021-03-29T02:30:44, size 3911 */ class QCP_LIB_DECL QCPAxisTickerPi : public QCPAxisTicker { @@ -1829,8 +1996,8 @@ Q_DECLARE_METATYPE(QCPAxisTickerPi::FractionStyle) /* end of 'src/axis/axistickerpi.h' */ -/* including file 'src/axis/axistickerlog.h', size 2663 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/axis/axistickerlog.h' */ +/* modified 2021-03-29T02:30:44, size 2594 */ class QCP_LIB_DECL QCPAxisTickerLog : public QCPAxisTicker { @@ -1854,7 +2021,6 @@ class QCP_LIB_DECL QCPAxisTickerLog : public QCPAxisTicker double mLogBaseLnInv; // reimplemented virtual methods: - virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE; virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE; virtual QVector createTickVector(double tickStep, const QCPRange &range) Q_DECL_OVERRIDE; }; @@ -1862,8 +2028,8 @@ class QCP_LIB_DECL QCPAxisTickerLog : public QCPAxisTicker /* end of 'src/axis/axistickerlog.h' */ -/* including file 'src/axis/axis.h', size 20698 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/axis/axis.h' */ +/* modified 2021-03-29T02:30:44, size 20913 */ class QCP_LIB_DECL QCPGrid :public QCPLayerable { @@ -2008,7 +2174,7 @@ class QCP_LIB_DECL QCPAxis : public QCPLayerable Q_DECLARE_FLAGS(SelectableParts, SelectablePart) explicit QCPAxis(QCPAxisRect *parent, AxisType type); - virtual ~QCPAxis(); + virtual ~QCPAxis() Q_DECL_OVERRIDE; // getters: AxisType axisType() const { return mAxisType; } @@ -2102,7 +2268,7 @@ class QCP_LIB_DECL QCPAxis : public QCPLayerable void setUpperEnding(const QCPLineEnding &ending); // reimplemented virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; // non-property methods: Qt::Orientation orientation() const { return mOrientation; } @@ -2120,7 +2286,7 @@ class QCP_LIB_DECL QCPAxis : public QCPLayerable QList items() const; static AxisType marginSideToAxisType(QCP::MarginSide side); - static Qt::Orientation orientation(AxisType type) { return type==atBottom||type==atTop ? Qt::Horizontal : Qt::Vertical; } + static Qt::Orientation orientation(AxisType type) { return type==atBottom || type==atTop ? Qt::Horizontal : Qt::Vertical; } static AxisType opposite(AxisType type); signals: @@ -2228,7 +2394,7 @@ class QCPAxisPainterPrivate virtual ~QCPAxisPainterPrivate(); virtual void draw(QCPPainter *painter); - virtual int size() const; + virtual int size(); void clearCache(); QRect axisSelectionBox() const { return mAxisSelectionBox; } @@ -2253,7 +2419,7 @@ class QCPAxisPainterPrivate QFont tickLabelFont; QColor tickLabelColor; QRect axisRect, viewportRect; - double offset; // directly accessed by QCPAxis setters/getters + int offset; // directly accessed by QCPAxis setters/getters bool abbreviateDecimalPowers; bool reversedEndings; @@ -2290,8 +2456,8 @@ class QCPAxisPainterPrivate /* end of 'src/axis/axis.h' */ -/* including file 'src/scatterstyle.h', size 7275 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/scatterstyle.h' */ +/* modified 2021-03-29T02:30:44, size 7275 */ class QCP_LIB_DECL QCPScatterStyle { @@ -2397,8 +2563,8 @@ Q_DECLARE_METATYPE(QCPScatterStyle::ScatterShape) /* end of 'src/scatterstyle.h' */ -/* including file 'src/datacontainer.h', size 4596 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/datacontainer.h' */ +/* modified 2021-03-29T02:30:44, size 34070 */ /*! \relates QCPDataContainer Returns whether the sort key of \a a is less than the sort key of \a b. @@ -2465,11 +2631,9 @@ class QCPDataContainer // no QCP_LIB_DECL, template class ends up in header (cpp void performAutoSqueeze(); }; -// include implementation in header since it is a class template: -/* including file 'src/datacontainer.cpp', size 31349 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +// include implementation in header since it is a class template: //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPDataContainer //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -2766,7 +2930,7 @@ void QCPDataContainer::removeBefore(double sortKey) { QCPDataContainer::iterator it = begin(); QCPDataContainer::iterator itEnd = std::lower_bound(begin(), end(), DataType::fromSortKey(sortKey), qcpLessThanSortKey); - mPreallocSize += itEnd-it; // don't actually delete, just add it to the preallocated block (if it gets too large, squeeze will take care of it) + mPreallocSize += int(itEnd-it); // don't actually delete, just add it to the preallocated block (if it gets too large, squeeze will take care of it) if (mAutoSqueeze) performAutoSqueeze(); } @@ -3096,8 +3260,8 @@ QCPRange QCPDataContainer::valueRange(bool &foundRange, QCP::SignDomai QCPDataContainer::const_iterator itEnd = constEnd(); if (DataType::sortKeyIsMainKey() && restrictKeyRange) { - itBegin = findBegin(inKeyRange.lower); - itEnd = findEnd(inKeyRange.upper); + itBegin = findBegin(inKeyRange.lower, false); + itEnd = findEnd(inKeyRange.upper, false); } if (signDomain == QCP::sdBoth) // range may be anywhere { @@ -3170,7 +3334,7 @@ QCPRange QCPDataContainer::valueRange(bool &foundRange, QCP::SignDomai template void QCPDataContainer::limitIteratorsToDataRange(const_iterator &begin, const_iterator &end, const QCPDataRange &dataRange) const { - QCPDataRange iteratorRange(begin-constBegin(), end-constBegin()); + QCPDataRange iteratorRange(int(begin-constBegin()), int(end-constBegin())); iteratorRange = iteratorRange.bounded(dataRange.bounded(this->dataRange())); begin = constBegin()+iteratorRange.begin(); end = constBegin()+iteratorRange.end(); @@ -3236,14 +3400,13 @@ void QCPDataContainer::performAutoSqueeze() if (shrinkPreAllocation || shrinkPostAllocation) squeeze(shrinkPreAllocation, shrinkPostAllocation); } -/* end of 'src/datacontainer.cpp' */ /* end of 'src/datacontainer.h' */ -/* including file 'src/plottable.h', size 8433 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/plottable.h' */ +/* modified 2021-03-29T02:30:44, size 8461 */ class QCP_LIB_DECL QCPSelectionDecorator { @@ -3309,7 +3472,7 @@ class QCP_LIB_DECL QCPAbstractPlottable : public QCPLayerable /// \endcond public: QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis); - virtual ~QCPAbstractPlottable(); + virtual ~QCPAbstractPlottable() Q_DECL_OVERRIDE; // getters: QString name() const { return mName; } @@ -3337,8 +3500,8 @@ class QCP_LIB_DECL QCPAbstractPlottable : public QCPLayerable void setSelectionDecorator(QCPSelectionDecorator *decorator); // introduced virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE = 0; // actually introduced in QCPLayerable as non-pure, but we want to force reimplementation for plottables - virtual QCPPlottableInterface1D *interface1D() { return 0; } + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE = 0; // actually introduced in QCPLayerable as non-pure, but we want to force reimplementation for plottables + virtual QCPPlottableInterface1D *interface1D() { return nullptr; } virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const = 0; virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const = 0; @@ -3399,8 +3562,8 @@ class QCP_LIB_DECL QCPAbstractPlottable : public QCPLayerable /* end of 'src/plottable.h' */ -/* including file 'src/item.h', size 9384 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/item.h' */ +/* modified 2021-03-29T02:30:44, size 9425 */ class QCP_LIB_DECL QCPItemAnchor { @@ -3424,7 +3587,7 @@ class QCP_LIB_DECL QCPItemAnchor QSet mChildrenX, mChildrenY; // introduced virtual methods: - virtual QCPItemPosition *toQCPItemPosition() { return 0; } + virtual QCPItemPosition *toQCPItemPosition() { return nullptr; } // non-virtual methods: void addChildX(QCPItemPosition* pos); // called from pos when this anchor is set as parent @@ -3462,7 +3625,7 @@ class QCP_LIB_DECL QCPItemPosition : public QCPItemAnchor Q_ENUMS(PositionType) QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name); - virtual ~QCPItemPosition(); + virtual ~QCPItemPosition() Q_DECL_OVERRIDE; // getters: PositionType type() const { return typeX(); } @@ -3487,7 +3650,7 @@ class QCP_LIB_DECL QCPItemPosition : public QCPItemAnchor bool setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false); bool setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false); void setCoords(double key, double value); - void setCoords(const QPointF &coords); + void setCoords(const QPointF &pos); void setAxes(QCPAxis* keyAxis, QCPAxis* valueAxis); void setAxisRect(QCPAxisRect *axisRect); void setPixelPosition(const QPointF &pixelPosition); @@ -3521,7 +3684,7 @@ class QCP_LIB_DECL QCPAbstractItem : public QCPLayerable /// \endcond public: explicit QCPAbstractItem(QCustomPlot *parentPlot); - virtual ~QCPAbstractItem(); + virtual ~QCPAbstractItem() Q_DECL_OVERRIDE; // getters: bool clipToAxisRect() const { return mClipToAxisRect; } @@ -3536,7 +3699,7 @@ class QCP_LIB_DECL QCPAbstractItem : public QCPLayerable Q_SLOT void setSelected(bool selected); // reimplemented virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE = 0; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE = 0; // non-virtual methods: QList positions() const { return mPositions; } @@ -3584,8 +3747,8 @@ class QCP_LIB_DECL QCPAbstractItem : public QCPLayerable /* end of 'src/item.h' */ -/* including file 'src/core.h', size 14886 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/core.h' */ +/* modified 2021-03-29T02:30:44, size 19304 */ class QCP_LIB_DECL QCustomPlot : public QWidget { @@ -3625,8 +3788,8 @@ class QCP_LIB_DECL QCustomPlot : public QWidget }; Q_ENUMS(RefreshPriority) - explicit QCustomPlot(QWidget *parent = 0); - virtual ~QCustomPlot(); + explicit QCustomPlot(QWidget *parent = nullptr); + virtual ~QCustomPlot() Q_DECL_OVERRIDE; // getters: QRect viewport() const { return mViewport; } @@ -3680,13 +3843,15 @@ class QCP_LIB_DECL QCustomPlot : public QWidget int clearPlottables(); int plottableCount() const; QList selectedPlottables() const; - QCPAbstractPlottable *plottableAt(const QPointF &pos, bool onlySelectable=false) const; + template + PlottableType *plottableAt(const QPointF &pos, bool onlySelectable=false, int *dataIndex=nullptr) const; + QCPAbstractPlottable *plottableAt(const QPointF &pos, bool onlySelectable=false, int *dataIndex=nullptr) const; bool hasPlottable(QCPAbstractPlottable *plottable) const; // specialized interface for QCPGraph: QCPGraph *graph(int index) const; QCPGraph *graph() const; - QCPGraph *addGraph(QCPAxis *keyAxis=0, QCPAxis *valueAxis=0); + QCPGraph *addGraph(QCPAxis *keyAxis=nullptr, QCPAxis *valueAxis=nullptr); bool removeGraph(QCPGraph *graph); bool removeGraph(int index); int clearGraphs(); @@ -3701,6 +3866,8 @@ class QCP_LIB_DECL QCustomPlot : public QWidget int clearItems(); int itemCount() const; QList selectedItems() const; + template + ItemType *itemAt(const QPointF &pos, bool onlySelectable=false) const; QCPAbstractItem *itemAt(const QPointF &pos, bool onlySelectable=false) const; bool hasItem(QCPAbstractItem *item) const; @@ -3711,7 +3878,7 @@ class QCP_LIB_DECL QCustomPlot : public QWidget bool setCurrentLayer(const QString &name); bool setCurrentLayer(QCPLayer *layer); int layerCount() const; - bool addLayer(const QString &name, QCPLayer *otherLayer=0, LayerInsertMode insertMode=limAbove); + bool addLayer(const QString &name, QCPLayer *otherLayer=nullptr, LayerInsertMode insertMode=limAbove); bool removeLayer(QCPLayer *layer); bool moveLayer(QCPLayer *layer, QCPLayer *otherLayer, LayerInsertMode insertMode=limAbove); @@ -3735,6 +3902,7 @@ class QCP_LIB_DECL QCustomPlot : public QWidget QPixmap toPixmap(int width=0, int height=0, double scale=1.0); void toPainter(QCPPainter *painter, int width=0, int height=0); Q_SLOT void replot(QCustomPlot::RefreshPriority refreshPriority=QCustomPlot::rpRefreshHint); + double replotTime(bool average=false) const; QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2; QCPLegend *legend; @@ -3757,6 +3925,7 @@ class QCP_LIB_DECL QCustomPlot : public QWidget void selectionChangedByUser(); void beforeReplot(); + void afterLayout(); void afterReplot(); protected: @@ -3795,6 +3964,7 @@ class QCP_LIB_DECL QCustomPlot : public QWidget QVariant mMouseSignalLayerableDetails; bool mReplotting; bool mReplotQueued; + double mReplotTime, mReplotTimeAverage; int mOpenGlMultisamples; QCP::AntialiasedElements mOpenGlAntialiasedElementsBackup; bool mOpenGlCacheLabelsBackup; @@ -3829,8 +3999,8 @@ class QCP_LIB_DECL QCustomPlot : public QWidget bool registerGraph(QCPGraph *graph); bool registerItem(QCPAbstractItem* item); void updateLayerIndices() const; - QCPLayerable *layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails=0) const; - QList layerableListAt(const QPointF &pos, bool onlySelectable, QList *selectionDetails=0) const; + QCPLayerable *layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails=nullptr) const; + QList layerableListAt(const QPointF &pos, bool onlySelectable, QList *selectionDetails=nullptr) const; void drawBackground(QCPPainter *painter); void setupPaintBuffers(); QCPAbstractPaintBuffer *createPaintBuffer(); @@ -3849,16 +4019,111 @@ class QCP_LIB_DECL QCustomPlot : public QWidget Q_DECLARE_METATYPE(QCustomPlot::LayerInsertMode) Q_DECLARE_METATYPE(QCustomPlot::RefreshPriority) + +// implementation of template functions: + +/*! + Returns the plottable at the pixel position \a pos. The plottable type (a QCPAbstractPlottable + subclass) that shall be taken into consideration can be specified via the template parameter. + + Plottables that only consist of single lines (like graphs) have a tolerance band around them, see + \ref setSelectionTolerance. If multiple plottables come into consideration, the one closest to \a + pos is returned. + + If \a onlySelectable is true, only plottables that are selectable + (QCPAbstractPlottable::setSelectable) are considered. + + if \a dataIndex is non-null, it is set to the index of the plottable's data point that is closest + to \a pos. + + If there is no plottable of the specified type at \a pos, returns \c nullptr. + + \see itemAt, layoutElementAt +*/ +template +PlottableType *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable, int *dataIndex) const +{ + PlottableType *resultPlottable = 0; + QVariant resultDetails; + double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value + + foreach (QCPAbstractPlottable *plottable, mPlottables) + { + PlottableType *currentPlottable = qobject_cast(plottable); + if (!currentPlottable || (onlySelectable && !currentPlottable->selectable())) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPAbstractPlottable::selectable + continue; + if (currentPlottable->clipRect().contains(pos.toPoint())) // only consider clicks where the plottable is actually visible + { + QVariant details; + double currentDistance = currentPlottable->selectTest(pos, false, dataIndex ? &details : nullptr); + if (currentDistance >= 0 && currentDistance < resultDistance) + { + resultPlottable = currentPlottable; + resultDetails = details; + resultDistance = currentDistance; + } + } + } + + if (resultPlottable && dataIndex) + { + QCPDataSelection sel = resultDetails.value(); + if (!sel.isEmpty()) + *dataIndex = sel.dataRange(0).begin(); + } + return resultPlottable; +} + +/*! + Returns the item at the pixel position \a pos. The item type (a QCPAbstractItem subclass) that shall be + taken into consideration can be specified via the template parameter. Items that only consist of single + lines (e.g. \ref QCPItemLine or \ref QCPItemCurve) have a tolerance band around them, see \ref + setSelectionTolerance. If multiple items come into consideration, the one closest to \a pos is returned. + + If \a onlySelectable is true, only items that are selectable (QCPAbstractItem::setSelectable) are + considered. + + If there is no item at \a pos, returns \c nullptr. + + \see plottableAt, layoutElementAt +*/ +template +ItemType *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const +{ + ItemType *resultItem = 0; + double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value + + foreach (QCPAbstractItem *item, mItems) + { + ItemType *currentItem = qobject_cast(item); + if (!currentItem || (onlySelectable && !currentItem->selectable())) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPAbstractItem::selectable + continue; + if (!currentItem->clipToAxisRect() || currentItem->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it + { + double currentDistance = currentItem->selectTest(pos, false); + if (currentDistance >= 0 && currentDistance < resultDistance) + { + resultItem = currentItem; + resultDistance = currentDistance; + } + } + } + + return resultItem; +} + + + /* end of 'src/core.h' */ -/* including file 'src/plottable1d.h', size 4544 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/plottable1d.h' */ +/* modified 2021-03-29T02:30:44, size 25638 */ class QCPPlottableInterface1D { public: - virtual ~QCPPlottableInterface1D() {} + virtual ~QCPPlottableInterface1D() = default; // introduced pure virtual methods: virtual int dataCount() const = 0; virtual double dataMainKey(int index) const = 0; @@ -3879,7 +4144,7 @@ class QCPAbstractPlottable1D : public QCPAbstractPlottable, public QCPPlottableI public: QCPAbstractPlottable1D(QCPAxis *keyAxis, QCPAxis *valueAxis); - virtual ~QCPAbstractPlottable1D(); + virtual ~QCPAbstractPlottable1D() Q_DECL_OVERRIDE; // virtual methods of 1d plottable interface: virtual int dataCount() const Q_DECL_OVERRIDE; @@ -3894,7 +4159,7 @@ class QCPAbstractPlottable1D : public QCPAbstractPlottable, public QCPPlottableI virtual int findEnd(double sortKey, bool expandedRange=true) const Q_DECL_OVERRIDE; // reimplemented virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; virtual QCPPlottableInterface1D *interface1D() Q_DECL_OVERRIDE { return this; } protected: @@ -3910,11 +4175,9 @@ class QCPAbstractPlottable1D : public QCPAbstractPlottable, public QCPPlottableI }; -// include implementation in header since it is a class template: -/* including file 'src/plottable1d.cpp', size 22361 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +// include implementation in header since it is a class template: //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPPlottableInterface1D //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -4249,16 +4512,16 @@ QCPDataSelection QCPAbstractPlottable1D::selectTestRect(const QRectF & if (currentSegmentBegin == -1) { if (valueRange.contains(it->mainValue()) && keyRange.contains(it->mainKey())) // start segment - currentSegmentBegin = it-mDataContainer->constBegin(); + currentSegmentBegin = int(it-mDataContainer->constBegin()); } else if (!valueRange.contains(it->mainValue()) || !keyRange.contains(it->mainKey())) // segment just ended { - result.addDataRange(QCPDataRange(currentSegmentBegin, it-mDataContainer->constBegin()), false); + result.addDataRange(QCPDataRange(currentSegmentBegin, int(it-mDataContainer->constBegin())), false); currentSegmentBegin = -1; } } // process potential last segment: if (currentSegmentBegin != -1) - result.addDataRange(QCPDataRange(currentSegmentBegin, end-mDataContainer->constBegin()), false); + result.addDataRange(QCPDataRange(currentSegmentBegin, int(end-mDataContainer->constBegin())), false); result.simplify(); return result; @@ -4270,7 +4533,7 @@ QCPDataSelection QCPAbstractPlottable1D::selectTestRect(const QRectF & template int QCPAbstractPlottable1D::findBegin(double sortKey, bool expandedRange) const { - return mDataContainer->findBegin(sortKey, expandedRange)-mDataContainer->constBegin(); + return int(mDataContainer->findBegin(sortKey, expandedRange)-mDataContainer->constBegin()); } /*! @@ -4279,7 +4542,7 @@ int QCPAbstractPlottable1D::findBegin(double sortKey, bool expandedRan template int QCPAbstractPlottable1D::findEnd(double sortKey, bool expandedRange) const { - return mDataContainer->findEnd(sortKey, expandedRange)-mDataContainer->constBegin(); + return int(mDataContainer->findEnd(sortKey, expandedRange)-mDataContainer->constBegin()); } /*! @@ -4331,7 +4594,7 @@ double QCPAbstractPlottable1D::selectTest(const QPointF &pos, bool onl if (currentDistSqr < minDistSqr) { minDistSqr = currentDistSqr; - minDistIndex = it-mDataContainer->constBegin(); + minDistIndex = int(it-mDataContainer->constBegin()); } } } @@ -4387,6 +4650,18 @@ void QCPAbstractPlottable1D::getDataSegments(QList &sele template void QCPAbstractPlottable1D::drawPolyline(QCPPainter *painter, const QVector &lineData) const { + // if drawing lines in plot (instead of PDF), reduce 1px lines to cosmetic, because at least in + // Qt6 drawing of "1px" width lines is much slower even though it has same appearance apart from + // High-DPI. In High-DPI cases people must set a pen width slightly larger than 1.0 to get + // correct DPI scaling of width, but of course with performance penalty. + if (!painter->modes().testFlag(QCPPainter::pmVectorized) && + qFuzzyCompare(painter->pen().widthF(), 1.0)) + { + QPen newPen = painter->pen(); + newPen.setWidth(0); + painter->setPen(newPen); + } + // if drawing solid line and not in PDF, use much faster line drawing instead of polyline: if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) && painter->pen().style() == Qt::SolidLine && @@ -4429,14 +4704,13 @@ void QCPAbstractPlottable1D::drawPolyline(QCPPainter *painter, const Q painter->drawPolyline(lineData.constData()+segmentStart, lineDataSize-segmentStart); } } -/* end of 'src/plottable1d.cpp' */ /* end of 'src/plottable1d.h' */ -/* including file 'src/colorgradient.h', size 6243 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/colorgradient.h' */ +/* modified 2021-03-29T02:30:44, size 7262 */ class QCP_LIB_DECL QCPColorGradient { @@ -4452,6 +4726,19 @@ class QCP_LIB_DECL QCPColorGradient }; Q_ENUMS(ColorInterpolation) + /*! + Defines how NaN data points shall appear in the plot. + + \see setNanHandling, setNanColor + */ + enum NanHandling { nhNone ///< NaN data points are not explicitly handled and shouldn't occur in the data (this gives slight performance improvement) + ,nhLowestColor ///< NaN data points appear as the lowest color defined in this QCPColorGradient + ,nhHighestColor ///< NaN data points appear as the highest color defined in this QCPColorGradient + ,nhTransparent ///< NaN data points appear transparent + ,nhNanColor ///< NaN data points appear as the color defined with \ref setNanColor + }; + Q_ENUMS(NanHandling) + /*! Defines the available presets that can be loaded with \ref loadPreset. See the documentation there for an image of the presets. @@ -4480,6 +4767,8 @@ class QCP_LIB_DECL QCPColorGradient int levelCount() const { return mLevelCount; } QMap colorStops() const { return mColorStops; } ColorInterpolation colorInterpolation() const { return mColorInterpolation; } + NanHandling nanHandling() const { return mNanHandling; } + QColor nanColor() const { return mNanColor; } bool periodic() const { return mPeriodic; } // setters: @@ -4487,6 +4776,8 @@ class QCP_LIB_DECL QCPColorGradient void setColorStops(const QMap &colorStops); void setColorStopAt(double position, const QColor &color); void setColorInterpolation(ColorInterpolation interpolation); + void setNanHandling(NanHandling handling); + void setNanColor(const QColor &color); void setPeriodic(bool enabled); // non-property methods: @@ -4502,6 +4793,8 @@ class QCP_LIB_DECL QCPColorGradient int mLevelCount; QMap mColorStops; ColorInterpolation mColorInterpolation; + NanHandling mNanHandling; + QColor mNanColor; bool mPeriodic; // non-property members: @@ -4513,13 +4806,14 @@ class QCP_LIB_DECL QCPColorGradient void updateColorBuffer(); }; Q_DECLARE_METATYPE(QCPColorGradient::ColorInterpolation) +Q_DECLARE_METATYPE(QCPColorGradient::NanHandling) Q_DECLARE_METATYPE(QCPColorGradient::GradientPreset) /* end of 'src/colorgradient.h' */ -/* including file 'src/selectiondecorator-bracket.h', size 4442 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/selectiondecorator-bracket.h' */ +/* modified 2021-03-29T02:30:44, size 4458 */ class QCP_LIB_DECL QCPSelectionDecoratorBracket : public QCPSelectionDecorator { @@ -4541,7 +4835,7 @@ class QCP_LIB_DECL QCPSelectionDecoratorBracket : public QCPSelectionDecorator Q_ENUMS(BracketStyle) QCPSelectionDecoratorBracket(); - virtual ~QCPSelectionDecoratorBracket(); + virtual ~QCPSelectionDecoratorBracket() Q_DECL_OVERRIDE; // getters: QPen bracketPen() const { return mBracketPen; } @@ -4587,8 +4881,8 @@ Q_DECLARE_METATYPE(QCPSelectionDecoratorBracket::BracketStyle) /* end of 'src/selectiondecorator-bracket.h' */ -/* including file 'src/layoutelements/layoutelement-axisrect.h', size 7507 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/layoutelements/layoutelement-axisrect.h' */ +/* modified 2021-03-29T02:30:44, size 7529 */ class QCP_LIB_DECL QCPAxisRect : public QCPLayoutElement { @@ -4602,7 +4896,7 @@ class QCP_LIB_DECL QCPAxisRect : public QCPLayoutElement /// \endcond public: explicit QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes=true); - virtual ~QCPAxisRect(); + virtual ~QCPAxisRect() Q_DECL_OVERRIDE; // getters: QPixmap background() const { return mBackgroundPixmap; } @@ -4639,7 +4933,7 @@ class QCP_LIB_DECL QCPAxisRect : public QCPLayoutElement QCPAxis *axis(QCPAxis::AxisType type, int index=0) const; QList axes(QCPAxis::AxisTypes types) const; QList axes() const; - QCPAxis *addAxis(QCPAxis::AxisType type, QCPAxis *axis=0); + QCPAxis *addAxis(QCPAxis::AxisType type, QCPAxis *axis=nullptr); QList addAxes(QCPAxis::AxisTypes types); bool removeAxis(QCPAxis *axis); QCPLayoutInset *insetLayout() const { return mInsetLayout; } @@ -4713,8 +5007,8 @@ class QCP_LIB_DECL QCPAxisRect : public QCPLayoutElement /* end of 'src/layoutelements/layoutelement-axisrect.h' */ -/* including file 'src/layoutelements/layoutelement-legend.h', size 10397 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/layoutelements/layoutelement-legend.h' */ +/* modified 2021-03-29T02:30:44, size 10425 */ class QCP_LIB_DECL QCPAbstractLegendItem : public QCPLayoutElement { @@ -4749,7 +5043,7 @@ class QCP_LIB_DECL QCPAbstractLegendItem : public QCPLayoutElement Q_SLOT void setSelected(bool selected); // reimplemented virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; signals: void selectionChanged(bool selected); @@ -4838,7 +5132,7 @@ class QCP_LIB_DECL QCPLegend : public QCPLayoutGrid Q_DECLARE_FLAGS(SelectableParts, SelectablePart) explicit QCPLegend(); - virtual ~QCPLegend(); + virtual ~QCPLegend() Q_DECL_OVERRIDE; // getters: QPen borderPen() const { return mBorderPen; } @@ -4874,7 +5168,7 @@ class QCP_LIB_DECL QCPLegend : public QCPLayoutGrid void setSelectedTextColor(const QColor &color); // reimplemented virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; // non-virtual methods: QCPAbstractLegendItem *item(int index) const; @@ -4931,8 +5225,8 @@ Q_DECLARE_METATYPE(QCPLegend::SelectablePart) /* end of 'src/layoutelements/layoutelement-legend.h' */ -/* including file 'src/layoutelements/layoutelement-textelement.h', size 5353 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/layoutelements/layoutelement-textelement.h' */ +/* modified 2021-03-29T02:30:44, size 5359 */ class QCP_LIB_DECL QCPTextElement : public QCPLayoutElement { @@ -4974,7 +5268,7 @@ class QCP_LIB_DECL QCPTextElement : public QCPLayoutElement Q_SLOT void setSelected(bool selected); // reimplemented virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE; virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE; virtual void mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE; @@ -5018,8 +5312,8 @@ class QCP_LIB_DECL QCPTextElement : public QCPLayoutElement /* end of 'src/layoutelements/layoutelement-textelement.h' */ -/* including file 'src/layoutelements/layoutelement-colorscale.h', size 5923 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/layoutelements/layoutelement-colorscale.h' */ +/* modified 2021-03-29T02:30:44, size 5939 */ class QCPColorScaleAxisRectPrivate : public QCPAxisRect @@ -5061,7 +5355,7 @@ class QCP_LIB_DECL QCPColorScale : public QCPLayoutElement /// \endcond public: explicit QCPColorScale(QCustomPlot *parentPlot); - virtual ~QCPColorScale(); + virtual ~QCPColorScale() Q_DECL_OVERRIDE; // getters: QCPAxis *axis() const { return mColorAxis.data(); } @@ -5126,8 +5420,8 @@ class QCP_LIB_DECL QCPColorScale : public QCPLayoutElement /* end of 'src/layoutelements/layoutelement-colorscale.h' */ -/* including file 'src/plottables/plottable-graph.h', size 9294 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/plottables/plottable-graph.h' */ +/* modified 2021-03-29T02:30:44, size 9316 */ class QCP_LIB_DECL QCPGraphData { @@ -5187,7 +5481,7 @@ class QCP_LIB_DECL QCPGraph : public QCPAbstractPlottable1D Q_ENUMS(LineStyle) explicit QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis); - virtual ~QCPGraph(); + virtual ~QCPGraph() Q_DECL_OVERRIDE; // getters: QSharedPointer data() const { return mDataContainer; } @@ -5211,7 +5505,7 @@ class QCP_LIB_DECL QCPGraph : public QCPAbstractPlottable1D void addData(double key, double value); // reimplemented virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE; virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE; @@ -5250,7 +5544,7 @@ class QCP_LIB_DECL QCPGraph : public QCPAbstractPlottable1D bool segmentsIntersect(double aLower, double aUpper, double bLower, double bUpper, int &bPrecedence) const; QPointF getFillBasePoint(QPointF matchingDataPoint) const; const QPolygonF getFillPolygon(const QVector *lineData, QCPDataRange segment) const; - const QPolygonF getChannelFillPolygon(const QVector *lineData, QCPDataRange thisSegment, const QVector *otherData, QCPDataRange otherSegment) const; + const QPolygonF getChannelFillPolygon(const QVector *thisData, QCPDataRange thisSegment, const QVector *otherData, QCPDataRange otherSegment) const; int findIndexBelowX(const QVector *data, double x) const; int findIndexAboveX(const QVector *data, double x) const; int findIndexBelowY(const QVector *data, double y) const; @@ -5265,8 +5559,8 @@ Q_DECLARE_METATYPE(QCPGraph::LineStyle) /* end of 'src/plottables/plottable-graph.h' */ -/* including file 'src/plottables/plottable-curve.h', size 7409 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/plottables/plottable-curve.h' */ +/* modified 2021-03-29T02:30:44, size 7434 */ class QCP_LIB_DECL QCPCurveData { @@ -5320,7 +5614,7 @@ class QCP_LIB_DECL QCPCurve : public QCPAbstractPlottable1D Q_ENUMS(LineStyle) explicit QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis); - virtual ~QCPCurve(); + virtual ~QCPCurve() Q_DECL_OVERRIDE; // getters: QSharedPointer data() const { return mDataContainer; } @@ -5343,7 +5637,7 @@ class QCP_LIB_DECL QCPCurve : public QCPAbstractPlottable1D void addData(double key, double value); // reimplemented virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE; virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE; @@ -5365,7 +5659,7 @@ class QCP_LIB_DECL QCPCurve : public QCPAbstractPlottable1D void getCurveLines(QVector *lines, const QCPDataRange &dataRange, double penWidth) const; void getScatters(QVector *scatters, const QCPDataRange &dataRange, double scatterWidth) const; int getRegion(double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const; - QPointF getOptimizedPoint(int prevRegion, double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const; + QPointF getOptimizedPoint(int otherRegion, double otherKey, double otherValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const; QVector getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const; bool mayTraverse(int prevRegion, int currentRegion) const; bool getTraverse(double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin, QPointF &crossA, QPointF &crossB) const; @@ -5380,8 +5674,8 @@ Q_DECLARE_METATYPE(QCPCurve::LineStyle) /* end of 'src/plottables/plottable-curve.h' */ -/* including file 'src/plottables/plottable-bars.h', size 8933 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/plottables/plottable-bars.h' */ +/* modified 2021-03-29T02:30:44, size 8955 */ class QCP_LIB_DECL QCPBarsGroup : public QObject { @@ -5505,7 +5799,7 @@ class QCP_LIB_DECL QCPBars : public QCPAbstractPlottable1D Q_ENUMS(WidthType) explicit QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis); - virtual ~QCPBars(); + virtual ~QCPBars() Q_DECL_OVERRIDE; // getters: double width() const { return mWidth; } @@ -5534,7 +5828,7 @@ class QCP_LIB_DECL QCPBars : public QCPAbstractPlottable1D // reimplemented virtual methods: virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE; - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE; virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE; virtual QPointF dataPixelPosition(int index) const Q_DECL_OVERRIDE; @@ -5568,8 +5862,8 @@ Q_DECLARE_METATYPE(QCPBars::WidthType) /* end of 'src/plottables/plottable-bars.h' */ -/* including file 'src/plottables/plottable-statisticalbox.h', size 7516 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/plottables/plottable-statisticalbox.h' */ +/* modified 2021-03-29T02:30:44, size 7522 */ class QCP_LIB_DECL QCPStatisticalBoxData { @@ -5652,7 +5946,7 @@ class QCP_LIB_DECL QCPStatisticalBox : public QCPAbstractPlottable1D data() const { return mDataContainer; } @@ -5923,7 +6217,7 @@ class QCP_LIB_DECL QCPFinancial : public QCPAbstractPlottable1D data() const { return mDataContainer; } QCPAbstractPlottable *dataPlottable() const { return mDataPlottable.data(); } @@ -6052,7 +6346,7 @@ class QCP_LIB_DECL QCPErrorBars : public QCPAbstractPlottable, public QCPPlottab virtual int findEnd(double sortKey, bool expandedRange=true) const Q_DECL_OVERRIDE; // reimplemented virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; virtual QCPPlottableInterface1D *interface1D() Q_DECL_OVERRIDE { return this; } protected: @@ -6085,8 +6379,8 @@ class QCP_LIB_DECL QCPErrorBars : public QCPAbstractPlottable, public QCPPlottab /* end of 'src/plottables/plottable-errorbar.h' */ -/* including file 'src/items/item-straightline.h', size 3117 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/items/item-straightline.h' */ +/* modified 2021-03-29T02:30:44, size 3137 */ class QCP_LIB_DECL QCPItemStraightLine : public QCPAbstractItem { @@ -6097,7 +6391,7 @@ class QCP_LIB_DECL QCPItemStraightLine : public QCPAbstractItem /// \endcond public: explicit QCPItemStraightLine(QCustomPlot *parentPlot); - virtual ~QCPItemStraightLine(); + virtual ~QCPItemStraightLine() Q_DECL_OVERRIDE; // getters: QPen pen() const { return mPen; } @@ -6108,7 +6402,7 @@ class QCP_LIB_DECL QCPItemStraightLine : public QCPAbstractItem void setSelectedPen(const QPen &pen); // reimplemented virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; QCPItemPosition * const point1; QCPItemPosition * const point2; @@ -6121,15 +6415,15 @@ class QCP_LIB_DECL QCPItemStraightLine : public QCPAbstractItem virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; // non-virtual methods: - QLineF getRectClippedStraightLine(const QCPVector2D &point1, const QCPVector2D &vec, const QRect &rect) const; + QLineF getRectClippedStraightLine(const QCPVector2D &base, const QCPVector2D &vec, const QRect &rect) const; QPen mainPen() const; }; /* end of 'src/items/item-straightline.h' */ -/* including file 'src/items/item-line.h', size 3407 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/items/item-line.h' */ +/* modified 2021-03-29T02:30:44, size 3429 */ class QCP_LIB_DECL QCPItemLine : public QCPAbstractItem { @@ -6142,7 +6436,7 @@ class QCP_LIB_DECL QCPItemLine : public QCPAbstractItem /// \endcond public: explicit QCPItemLine(QCustomPlot *parentPlot); - virtual ~QCPItemLine(); + virtual ~QCPItemLine() Q_DECL_OVERRIDE; // getters: QPen pen() const { return mPen; } @@ -6157,7 +6451,7 @@ class QCP_LIB_DECL QCPItemLine : public QCPAbstractItem void setTail(const QCPLineEnding &tail); // reimplemented virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; QCPItemPosition * const start; QCPItemPosition * const end; @@ -6178,8 +6472,8 @@ class QCP_LIB_DECL QCPItemLine : public QCPAbstractItem /* end of 'src/items/item-line.h' */ -/* including file 'src/items/item-curve.h', size 3379 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/items/item-curve.h' */ +/* modified 2021-03-29T02:30:44, size 3401 */ class QCP_LIB_DECL QCPItemCurve : public QCPAbstractItem { @@ -6192,7 +6486,7 @@ class QCP_LIB_DECL QCPItemCurve : public QCPAbstractItem /// \endcond public: explicit QCPItemCurve(QCustomPlot *parentPlot); - virtual ~QCPItemCurve(); + virtual ~QCPItemCurve() Q_DECL_OVERRIDE; // getters: QPen pen() const { return mPen; } @@ -6207,7 +6501,7 @@ class QCP_LIB_DECL QCPItemCurve : public QCPAbstractItem void setTail(const QCPLineEnding &tail); // reimplemented virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; QCPItemPosition * const start; QCPItemPosition * const startDir; @@ -6229,8 +6523,8 @@ class QCP_LIB_DECL QCPItemCurve : public QCPAbstractItem /* end of 'src/items/item-curve.h' */ -/* including file 'src/items/item-rect.h', size 3688 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/items/item-rect.h' */ +/* modified 2021-03-29T02:30:44, size 3710 */ class QCP_LIB_DECL QCPItemRect : public QCPAbstractItem { @@ -6243,7 +6537,7 @@ class QCP_LIB_DECL QCPItemRect : public QCPAbstractItem /// \endcond public: explicit QCPItemRect(QCustomPlot *parentPlot); - virtual ~QCPItemRect(); + virtual ~QCPItemRect() Q_DECL_OVERRIDE; // getters: QPen pen() const { return mPen; } @@ -6258,7 +6552,7 @@ class QCP_LIB_DECL QCPItemRect : public QCPAbstractItem void setSelectedBrush(const QBrush &brush); // reimplemented virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; QCPItemPosition * const topLeft; QCPItemPosition * const bottomRight; @@ -6288,8 +6582,8 @@ class QCP_LIB_DECL QCPItemRect : public QCPAbstractItem /* end of 'src/items/item-rect.h' */ -/* including file 'src/items/item-text.h', size 5554 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/items/item-text.h' */ +/* modified 2021-03-29T02:30:44, size 5576 */ class QCP_LIB_DECL QCPItemText : public QCPAbstractItem { @@ -6311,7 +6605,7 @@ class QCP_LIB_DECL QCPItemText : public QCPAbstractItem /// \endcond public: explicit QCPItemText(QCustomPlot *parentPlot); - virtual ~QCPItemText(); + virtual ~QCPItemText() Q_DECL_OVERRIDE; // getters: QColor color() const { return mColor; } @@ -6344,7 +6638,7 @@ class QCP_LIB_DECL QCPItemText : public QCPAbstractItem void setPadding(const QMargins &padding); // reimplemented virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; QCPItemPosition * const position; QCPItemAnchor * const topLeft; @@ -6385,8 +6679,8 @@ class QCP_LIB_DECL QCPItemText : public QCPAbstractItem /* end of 'src/items/item-text.h' */ -/* including file 'src/items/item-ellipse.h', size 3868 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/items/item-ellipse.h' */ +/* modified 2021-03-29T02:30:44, size 3890 */ class QCP_LIB_DECL QCPItemEllipse : public QCPAbstractItem { @@ -6399,7 +6693,7 @@ class QCP_LIB_DECL QCPItemEllipse : public QCPAbstractItem /// \endcond public: explicit QCPItemEllipse(QCustomPlot *parentPlot); - virtual ~QCPItemEllipse(); + virtual ~QCPItemEllipse() Q_DECL_OVERRIDE; // getters: QPen pen() const { return mPen; } @@ -6414,7 +6708,7 @@ class QCP_LIB_DECL QCPItemEllipse : public QCPAbstractItem void setSelectedBrush(const QBrush &brush); // reimplemented virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; QCPItemPosition * const topLeft; QCPItemPosition * const bottomRight; @@ -6447,8 +6741,8 @@ class QCP_LIB_DECL QCPItemEllipse : public QCPAbstractItem /* end of 'src/items/item-ellipse.h' */ -/* including file 'src/items/item-pixmap.h', size 4373 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/items/item-pixmap.h' */ +/* modified 2021-03-29T02:30:44, size 4407 */ class QCP_LIB_DECL QCPItemPixmap : public QCPAbstractItem { @@ -6463,7 +6757,7 @@ class QCP_LIB_DECL QCPItemPixmap : public QCPAbstractItem /// \endcond public: explicit QCPItemPixmap(QCustomPlot *parentPlot); - virtual ~QCPItemPixmap(); + virtual ~QCPItemPixmap() Q_DECL_OVERRIDE; // getters: QPixmap pixmap() const { return mPixmap; } @@ -6480,7 +6774,7 @@ class QCP_LIB_DECL QCPItemPixmap : public QCPAbstractItem void setSelectedPen(const QPen &pen); // reimplemented virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; QCPItemPosition * const topLeft; QCPItemPosition * const bottomRight; @@ -6509,15 +6803,15 @@ class QCP_LIB_DECL QCPItemPixmap : public QCPAbstractItem // non-virtual methods: void updateScaledPixmap(QRect finalRect=QRect(), bool flipHorz=false, bool flipVert=false); - QRect getFinalRect(bool *flippedHorz=0, bool *flippedVert=0) const; + QRect getFinalRect(bool *flippedHorz=nullptr, bool *flippedVert=nullptr) const; QPen mainPen() const; }; /* end of 'src/items/item-pixmap.h' */ -/* including file 'src/items/item-tracer.h', size 4762 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/items/item-tracer.h' */ +/* modified 2021-03-29T02:30:44, size 4811 */ class QCP_LIB_DECL QCPItemTracer : public QCPAbstractItem { @@ -6548,7 +6842,7 @@ class QCP_LIB_DECL QCPItemTracer : public QCPAbstractItem Q_ENUMS(TracerStyle) explicit QCPItemTracer(QCustomPlot *parentPlot); - virtual ~QCPItemTracer(); + virtual ~QCPItemTracer() Q_DECL_OVERRIDE; // getters: QPen pen() const { return mPen; } @@ -6573,7 +6867,7 @@ class QCP_LIB_DECL QCPItemTracer : public QCPAbstractItem void setInterpolating(bool enabled); // reimplemented virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; // non-virtual methods: void updatePosition(); @@ -6602,8 +6896,8 @@ Q_DECLARE_METATYPE(QCPItemTracer::TracerStyle) /* end of 'src/items/item-tracer.h' */ -/* including file 'src/items/item-bracket.h', size 3969 */ -/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ +/* including file 'src/items/item-bracket.h' */ +/* modified 2021-03-29T02:30:44, size 3991 */ class QCP_LIB_DECL QCPItemBracket : public QCPAbstractItem { @@ -6629,7 +6923,7 @@ class QCP_LIB_DECL QCPItemBracket : public QCPAbstractItem Q_ENUMS(BracketStyle) explicit QCPItemBracket(QCustomPlot *parentPlot); - virtual ~QCPItemBracket(); + virtual ~QCPItemBracket() Q_DECL_OVERRIDE; // getters: QPen pen() const { return mPen; } @@ -6644,7 +6938,7 @@ class QCP_LIB_DECL QCPItemBracket : public QCPAbstractItem void setStyle(BracketStyle style); // reimplemented virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; QCPItemPosition * const left; QCPItemPosition * const right; @@ -6669,5 +6963,775 @@ Q_DECLARE_METATYPE(QCPItemBracket::BracketStyle) /* end of 'src/items/item-bracket.h' */ +/* including file 'src/polar/radialaxis.h' */ +/* modified 2021-03-29T02:30:44, size 12227 */ + + +class QCP_LIB_DECL QCPPolarAxisRadial : public QCPLayerable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + + /// \endcond +public: + /*! + Defines the reference of the angle at which a radial axis is tilted (\ref setAngle). + */ + enum AngleReference { arAbsolute ///< The axis tilt is given in absolute degrees. The zero is to the right and positive angles are measured counter-clockwise. + ,arAngularAxis ///< The axis tilt is measured in the angular coordinate system given by the parent angular axis. + }; + Q_ENUMS(AngleReference) + /*! + Defines the scale of an axis. + \see setScaleType + */ + enum ScaleType { stLinear ///< Linear scaling + ,stLogarithmic ///< Logarithmic scaling with correspondingly transformed axis coordinates (possibly also \ref setTicker to a \ref QCPAxisTickerLog instance). + }; + Q_ENUMS(ScaleType) + /*! + Defines the selectable parts of an axis. + \see setSelectableParts, setSelectedParts + */ + enum SelectablePart { spNone = 0 ///< None of the selectable parts + ,spAxis = 0x001 ///< The axis backbone and tick marks + ,spTickLabels = 0x002 ///< Tick labels (numbers) of this axis (as a whole, not individually) + ,spAxisLabel = 0x004 ///< The axis label + }; + Q_ENUMS(SelectablePart) + Q_FLAGS(SelectableParts) + Q_DECLARE_FLAGS(SelectableParts, SelectablePart) + + enum LabelMode { lmUpright ///< + ,lmRotated ///< + }; + Q_ENUMS(LabelMode) + + explicit QCPPolarAxisRadial(QCPPolarAxisAngular *parent); + virtual ~QCPPolarAxisRadial(); + + // getters: + bool rangeDrag() const { return mRangeDrag; } + bool rangeZoom() const { return mRangeZoom; } + double rangeZoomFactor() const { return mRangeZoomFactor; } + + QCPPolarAxisAngular *angularAxis() const { return mAngularAxis; } + ScaleType scaleType() const { return mScaleType; } + const QCPRange range() const { return mRange; } + bool rangeReversed() const { return mRangeReversed; } + double angle() const { return mAngle; } + AngleReference angleReference() const { return mAngleReference; } + QSharedPointer ticker() const { return mTicker; } + bool ticks() const { return mTicks; } + bool tickLabels() const { return mTickLabels; } + int tickLabelPadding() const { return mLabelPainter.padding(); } + QFont tickLabelFont() const { return mTickLabelFont; } + QColor tickLabelColor() const { return mTickLabelColor; } + double tickLabelRotation() const { return mLabelPainter.rotation(); } + LabelMode tickLabelMode() const; + QString numberFormat() const; + int numberPrecision() const { return mNumberPrecision; } + QVector tickVector() const { return mTickVector; } + QVector subTickVector() const { return mSubTickVector; } + QVector tickVectorLabels() const { return mTickVectorLabels; } + int tickLengthIn() const; + int tickLengthOut() const; + bool subTicks() const { return mSubTicks; } + int subTickLengthIn() const; + int subTickLengthOut() const; + QPen basePen() const { return mBasePen; } + QPen tickPen() const { return mTickPen; } + QPen subTickPen() const { return mSubTickPen; } + QFont labelFont() const { return mLabelFont; } + QColor labelColor() const { return mLabelColor; } + QString label() const { return mLabel; } + int labelPadding() const; + SelectableParts selectedParts() const { return mSelectedParts; } + SelectableParts selectableParts() const { return mSelectableParts; } + QFont selectedTickLabelFont() const { return mSelectedTickLabelFont; } + QFont selectedLabelFont() const { return mSelectedLabelFont; } + QColor selectedTickLabelColor() const { return mSelectedTickLabelColor; } + QColor selectedLabelColor() const { return mSelectedLabelColor; } + QPen selectedBasePen() const { return mSelectedBasePen; } + QPen selectedTickPen() const { return mSelectedTickPen; } + QPen selectedSubTickPen() const { return mSelectedSubTickPen; } + + // setters: + void setRangeDrag(bool enabled); + void setRangeZoom(bool enabled); + void setRangeZoomFactor(double factor); + + Q_SLOT void setScaleType(QCPPolarAxisRadial::ScaleType type); + Q_SLOT void setRange(const QCPRange &range); + void setRange(double lower, double upper); + void setRange(double position, double size, Qt::AlignmentFlag alignment); + void setRangeLower(double lower); + void setRangeUpper(double upper); + void setRangeReversed(bool reversed); + void setAngle(double degrees); + void setAngleReference(AngleReference reference); + void setTicker(QSharedPointer ticker); + void setTicks(bool show); + void setTickLabels(bool show); + void setTickLabelPadding(int padding); + void setTickLabelFont(const QFont &font); + void setTickLabelColor(const QColor &color); + void setTickLabelRotation(double degrees); + void setTickLabelMode(LabelMode mode); + void setNumberFormat(const QString &formatCode); + void setNumberPrecision(int precision); + void setTickLength(int inside, int outside=0); + void setTickLengthIn(int inside); + void setTickLengthOut(int outside); + void setSubTicks(bool show); + void setSubTickLength(int inside, int outside=0); + void setSubTickLengthIn(int inside); + void setSubTickLengthOut(int outside); + void setBasePen(const QPen &pen); + void setTickPen(const QPen &pen); + void setSubTickPen(const QPen &pen); + void setLabelFont(const QFont &font); + void setLabelColor(const QColor &color); + void setLabel(const QString &str); + void setLabelPadding(int padding); + void setSelectedTickLabelFont(const QFont &font); + void setSelectedLabelFont(const QFont &font); + void setSelectedTickLabelColor(const QColor &color); + void setSelectedLabelColor(const QColor &color); + void setSelectedBasePen(const QPen &pen); + void setSelectedTickPen(const QPen &pen); + void setSelectedSubTickPen(const QPen &pen); + Q_SLOT void setSelectableParts(const QCPPolarAxisRadial::SelectableParts &selectableParts); + Q_SLOT void setSelectedParts(const QCPPolarAxisRadial::SelectableParts &selectedParts); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + + // non-property methods: + void moveRange(double diff); + void scaleRange(double factor); + void scaleRange(double factor, double center); + void rescale(bool onlyVisiblePlottables=false); + void pixelToCoord(QPointF pixelPos, double &angleCoord, double &radiusCoord) const; + QPointF coordToPixel(double angleCoord, double radiusCoord) const; + double coordToRadius(double coord) const; + double radiusToCoord(double radius) const; + SelectablePart getPartAt(const QPointF &pos) const; + +signals: + void rangeChanged(const QCPRange &newRange); + void rangeChanged(const QCPRange &newRange, const QCPRange &oldRange); + void scaleTypeChanged(QCPPolarAxisRadial::ScaleType scaleType); + void selectionChanged(const QCPPolarAxisRadial::SelectableParts &parts); + void selectableChanged(const QCPPolarAxisRadial::SelectableParts &parts); + +protected: + // property members: + bool mRangeDrag; + bool mRangeZoom; + double mRangeZoomFactor; + + // axis base: + QCPPolarAxisAngular *mAngularAxis; + double mAngle; + AngleReference mAngleReference; + SelectableParts mSelectableParts, mSelectedParts; + QPen mBasePen, mSelectedBasePen; + // axis label: + int mLabelPadding; + QString mLabel; + QFont mLabelFont, mSelectedLabelFont; + QColor mLabelColor, mSelectedLabelColor; + // tick labels: + //int mTickLabelPadding; in label painter + bool mTickLabels; + //double mTickLabelRotation; in label painter + QFont mTickLabelFont, mSelectedTickLabelFont; + QColor mTickLabelColor, mSelectedTickLabelColor; + int mNumberPrecision; + QLatin1Char mNumberFormatChar; + bool mNumberBeautifulPowers; + bool mNumberMultiplyCross; + // ticks and subticks: + bool mTicks; + bool mSubTicks; + int mTickLengthIn, mTickLengthOut, mSubTickLengthIn, mSubTickLengthOut; + QPen mTickPen, mSelectedTickPen; + QPen mSubTickPen, mSelectedSubTickPen; + // scale and range: + QCPRange mRange; + bool mRangeReversed; + ScaleType mScaleType; + + // non-property members: + QPointF mCenter; + double mRadius; + QSharedPointer mTicker; + QVector mTickVector; + QVector mTickVectorLabels; + QVector mSubTickVector; + bool mDragging; + QCPRange mDragStartRange; + QCP::AntialiasedElements mAADragBackup, mNotAADragBackup; + QCPLabelPainterPrivate mLabelPainter; + + // reimplemented virtual methods: + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE; + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE; + // events: + virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE; + virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE; + // mouse events: + virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE; + virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE; + virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE; + virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; + + // non-virtual methods: + void updateGeometry(const QPointF ¢er, double radius); + void setupTickVectors(); + QPen getBasePen() const; + QPen getTickPen() const; + QPen getSubTickPen() const; + QFont getTickLabelFont() const; + QFont getLabelFont() const; + QColor getTickLabelColor() const; + QColor getLabelColor() const; + +private: + Q_DISABLE_COPY(QCPPolarAxisRadial) + + friend class QCustomPlot; + friend class QCPPolarAxisAngular; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QCPPolarAxisRadial::SelectableParts) +Q_DECLARE_METATYPE(QCPPolarAxisRadial::AngleReference) +Q_DECLARE_METATYPE(QCPPolarAxisRadial::ScaleType) +Q_DECLARE_METATYPE(QCPPolarAxisRadial::SelectablePart) + + + +/* end of 'src/polar/radialaxis.h' */ + + +/* including file 'src/polar/layoutelement-angularaxis.h' */ +/* modified 2021-03-29T02:30:44, size 13461 */ + +class QCP_LIB_DECL QCPPolarAxisAngular : public QCPLayoutElement +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + + /// \endcond +public: + /*! + Defines the selectable parts of an axis. + \see setSelectableParts, setSelectedParts + */ + enum SelectablePart { spNone = 0 ///< None of the selectable parts + ,spAxis = 0x001 ///< The axis backbone and tick marks + ,spTickLabels = 0x002 ///< Tick labels (numbers) of this axis (as a whole, not individually) + ,spAxisLabel = 0x004 ///< The axis label + }; + Q_ENUMS(SelectablePart) + Q_FLAGS(SelectableParts) + Q_DECLARE_FLAGS(SelectableParts, SelectablePart) + + /*! + TODO + */ + enum LabelMode { lmUpright ///< + ,lmRotated ///< + }; + Q_ENUMS(LabelMode) + + explicit QCPPolarAxisAngular(QCustomPlot *parentPlot); + virtual ~QCPPolarAxisAngular(); + + // getters: + QPixmap background() const { return mBackgroundPixmap; } + QBrush backgroundBrush() const { return mBackgroundBrush; } + bool backgroundScaled() const { return mBackgroundScaled; } + Qt::AspectRatioMode backgroundScaledMode() const { return mBackgroundScaledMode; } + bool rangeDrag() const { return mRangeDrag; } + bool rangeZoom() const { return mRangeZoom; } + double rangeZoomFactor() const { return mRangeZoomFactor; } + + const QCPRange range() const { return mRange; } + bool rangeReversed() const { return mRangeReversed; } + double angle() const { return mAngle; } + QSharedPointer ticker() const { return mTicker; } + bool ticks() const { return mTicks; } + bool tickLabels() const { return mTickLabels; } + int tickLabelPadding() const { return mLabelPainter.padding(); } + QFont tickLabelFont() const { return mTickLabelFont; } + QColor tickLabelColor() const { return mTickLabelColor; } + double tickLabelRotation() const { return mLabelPainter.rotation(); } + LabelMode tickLabelMode() const; + QString numberFormat() const; + int numberPrecision() const { return mNumberPrecision; } + QVector tickVector() const { return mTickVector; } + QVector tickVectorLabels() const { return mTickVectorLabels; } + int tickLengthIn() const { return mTickLengthIn; } + int tickLengthOut() const { return mTickLengthOut; } + bool subTicks() const { return mSubTicks; } + int subTickLengthIn() const { return mSubTickLengthIn; } + int subTickLengthOut() const { return mSubTickLengthOut; } + QPen basePen() const { return mBasePen; } + QPen tickPen() const { return mTickPen; } + QPen subTickPen() const { return mSubTickPen; } + QFont labelFont() const { return mLabelFont; } + QColor labelColor() const { return mLabelColor; } + QString label() const { return mLabel; } + int labelPadding() const { return mLabelPadding; } + SelectableParts selectedParts() const { return mSelectedParts; } + SelectableParts selectableParts() const { return mSelectableParts; } + QFont selectedTickLabelFont() const { return mSelectedTickLabelFont; } + QFont selectedLabelFont() const { return mSelectedLabelFont; } + QColor selectedTickLabelColor() const { return mSelectedTickLabelColor; } + QColor selectedLabelColor() const { return mSelectedLabelColor; } + QPen selectedBasePen() const { return mSelectedBasePen; } + QPen selectedTickPen() const { return mSelectedTickPen; } + QPen selectedSubTickPen() const { return mSelectedSubTickPen; } + QCPPolarGrid *grid() const { return mGrid; } + + // setters: + void setBackground(const QPixmap &pm); + void setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode=Qt::KeepAspectRatioByExpanding); + void setBackground(const QBrush &brush); + void setBackgroundScaled(bool scaled); + void setBackgroundScaledMode(Qt::AspectRatioMode mode); + void setRangeDrag(bool enabled); + void setRangeZoom(bool enabled); + void setRangeZoomFactor(double factor); + + Q_SLOT void setRange(const QCPRange &range); + void setRange(double lower, double upper); + void setRange(double position, double size, Qt::AlignmentFlag alignment); + void setRangeLower(double lower); + void setRangeUpper(double upper); + void setRangeReversed(bool reversed); + void setAngle(double degrees); + void setTicker(QSharedPointer ticker); + void setTicks(bool show); + void setTickLabels(bool show); + void setTickLabelPadding(int padding); + void setTickLabelFont(const QFont &font); + void setTickLabelColor(const QColor &color); + void setTickLabelRotation(double degrees); + void setTickLabelMode(LabelMode mode); + void setNumberFormat(const QString &formatCode); + void setNumberPrecision(int precision); + void setTickLength(int inside, int outside=0); + void setTickLengthIn(int inside); + void setTickLengthOut(int outside); + void setSubTicks(bool show); + void setSubTickLength(int inside, int outside=0); + void setSubTickLengthIn(int inside); + void setSubTickLengthOut(int outside); + void setBasePen(const QPen &pen); + void setTickPen(const QPen &pen); + void setSubTickPen(const QPen &pen); + void setLabelFont(const QFont &font); + void setLabelColor(const QColor &color); + void setLabel(const QString &str); + void setLabelPadding(int padding); + void setLabelPosition(Qt::AlignmentFlag position); + void setSelectedTickLabelFont(const QFont &font); + void setSelectedLabelFont(const QFont &font); + void setSelectedTickLabelColor(const QColor &color); + void setSelectedLabelColor(const QColor &color); + void setSelectedBasePen(const QPen &pen); + void setSelectedTickPen(const QPen &pen); + void setSelectedSubTickPen(const QPen &pen); + Q_SLOT void setSelectableParts(const QCPPolarAxisAngular::SelectableParts &selectableParts); + Q_SLOT void setSelectedParts(const QCPPolarAxisAngular::SelectableParts &selectedParts); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual void update(UpdatePhase phase) Q_DECL_OVERRIDE; + virtual QList elements(bool recursive) const Q_DECL_OVERRIDE; + + // non-property methods: + bool removeGraph(QCPPolarGraph *graph); + int radialAxisCount() const; + QCPPolarAxisRadial *radialAxis(int index=0) const; + QList radialAxes() const; + QCPPolarAxisRadial *addRadialAxis(QCPPolarAxisRadial *axis=0); + bool removeRadialAxis(QCPPolarAxisRadial *axis); + QCPLayoutInset *insetLayout() const { return mInsetLayout; } + QRegion exactClipRegion() const; + + void moveRange(double diff); + void scaleRange(double factor); + void scaleRange(double factor, double center); + void rescale(bool onlyVisiblePlottables=false); + double coordToAngleRad(double coord) const { return mAngleRad+(coord-mRange.lower)/mRange.size()*(mRangeReversed ? -2.0*M_PI : 2.0*M_PI); } // mention in doc that return doesn't wrap + double angleRadToCoord(double angleRad) const { return mRange.lower+(angleRad-mAngleRad)/(mRangeReversed ? -2.0*M_PI : 2.0*M_PI)*mRange.size(); } + void pixelToCoord(QPointF pixelPos, double &angleCoord, double &radiusCoord) const; + QPointF coordToPixel(double angleCoord, double radiusCoord) const; + SelectablePart getPartAt(const QPointF &pos) const; + + // read-only interface imitating a QRect: + int left() const { return mRect.left(); } + int right() const { return mRect.right(); } + int top() const { return mRect.top(); } + int bottom() const { return mRect.bottom(); } + int width() const { return mRect.width(); } + int height() const { return mRect.height(); } + QSize size() const { return mRect.size(); } + QPoint topLeft() const { return mRect.topLeft(); } + QPoint topRight() const { return mRect.topRight(); } + QPoint bottomLeft() const { return mRect.bottomLeft(); } + QPoint bottomRight() const { return mRect.bottomRight(); } + QPointF center() const { return mCenter; } + double radius() const { return mRadius; } + +signals: + void rangeChanged(const QCPRange &newRange); + void rangeChanged(const QCPRange &newRange, const QCPRange &oldRange); + void selectionChanged(const QCPPolarAxisAngular::SelectableParts &parts); + void selectableChanged(const QCPPolarAxisAngular::SelectableParts &parts); + +protected: + // property members: + QBrush mBackgroundBrush; + QPixmap mBackgroundPixmap; + QPixmap mScaledBackgroundPixmap; + bool mBackgroundScaled; + Qt::AspectRatioMode mBackgroundScaledMode; + QCPLayoutInset *mInsetLayout; + bool mRangeDrag; + bool mRangeZoom; + double mRangeZoomFactor; + + // axis base: + double mAngle, mAngleRad; + SelectableParts mSelectableParts, mSelectedParts; + QPen mBasePen, mSelectedBasePen; + // axis label: + int mLabelPadding; + QString mLabel; + QFont mLabelFont, mSelectedLabelFont; + QColor mLabelColor, mSelectedLabelColor; + // tick labels: + //int mTickLabelPadding; in label painter + bool mTickLabels; + //double mTickLabelRotation; in label painter + QFont mTickLabelFont, mSelectedTickLabelFont; + QColor mTickLabelColor, mSelectedTickLabelColor; + int mNumberPrecision; + QLatin1Char mNumberFormatChar; + bool mNumberBeautifulPowers; + bool mNumberMultiplyCross; + // ticks and subticks: + bool mTicks; + bool mSubTicks; + int mTickLengthIn, mTickLengthOut, mSubTickLengthIn, mSubTickLengthOut; + QPen mTickPen, mSelectedTickPen; + QPen mSubTickPen, mSelectedSubTickPen; + // scale and range: + QCPRange mRange; + bool mRangeReversed; + + // non-property members: + QPointF mCenter; + double mRadius; + QList mRadialAxes; + QCPPolarGrid *mGrid; + QList mGraphs; + QSharedPointer mTicker; + QVector mTickVector; + QVector mTickVectorLabels; + QVector mTickVectorCosSin; + QVector mSubTickVector; + QVector mSubTickVectorCosSin; + bool mDragging; + QCPRange mDragAngularStart; + QList mDragRadialStart; + QCP::AntialiasedElements mAADragBackup, mNotAADragBackup; + QCPLabelPainterPrivate mLabelPainter; + + // reimplemented virtual methods: + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE; + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE; + // events: + virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE; + virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE; + virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE; + virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; + + // non-virtual methods: + bool registerPolarGraph(QCPPolarGraph *graph); + void drawBackground(QCPPainter *painter, const QPointF ¢er, double radius); + void setupTickVectors(); + QPen getBasePen() const; + QPen getTickPen() const; + QPen getSubTickPen() const; + QFont getTickLabelFont() const; + QFont getLabelFont() const; + QColor getTickLabelColor() const; + QColor getLabelColor() const; + +private: + Q_DISABLE_COPY(QCPPolarAxisAngular) + + friend class QCustomPlot; + friend class QCPPolarGrid; + friend class QCPPolarGraph; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QCPPolarAxisAngular::SelectableParts) +Q_DECLARE_METATYPE(QCPPolarAxisAngular::SelectablePart) + +/* end of 'src/polar/layoutelement-angularaxis.h' */ + + +/* including file 'src/polar/polargrid.h' */ +/* modified 2021-03-29T02:30:44, size 4506 */ + +class QCP_LIB_DECL QCPPolarGrid :public QCPLayerable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + + /// \endcond +public: + /*! + TODO + */ + enum GridType { gtAngular = 0x01 ///< + ,gtRadial = 0x02 ///< + ,gtAll = 0xFF ///< + ,gtNone = 0x00 ///< + }; + Q_ENUMS(GridType) + Q_FLAGS(GridTypes) + Q_DECLARE_FLAGS(GridTypes, GridType) + + explicit QCPPolarGrid(QCPPolarAxisAngular *parentAxis); + + // getters: + QCPPolarAxisRadial *radialAxis() const { return mRadialAxis.data(); } + GridTypes type() const { return mType; } + GridTypes subGridType() const { return mSubGridType; } + bool antialiasedSubGrid() const { return mAntialiasedSubGrid; } + bool antialiasedZeroLine() const { return mAntialiasedZeroLine; } + QPen angularPen() const { return mAngularPen; } + QPen angularSubGridPen() const { return mAngularSubGridPen; } + QPen radialPen() const { return mRadialPen; } + QPen radialSubGridPen() const { return mRadialSubGridPen; } + QPen radialZeroLinePen() const { return mRadialZeroLinePen; } + + // setters: + void setRadialAxis(QCPPolarAxisRadial *axis); + void setType(GridTypes type); + void setSubGridType(GridTypes type); + void setAntialiasedSubGrid(bool enabled); + void setAntialiasedZeroLine(bool enabled); + void setAngularPen(const QPen &pen); + void setAngularSubGridPen(const QPen &pen); + void setRadialPen(const QPen &pen); + void setRadialSubGridPen(const QPen &pen); + void setRadialZeroLinePen(const QPen &pen); + +protected: + // property members: + GridTypes mType; + GridTypes mSubGridType; + bool mAntialiasedSubGrid, mAntialiasedZeroLine; + QPen mAngularPen, mAngularSubGridPen; + QPen mRadialPen, mRadialSubGridPen, mRadialZeroLinePen; + + // non-property members: + QCPPolarAxisAngular *mParentAxis; + QPointer mRadialAxis; + + // reimplemented virtual methods: + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE; + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + + // non-virtual methods: + void drawRadialGrid(QCPPainter *painter, const QPointF ¢er, const QVector &coords, const QPen &pen, const QPen &zeroPen=Qt::NoPen); + void drawAngularGrid(QCPPainter *painter, const QPointF ¢er, double radius, const QVector &ticksCosSin, const QPen &pen); + +private: + Q_DISABLE_COPY(QCPPolarGrid) + +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QCPPolarGrid::GridTypes) +Q_DECLARE_METATYPE(QCPPolarGrid::GridType) + + +/* end of 'src/polar/polargrid.h' */ + + +/* including file 'src/polar/polargraph.h' */ +/* modified 2021-03-29T02:30:44, size 9606 */ + + +class QCP_LIB_DECL QCPPolarLegendItem : public QCPAbstractLegendItem +{ + Q_OBJECT +public: + QCPPolarLegendItem(QCPLegend *parent, QCPPolarGraph *graph); + + // getters: + QCPPolarGraph *polarGraph() { return mPolarGraph; } + +protected: + // property members: + QCPPolarGraph *mPolarGraph; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual QSize minimumOuterSizeHint() const Q_DECL_OVERRIDE; + + // non-virtual methods: + QPen getIconBorderPen() const; + QColor getTextColor() const; + QFont getFont() const; +}; + + +class QCP_LIB_DECL QCPPolarGraph : public QCPLayerable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + + /// \endcond +public: + /*! + Defines how the graph's line is represented visually in the plot. The line is drawn with the + current pen of the graph (\ref setPen). + \see setLineStyle + */ + enum LineStyle { lsNone ///< data points are not connected with any lines (e.g. data only represented + ///< with symbols according to the scatter style, see \ref setScatterStyle) + ,lsLine ///< data points are connected by a straight line + }; + Q_ENUMS(LineStyle) + + QCPPolarGraph(QCPPolarAxisAngular *keyAxis, QCPPolarAxisRadial *valueAxis); + virtual ~QCPPolarGraph(); + + // getters: + QString name() const { return mName; } + bool antialiasedFill() const { return mAntialiasedFill; } + bool antialiasedScatters() const { return mAntialiasedScatters; } + QPen pen() const { return mPen; } + QBrush brush() const { return mBrush; } + bool periodic() const { return mPeriodic; } + QCPPolarAxisAngular *keyAxis() const { return mKeyAxis.data(); } + QCPPolarAxisRadial *valueAxis() const { return mValueAxis.data(); } + QCP::SelectionType selectable() const { return mSelectable; } + bool selected() const { return !mSelection.isEmpty(); } + QCPDataSelection selection() const { return mSelection; } + //QCPSelectionDecorator *selectionDecorator() const { return mSelectionDecorator; } + QSharedPointer data() const { return mDataContainer; } + LineStyle lineStyle() const { return mLineStyle; } + QCPScatterStyle scatterStyle() const { return mScatterStyle; } + + // setters: + void setName(const QString &name); + void setAntialiasedFill(bool enabled); + void setAntialiasedScatters(bool enabled); + void setPen(const QPen &pen); + void setBrush(const QBrush &brush); + void setPeriodic(bool enabled); + void setKeyAxis(QCPPolarAxisAngular *axis); + void setValueAxis(QCPPolarAxisRadial *axis); + Q_SLOT void setSelectable(QCP::SelectionType selectable); + Q_SLOT void setSelection(QCPDataSelection selection); + //void setSelectionDecorator(QCPSelectionDecorator *decorator); + void setData(QSharedPointer data); + void setData(const QVector &keys, const QVector &values, bool alreadySorted=false); + void setLineStyle(LineStyle ls); + void setScatterStyle(const QCPScatterStyle &style); + + // non-property methods: + void addData(const QVector &keys, const QVector &values, bool alreadySorted=false); + void addData(double key, double value); + void coordsToPixels(double key, double value, double &x, double &y) const; + const QPointF coordsToPixels(double key, double value) const; + void pixelsToCoords(double x, double y, double &key, double &value) const; + void pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const; + void rescaleAxes(bool onlyEnlarge=false) const; + void rescaleKeyAxis(bool onlyEnlarge=false) const; + void rescaleValueAxis(bool onlyEnlarge=false, bool inKeyRange=false) const; + bool addToLegend(QCPLegend *legend); + bool addToLegend(); + bool removeFromLegend(QCPLegend *legend) const; + bool removeFromLegend() const; + + // introduced virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; // actually introduced in QCPLayerable as non-pure, but we want to force reimplementation for plottables + virtual QCPPlottableInterface1D *interface1D() { return 0; } // TODO: return this later, when QCPAbstractPolarPlottable is created + virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const; + virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const; + +signals: + void selectionChanged(bool selected); + void selectionChanged(const QCPDataSelection &selection); + void selectableChanged(QCP::SelectionType selectable); + +protected: + // property members: + QSharedPointer mDataContainer; + LineStyle mLineStyle; + QCPScatterStyle mScatterStyle; + QString mName; + bool mAntialiasedFill, mAntialiasedScatters; + QPen mPen; + QBrush mBrush; + bool mPeriodic; + QPointer mKeyAxis; + QPointer mValueAxis; + QCP::SelectionType mSelectable; + QCPDataSelection mSelection; + //QCPSelectionDecorator *mSelectionDecorator; + + // introduced virtual methods (later reimplemented TODO from QCPAbstractPolarPlottable): + virtual QRect clipRect() const; + virtual void draw(QCPPainter *painter); + virtual QCP::Interaction selectionCategory() const; + void applyDefaultAntialiasingHint(QCPPainter *painter) const; + // events: + virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged); + virtual void deselectEvent(bool *selectionStateChanged); + // virtual drawing helpers: + virtual void drawLinePlot(QCPPainter *painter, const QVector &lines) const; + virtual void drawFill(QCPPainter *painter, QVector *lines) const; + virtual void drawScatterPlot(QCPPainter *painter, const QVector &scatters, const QCPScatterStyle &style) const; + + // introduced virtual methods: + virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const; + + // non-virtual methods: + void applyFillAntialiasingHint(QCPPainter *painter) const; + void applyScattersAntialiasingHint(QCPPainter *painter) const; + double pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer::const_iterator &closestData) const; + // drawing helpers: + virtual int dataCount() const; + void getDataSegments(QList &selectedSegments, QList &unselectedSegments) const; + void drawPolyline(QCPPainter *painter, const QVector &lineData) const; + void getVisibleDataBounds(QCPGraphDataContainer::const_iterator &begin, QCPGraphDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const; + void getLines(QVector *lines, const QCPDataRange &dataRange) const; + void getScatters(QVector *scatters, const QCPDataRange &dataRange) const; + void getOptimizedLineData(QVector *lineData, const QCPGraphDataContainer::const_iterator &begin, const QCPGraphDataContainer::const_iterator &end) const; + void getOptimizedScatterData(QVector *scatterData, QCPGraphDataContainer::const_iterator begin, QCPGraphDataContainer::const_iterator end) const; + QVector dataToLines(const QVector &data) const; + +private: + Q_DISABLE_COPY(QCPPolarGraph) + + friend class QCPPolarLegendItem; +}; + +/* end of 'src/polar/polargraph.h' */ + + #endif // QCUSTOMPLOT_H