pax_global_header 0000666 0000000 0000000 00000000064 14173312715 0014516 g ustar 00root root 0000000 0000000 52 comment=76f68fb70731c74f195884c51168cffceae575b7
orson-charts-2.1.0/ 0000775 0000000 0000000 00000000000 14173312715 0014140 5 ustar 00root root 0000000 0000000 orson-charts-2.1.0/.github/ 0000775 0000000 0000000 00000000000 14173312715 0015500 5 ustar 00root root 0000000 0000000 orson-charts-2.1.0/.github/workflows/ 0000775 0000000 0000000 00000000000 14173312715 0017535 5 ustar 00root root 0000000 0000000 orson-charts-2.1.0/.github/workflows/ci.yaml 0000664 0000000 0000000 00000001053 14173312715 0021013 0 ustar 00root root 0000000 0000000 ---
name: Java CI
on: [push]
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-18.04, macOS-latest, windows-2016]
java: [11, 11.0.3, 12, 13, 13.0.4, 14, 15, 16-ea]
fail-fast: false
max-parallel: 4
name: Test JDK ${{ matrix.java }}, ${{ matrix.os }}
steps:
- uses: actions/checkout@v1
- name: Set up JDK
uses: actions/setup-java@v1
with:
java-version: ${{ matrix.java }}
- name: Test with Maven
run: mvn test -B --file pom.xml
... orson-charts-2.1.0/.gitignore 0000664 0000000 0000000 00000000151 14173312715 0016125 0 ustar 00root root 0000000 0000000 *.class
# Package Files #
*.jar
*.war
*.ear
/target/
*.DS_Store
.idea
nb-configuration.xml
nbactions.xml orson-charts-2.1.0/LICENSE 0000664 0000000 0000000 00000104515 14173312715 0015153 0 ustar 00root root 0000000 0000000 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
.
orson-charts-2.1.0/README.md 0000664 0000000 0000000 00000015125 14173312715 0015423 0 ustar 00root root 0000000 0000000 Orson Charts
============
(C)opyright 2013-2022, by David Gilbert. All rights reserved.
Version 2.1.0, 23 January 2022.
### Overview
Orson Charts is a 3D chart library for the Java™ platform that can generate a wide variety of 3D charts for use in client-side applications (JavaFX and Swing) and server-side applications (with export to PDF, SVG, PNG and JPEG).
Key features include:
- multiple chart types: pie charts, bar charts (regular and stacked), line charts, area charts, scatter charts and surface plots;
- mouse-enabled chart viewers in JavaFX and Swing provide 360 degree rotation and zooming for precise end-user view control;
- configurable tool tip support;
- interactive charts (mouse event support on all chart elements);
- flexible data sources;
- JSON format data import and export;
- regular and logarithmic axis scales;
- auto-adaptive axis labeling;
- value and range marker annotations
- support for PDF, SVG, PNG and JPG export of charts for reporting;
- a clean and well-documented API with a high degree of chart configurability.
Orson Charts is very easy to use, and includes comprehensive [Javadocs](https://www.javadoc.io/doc/org.jfree/org.jfree.chart3d/latest/org.jfree.chart3d/module-summary.html). It is licensed under the terms of the GNU General Public License version 3 or later. Orson Charts requires JDK/JRE 11 or later. To use Orson Charts with JavaFX requires the [Orson Charts FX](https://github.com/jfree/orson-charts-fx) extension project.
### Demos
Demo applications can be found in the following projects at GitHub:
* [JFree-Demos](https://github.com/jfree/jfree-demos "JFree-Demos Project Page at GitHub")
* [JFree-FXDemos](https://github.com/jfree/jfree-fxdemos "JFree-FXDemos Project Page at GitHub")
### Building
You can build Orson Charts using Maven by issuing the following command from the root directory of the project:
mvn clean install
The build requires JDK 11 or later.
### Reporting Bugs
If you find a bug in Orson Charts, please file a bug report at:
https://github.com/jfree/orson-charts/issues
### History
#### Version 2.1.0 : 23-Jan-2022
- bug fix - axis ranges not being updated correctly. See issue [#9](https://github.com/jfree/orson-charts/issues/9)
- clean-up - fix numerous warnings/hints highlighted by IntelliJ IDEA.
- dependencies - upgraded JUnit to version 5;
- build - set plugin versions in `pom.xml`.
#### Version 2.0 : 15-Mar-2020
- created a Java module (`org.jfree.chart3d`);
- refactored into `org.jfree.chart3d` namespace;
- fixed bug [#4](https://github.com/jfree/orson-charts/issues/4) in `NumberAxis3D` where tick label override is not applied.
#### Version 1.7 : 17-Nov-2017
- removed JavaFX support to a separate project [Orson Charts FX](https://github.com/jfree/orson-charts-fx);
- fixed cell content bug in `GridElement`;
- fixed bug in `GradientColorScale`;
- protect from `NullPointerException` in `AbstractValueAxis3D`;
- streamline build by removing Ant build support and moving demo code to external projects.
#### Version 1.6 : 2-Nov-2016
- added `remove()` method to `XYZSeries` and added change notification mechanism;
- added `remove()` and `removeAll()` methods to `XYZSeriesCollection` and added change notification;
- added generics to source files;
- updated `FXGraphics2D` to version 1.5;
- updated `JFreeSVG` to version 3.2.
#### Version 1.5 : 28-Jan-2016
- added new `LineXYZRenderer`;
- added option to invert axes;
- fix exception when setting a new dataset using `CategoryPlot3D.setDataset()`:
- fix direction of mouse wheel zooming in JavaFX;
- included FXGraphics2D version 1.3 as a dependency;
- updated OrsonPDF to version 1.7;
- updated JFreeSVG to version 3.0;
- added `pom.xml` for Maven builds;
- JavaFX demos brought up to match the Swing demos;
- various Javadoc improvements.
#### Version 1.4 : 27-May-2014
- added JavaFX support;
- added support to marker elements and item labels for `KEY_BEGIN_ELEMENT` and `KEY_END_ELEMENT` rendering hints;
- added `JPEG` export option;
- added `minAutoRangeLength` attribute in `AbstractValueAxis3D` (this fixes a bug for plots where the length of the data range is zero, for example scatter plots with a single value);
- fixed endless loop in axis range code for datasets with infinite values;
- fixed bug in hinting for tick labels on `NumberAxis3D`;
- fixed `Utils.js` functions that didn't work with Internet Explorer 9.
#### Version 1.3 : 11-Apr-2014
- added chart mouse event and tooltip support for the chart viewer in Swing;
- added item label support;
- added `JSON` format data import and export;
- new utility methods to extract an `XYZDataset` from a `CategoryDataset3D`;
- fixed a clipping issue for panels with borders assigned;
- added rendering hints for SVG output via JFreeSVG (to support tool-tips
and mouse events on chart elements);
- added JavaScript utility library to support JFreeSVG export;
#### Version 1.2 : 7-Mar-2014
- added value and range markers for numerical axes, and category markers for category axes;
- added a `tickLabelOrientation` attribute for axes so that tick labels can be drawn either perpendicular or parallel to the axis line;
- added a logarithmic axis;
- added theme support, with several built-in themes;
- added template driven label generators for pie section labels and category axis labels;
- added export to `JPEG`, plus options to configure the available export types;
- optimized the rendering code to reduce memory usage;
- put in place a localisation mechanism, and added German and Italian translations;
- made the projection distance configurable in the chart viewer;
- added series accessors for `XYZSeriesCollection`;
- added `yDimensionOverride` attribute for `CategoryPlot3D`;
- fixed an issue with the `StackedBarRenderer` and negative values;
- incorporated various other bug fixes.
#### Version 1.1 : 23-Dec-2013
- added surface plots (via the new `SurfaceRenderer` class and `Chart3DFactory.createSurfaceChart()`);
- added `ColorScale` and supporting classes for use by the surface charts;
- added orientation attribute to control the chart legend orientation;
- optimized rendering code for improved performance;
- added missing change events for gridline attributes in `CategoryPlot3D`;
- added constants including `SCALE_TO_FIT_TARGET` and `CENTER_NO_SCALING` to the `Fit2D` class;
- added `setColors(Color...)` to `AbstractCategoryRenderer3D` and `AbstractXYZRenderer3D`;
#### Version 1.0 : 17-Nov-2013
This is the first public release of Orson Charts, we welcome your feedback and suggestions.
orson-charts-2.1.0/js/ 0000775 0000000 0000000 00000000000 14173312715 0014554 5 ustar 00root root 0000000 0000000 orson-charts-2.1.0/js/README.txt 0000664 0000000 0000000 00000000736 14173312715 0016260 0 ustar 00root root 0000000 0000000 JavaScript Utilities
--------------------
This directory contains some JavaScript utility code that can be used when exporting charts to SVG format with JFreeSVG. The build.sh script will build the library, but note that it requires the Google Closure Compiler jar file to be on the classpath (please edit the script accordingly).
https://developers.google.com/closure/compiler/
Look in the 'svg' directory (open SVGDemo1.html) to see a demo that uses this JavaScript code.
orson-charts-2.1.0/js/build.sh 0000775 0000000 0000000 00000000521 14173312715 0016210 0 ustar 00root root 0000000 0000000 #!/bin/bash
java -jar ~/jars/compiler.jar --warning_level VERBOSE --compilation_level WHITESPACE_ONLY \
--js src/Utils.js \
--js src/KeyedValuesDataset.js \
--js src/KeyedValues3DDataset.js \
--js src/XYZDataset.js \
--js src/KeyedValueLabels.js \
--js src/KeyedValue3DLabels.js \
--js src/XYZLabels.js \
--js_output_file orsoncharts.js orson-charts-2.1.0/js/orsoncharts.js 0000664 0000000 0000000 00000031015 14173312715 0017457 0 ustar 00root root 0000000 0000000 var orsoncharts;if(!orsoncharts)orsoncharts={};orsoncharts.Utils={};orsoncharts.Utils.makeArrayOf=function(value,length){var arr=[],i=length;while(i--)arr[i]=value;return arr};orsoncharts.Utils.findChartRef=function(element){var id=element.getAttribute("jfreesvg:ref");var found=false;var current=element;while(!found){current=current.parentNode;if(current!=null){id=current.getAttribute("jfreesvg:ref");found=id!=null}else found=true}return id};
orsoncharts.Utils.findChartId=function(element){var id=null;var found=false;var current=element;while(!found){current=current.parentNode;if(current!=null){var ref=current.getAttribute("jfreesvg:ref");if(ref=="ORSON_CHART_TOP_LEVEL"){found=true;id=current.getAttribute("id")}}else found=true}return id};if(!orsoncharts)orsoncharts={};orsoncharts.KeyedValuesDataset=function(){if(!(this instanceof orsoncharts.KeyedValuesDataset))return new orsoncharts.KeyedValuesDataset;this.data=[];this.listeners=[]};orsoncharts.KeyedValuesDataset.prototype.itemCount=function(){return this.data.length};orsoncharts.KeyedValuesDataset.prototype.isEmpty=function(){return this.data.length===0};orsoncharts.KeyedValuesDataset.prototype.key=function(index){return this.data[index][0]};
orsoncharts.KeyedValuesDataset.prototype.keys=function(){return this.data.map(function(d){return d[0]})};orsoncharts.KeyedValuesDataset.prototype.indexOf=function(sectionKey){var arrayLength=this.data.length;for(var i=0;i=0)this.listeners.splice(i,1)};
orsoncharts.KeyedValuesDataset.prototype.notifyListeners=function(){};orsoncharts.KeyedValuesDataset.prototype.add=function(sectionKey,value){this.data.push([sectionKey,value])};orsoncharts.KeyedValuesDataset.prototype.remove=function(sectionKey){if(!sectionKey)throw new Error("The 'sectionKey' must be defined.");var i=this.indexOf(sectionKey);if(i<0)throw new Error("The sectionKey '"+sectionKey.toString()+"' is not recognised.");this.data.splice(i,1)};
orsoncharts.KeyedValuesDataset.prototype.dataFromJSON=function(jsonStr){this.data=JSON.parse(jsonStr);this.notifyListeners()};orsoncharts.KeyedValuesDataset.prototype.removeByIndex=function(itemIndex){this.data.splice(itemIndex,1)};orsoncharts.KeyedValuesDataset.prototype.totalForDataset=function(dataset){var total=0;var itemCount=dataset.itemCount();for(var i=0;i=0)this.listeners.splice(i,1)};orsoncharts.KeyedValues3DDataset.prototype.notifyListeners=function(){};
orsoncharts.KeyedValues3DDataset.prototype.add=function(seriesKey,rowKey,columnKey,value){if(this.isEmpty()){this.data.rowKeys.push(rowKey);this.data.columnKeys.push(columnKey);this.data.series.push({"seriesKey":seriesKey,"rows":[[rowKey,[value]]]})}else{var seriesIndex=this.seriesIndex(seriesKey);if(seriesIndex<0){this.data.series.push({"seriesKey":seriesKey,"rows":[]});seriesIndex=this.data.series.length-1}var columnIndex=this.columnIndex(columnKey);if(columnIndex<0){this.data.columnKeys.push(columnKey);
for(var s=0;s=0)throw new Error("There is already a series with the key '"+seriesKey);this.data["series"].push([seriesKey,[]])};
orsoncharts.XYZDataset.prototype.removeSeries=function(seriesKey){if(!(typeof seriesKey==="string"))throw new Error("The 'seriesKey' must be a string.");var s=this.seriesIndex(seriesKey);if(s>=0)this.data["series"].splice(s,1)};orsoncharts.XYZDataset.prototype.addListener=function(listenerMethod){this.listeners.push(listenerMethod)};orsoncharts.XYZDataset.prototype.removeListener=function(listenerMethod){var i=this.listeners.indexOf(listenerMethod);if(i>=0)this.listeners.splice(i,1)};if(!orsoncharts)orsoncharts={};orsoncharts.KeyedValueLabels=function(){if(!(this instanceof orsoncharts.KeyedValueLabels))return new orsoncharts.KeyedValueLabels;this.format="{K} = {V}";this.valueDP=2;this.percentDP=2};
orsoncharts.KeyedValueLabels.prototype.itemLabel=function(keyedValues,itemIndex){var labelStr=new String(this.format);var keyStr=keyedValues.key(itemIndex);var value=keyedValues.valueByIndex(itemIndex);var valueStr=value.toFixed(this.valueDP);var total=keyedValues.total();var percentStr=(value/total*100).toFixed(this.percentDP);labelStr=labelStr.replace(/{K}/g,keyStr);labelStr=labelStr.replace(/{V}/g,valueStr);labelStr=labelStr.replace(/{P}/g,percentStr);return labelStr};if(!orsoncharts)orsoncharts={};orsoncharts.KeyedValue3DLabels=function(){if(!(this instanceof orsoncharts.KeyedValue3DLabels))return new orsoncharts.KeyedValue3DLabels;this.format="{S}, {R}, {C} = {V}";this.valueDP=2};
orsoncharts.KeyedValue3DLabels.prototype.itemLabel=function(keyedValues3D,seriesIndex,rowIndex,columnIndex){var labelStr=new String(this.format);var seriesKeyStr=keyedValues3D.seriesKey(seriesIndex);var rowKeyStr=keyedValues3D.rowKey(rowIndex);var columnKeyStr=keyedValues3D.columnKey(columnIndex);var value=keyedValues3D.valueByIndex(seriesIndex,rowIndex,columnIndex);var valueStr=value.toFixed(this.valueDP);labelStr=labelStr.replace(/{S}/g,seriesKeyStr);labelStr=labelStr.replace(/{R}/g,rowKeyStr);
labelStr=labelStr.replace(/{C}/g,columnKeyStr);labelStr=labelStr.replace(/{V}/g,valueStr);return labelStr};if(!orsoncharts)orsoncharts={};orsoncharts.XYZLabels=function(){if(!(this instanceof orsoncharts.XYZLabels))return new orsoncharts.XYZLabels;this.format="{X}, {Y}, {Z} / {S}";this.xDP=2;this.yDP=2;this.zDP=2};
orsoncharts.XYZLabels.prototype.itemLabel=function(dataset,seriesKey,itemIndex){var labelStr=new String(this.format);var seriesKeyStr=seriesKey;var seriesIndex=dataset.seriesIndex(seriesKey);var item=dataset.item(seriesIndex,itemIndex);var xStr=item[0].toFixed(this.xDP);var yStr=item[1].toFixed(this.yDP);var zStr=item[2].toFixed(this.zDP);labelStr=labelStr.replace(/{X}/g,xStr);labelStr=labelStr.replace(/{Y}/g,yStr);labelStr=labelStr.replace(/{Z}/g,zStr);labelStr=labelStr.replace(/{S}/g,seriesKeyStr);
return labelStr};
orson-charts-2.1.0/js/src/ 0000775 0000000 0000000 00000000000 14173312715 0015343 5 ustar 00root root 0000000 0000000 orson-charts-2.1.0/js/src/KeyedValue3DLabels.js 0000664 0000000 0000000 00000002262 14173312715 0021253 0 ustar 00root root 0000000 0000000 /*
* Orson Charts
* ------------
* Copyright 2014-2022 by David Gilbert.
*/
"use strict";
if (!orsoncharts) orsoncharts = {};
/**
* Constructor for a new KeyedValue3DLabels instance
* @constructor
*/
orsoncharts.KeyedValue3DLabels = function() {
if (!(this instanceof orsoncharts.KeyedValue3DLabels)) {
return new orsoncharts.KeyedValue3DLabels();
}
this.format = "{S}, {R}, {C} = {V}";
this.valueDP = 2;
};
// Generates a label for an item in a KeyedValue3DDataset.
orsoncharts.KeyedValue3DLabels.prototype.itemLabel = function(keyedValues3D,
seriesIndex, rowIndex, columnIndex) {
var labelStr = new String(this.format);
var seriesKeyStr = keyedValues3D.seriesKey(seriesIndex);
var rowKeyStr = keyedValues3D.rowKey(rowIndex);
var columnKeyStr = keyedValues3D.columnKey(columnIndex);
var value = keyedValues3D.valueByIndex(seriesIndex, rowIndex, columnIndex);
var valueStr = value.toFixed(this.valueDP);
labelStr = labelStr.replace(/{S}/g, seriesKeyStr);
labelStr = labelStr.replace(/{R}/g, rowKeyStr);
labelStr = labelStr.replace(/{C}/g, columnKeyStr);
labelStr = labelStr.replace(/{V}/g, valueStr);
return labelStr;
}; orson-charts-2.1.0/js/src/KeyedValueLabels.js 0000664 0000000 0000000 00000002043 14173312715 0021061 0 ustar 00root root 0000000 0000000 /*
* Orson Charts
* ------------
* Copyright 2014-2022 by David Gilbert.
*/
"use strict";
if (!orsoncharts) orsoncharts = {};
/**
* Constructor for a new KeyedValueLabels instance
* @constructor
*/
orsoncharts.KeyedValueLabels = function() {
if (!(this instanceof orsoncharts.KeyedValueLabels)) {
return new orsoncharts.KeyedValueLabels();
}
this.format = "{K} = {V}";
this.valueDP = 2;
this.percentDP = 2;
};
// Generates a label for an item in a KeyedValuesDataset.
orsoncharts.KeyedValueLabels.prototype.itemLabel = function(keyedValues, itemIndex) {
var labelStr = new String(this.format);
var keyStr = keyedValues.key(itemIndex);
var value = keyedValues.valueByIndex(itemIndex);
var valueStr = value.toFixed(this.valueDP);
var total = keyedValues.total();
var percentStr = (value / total * 100).toFixed(this.percentDP);
labelStr = labelStr.replace(/{K}/g, keyStr);
labelStr = labelStr.replace(/{V}/g, valueStr);
labelStr = labelStr.replace(/{P}/g, percentStr);
return labelStr;
};
orson-charts-2.1.0/js/src/KeyedValues3DDataset.js 0000664 0000000 0000000 00000015372 14173312715 0021627 0 ustar 00root root 0000000 0000000 /*
* Copyright 2014-2022 by David Gilbert.
*/
if (!orsoncharts) orsoncharts = {};
/**
* Constructor for a new KeyedValuesDataset3D
* @constructor
*/
orsoncharts.KeyedValues3DDataset = function() {
if (!(this instanceof orsoncharts.KeyedValues3DDataset)) {
return new orsoncharts.KeyedValues3DDataset();
}
this.data = { "columnKeys": [], "rowKeys": [], "series": [] };
this.listeners = [];
};
// Returns true if the dataset is empty and false otherwise.
orsoncharts.KeyedValues3DDataset.prototype.isEmpty = function() {
return this.data["columnKeys"].length === 0
&& this.data["rowKeys"].length === 0;
};
// Returns the number of series in the dataset.
orsoncharts.KeyedValues3DDataset.prototype.seriesCount = function() {
return this.data.series.length;
};
// Returns the number of rows in the dataset.
orsoncharts.KeyedValues3DDataset.prototype.rowCount = function() {
return this.data.rowKeys.length;
};
// Returns the number of columns in the dataset.
orsoncharts.KeyedValues3DDataset.prototype.columnCount = function() {
return this.data["columnKeys"].length;
};
orsoncharts.KeyedValues3DDataset.prototype._fetchRow = function(seriesIndex, rowKey) {
var rows = this.data.series[seriesIndex].rows;
for (var r = 0; r < rows.length; r++) {
if (rows[r][0] === rowKey) {
return rows[r];
}
}
return null;
};
// Returns a value by series, row and column index.
orsoncharts.KeyedValues3DDataset.prototype.valueByIndex = function(seriesIndex,
rowIndex, columnIndex) {
var rowKey = this.rowKey(rowIndex);
var row = this._fetchRow(seriesIndex, rowKey);
if (row === null) {
return null;
} else {
return row[1][columnIndex];
}
};
// Returns the index of the series with the specified key, or -1.
orsoncharts.KeyedValues3DDataset.prototype.seriesIndex = function(seriesKey) {
var seriesCount = this.seriesCount();
for (var s = 0; s < seriesCount; s++) {
if (this.data.series[s].seriesKey === seriesKey) {
return s;
}
}
return -1;
};
// Returns the key for the series with the specified index.
orsoncharts.KeyedValues3DDataset.prototype.seriesKey = function(seriesIndex) {
return this.data.series[seriesIndex].seriesKey;
};
// Returns the key for the row with the specified index.
orsoncharts.KeyedValues3DDataset.prototype.rowKey = function(rowIndex) {
return this.data.rowKeys[rowIndex];
};
// Returns the index of the row with the specified key.
orsoncharts.KeyedValues3DDataset.prototype.rowIndex = function(rowKey) {
var rowCount = this.data.rowKeys.length;
for (var r = 0; r < rowCount; r++) {
if (this.data.rowKeys[r] === rowKey) {
return r;
}
}
return -1;
};
// Returns all the row keys.
orsoncharts.KeyedValues3DDataset.prototype.rowKeys = function() {
return this.data.rowKeys.map(function(d) { return d; });
};
// Returns the key for the column with the specified index.
orsoncharts.KeyedValues3DDataset.prototype.columnKey = function(columnIndex) {
return this.data.columnKeys[columnIndex];
};
// Returns the index of hte column with the specified key.
orsoncharts.KeyedValues3DDataset.prototype.columnIndex = function(columnKey) {
var columnCount = this.data.columnKeys.length;
for (var c = 0; c < columnCount; c++) {
if (this.data.columnKeys[c] === columnKey) {
return c;
}
}
return -1;
};
// Returns all the column keys.
orsoncharts.KeyedValues3DDataset.prototype.columnKeys = function() {
return this.data.columnKeys.map(function(d) { return d; });
};
// Returns the value for the item identified by the specified series, row and
// column keys.
orsoncharts.KeyedValues3DDataset.prototype.valueByKey = function(seriesKey, rowKey,
columnKey) {
var seriesIndex = this.seriesIndex(seriesKey);
var row = this._fetchRow(seriesIndex, rowKey);
if (row === null) {
return null;
} else {
var columnIndex = this.columnIndex(columnKey);
return row[1][columnIndex];
}
};
// Adds a listener to the dataset (the listener method will be called whenever
// the dataset is modified)
orsoncharts.KeyedValues3DDataset.prototype.addListener = function(listenerMethod) {
this.listeners.push(listenerMethod);
};
// Deregisters the specified listener so that it no longer receives
// notification of dataset changes
orsoncharts.KeyedValues3DDataset.prototype.removeListener = function(listenerMethod) {
var i = this.listeners.indexOf(listenerMethod);
if (i >= 0) {
this.listeners.splice(i, 1);
}
};
// Notifies all registered listeners that there has been a change to this dataset
orsoncharts.KeyedValues3DDataset.prototype.notifyListeners = function() {
// TODO: call each listenerMethod
};
// adds a value to the dataset (or updates an existing value)
orsoncharts.KeyedValues3DDataset.prototype.add = function(seriesKey, rowKey,
columnKey, value) {
if (this.isEmpty()) {
this.data.rowKeys.push(rowKey);
this.data.columnKeys.push(columnKey);
this.data.series.push({"seriesKey": seriesKey,
"rows": [[rowKey, [value]]]});
} else {
var seriesIndex = this.seriesIndex(seriesKey);
if (seriesIndex < 0) {
this.data.series.push({"seriesKey": seriesKey, "rows": []});
seriesIndex = this.data.series.length - 1;
}
var columnIndex = this.columnIndex(columnKey);
if (columnIndex < 0) {
// add the column key and insert a null data item in all existing rows
this.data.columnKeys.push(columnKey);
for (var s = 0; s < this.data.series.length; s++) {
var rows = this.data.series[s].rows;
for (var r = 0; r < rows.length; r++) {
rows[r][1].push(null);
}
}
columnIndex = this.columnCount() - 1;
}
var rowIndex = this.rowIndex(rowKey);
if (rowIndex < 0) {
this.data.rowKeys.push(rowKey);
// add the row for the current series only
var rowData = orsoncharts.Utils.makeArrayOf(null, this.columnCount());
rowData[columnIndex] = value;
this.data.series[seriesIndex].rows.push([rowKey, rowData]);
} else {
var row = this._fetchRow(seriesIndex, rowKey);
if (row !== null) {
row[1][columnIndex] = value;
} else {
var rowData = orsoncharts.Utils.makeArrayOf(null, this.columnCount());
rowData[columnIndex] = value;
this.data.series[seriesIndex].rows.push([rowKey, rowData]);
}
}
}
};
orsoncharts.KeyedValues3DDataset.prototype.dataFromJSON = function(jsonStr) {
this.data = JSON.parse(jsonStr);
if (!this.data.hasOwnProperty("rowKeys")) {
this.data.rowKeys = [];
}
if (!this.data.hasOwnProperty("columnKeys")) {
this.data.columnKeys = [];
}
if (!this.data.hasOwnProperty("series")) {
this.data.series = [];
}
this.notifyListeners();
};
orson-charts-2.1.0/js/src/KeyedValuesDataset.js 0000664 0000000 0000000 00000011677 14173312715 0021444 0 ustar 00root root 0000000 0000000 /*
* Copyright 2014-2022 by David Gilbert.
*/
"use strict";
if (!orsoncharts) orsoncharts = {};
/**
* Constructor for a new KeyedValuesDataset
* @constructor
*/
orsoncharts.KeyedValuesDataset = function() {
if (!(this instanceof orsoncharts.KeyedValuesDataset)) {
return new orsoncharts.KeyedValuesDataset();
}
this.data = [];
this.listeners = [];
};
// returns the number of items in the dataset
orsoncharts.KeyedValuesDataset.prototype.itemCount = function() {
return this.data.length;
};
// Returns true if the dataset contains no items and false otherwise
orsoncharts.KeyedValuesDataset.prototype.isEmpty = function() {
return this.data.length === 0;
};
// returns the section key for the item with the specified index.
orsoncharts.KeyedValuesDataset.prototype.key = function(index) {
return this.data[index][0];
};
// Returns a new array containing all the keys for the dataset.
orsoncharts.KeyedValuesDataset.prototype.keys = function() {
return this.data.map(function(d) { return d[0]; });
};
// returns the index of the item with the specified key, or -1
orsoncharts.KeyedValuesDataset.prototype.indexOf = function(sectionKey) {
var arrayLength = this.data.length;
for (var i = 0; i < arrayLength; i++) {
if (this.data[i][0] === sectionKey) {
return i;
}
}
return -1;
};
// returns the value for the item with the specified index.
orsoncharts.KeyedValuesDataset.prototype.valueByIndex = function(index) {
return this.data[index][1];
};
// returns the value for the item with the specified key
orsoncharts.KeyedValuesDataset.prototype.valueByKey = function(sectionKey) {
var sectionIndex = this.indexOf(sectionKey);
if (sectionIndex < 0) return null;
return this.valueByIndex(sectionIndex);
};
// Adds a listener to the dataset (the listener method will be called whenever
// the dataset is modified)
orsoncharts.KeyedValuesDataset.prototype.addListener = function(listenerMethod) {
this.listeners.push(listenerMethod);
};
// Deregisters the specified listener so that it no longer receives
// notification of dataset changes
orsoncharts.KeyedValuesDataset.prototype.removeListener = function(listenerMethod) {
var i = this.listeners.indexOf(listenerMethod);
if (i >= 0) {
this.listeners.splice(i, 1);
}
};
// Notifies all registered listeners that there has been a change to this dataset
orsoncharts.KeyedValuesDataset.prototype.notifyListeners = function() {
// TODO: call each listenerMethod
};
// adds the specified (key, value) pair to the dataset or, if the key exists
// already, updates the value
orsoncharts.KeyedValuesDataset.prototype.add = function(sectionKey, value) {
this.data.push([sectionKey, value]);
};
// removes the item with the specified key
orsoncharts.KeyedValuesDataset.prototype.remove = function(sectionKey) {
if (!sectionKey) throw new Error("The 'sectionKey' must be defined.");
var i = this.indexOf(sectionKey);
if (i < 0) throw new Error("The sectionKey '" + sectionKey.toString()
+ "' is not recognised.");
this.data.splice(i, 1);
};
// sets the data array based on the supplied JSON string
orsoncharts.KeyedValuesDataset.prototype.dataFromJSON = function(jsonStr) {
this.data = JSON.parse(jsonStr);
this.notifyListeners();
};
orsoncharts.KeyedValuesDataset.prototype.removeByIndex = function(itemIndex) {
this.data.splice(itemIndex, 1);
};
// returns the total of all non-null values for the specified dataset
orsoncharts.KeyedValuesDataset.prototype.totalForDataset = function(dataset) {
var total = 0.0;
var itemCount = dataset.itemCount();
for (var i = 0; i < itemCount; i++) {
var v = dataset.valueByIndex(i);
if (v) {
total = total + v;
}
}
return total;
};
// returns the minimum value for the specified dataset
orsoncharts.KeyedValuesDataset.prototype.minForDataset = function(dataset) {
var min = null;
var itemCount = dataset.itemCount();
for (var i = 0; i < itemCount; i++) {
var v = dataset.valueByIndex(i);
if (v) {
if (min) {
min = Math.min(min, v);
} else {
min = v;
}
}
}
return min;
};
// returns the maximum value for the specified dataset
orsoncharts.KeyedValuesDataset.prototype.maxForDataset = function(dataset) {
var max = null;
var itemCount = dataset.itemCount();
for (var i = 0; i < itemCount; i++) {
var v = dataset.valueByIndex(i);
if (v) {
if (max) {
max = Math.max(max, v);
} else {
max = v;
}
}
}
return max;
};
// returns the total of all values in this dataset (ignoring null values)
orsoncharts.KeyedValuesDataset.prototype.total = function() {
return this.totalForDataset(this);
};
// returns the minimum value in this dataset (ignoring null values)
orsoncharts.KeyedValuesDataset.prototype.min = function() {
return this.minForDataset(this);
};
// returns the maximum value in this dataset (ignoring null values)
orsoncharts.KeyedValuesDataset.prototype.max = function() {
return this.maxForDataset(this);
};
orson-charts-2.1.0/js/src/Utils.js 0000664 0000000 0000000 00000002445 14173312715 0017006 0 ustar 00root root 0000000 0000000 /*
* Copyright 2014-2022 by David Gilbert.
*/
var orsoncharts;
if (!orsoncharts) orsoncharts = {};
orsoncharts.Utils = {};
orsoncharts.Utils.makeArrayOf = function(value, length) {
var arr = [], i = length;
while (i--) {
arr[i] = value;
}
return arr;
};
// returns the chart entity reference for this element
orsoncharts.Utils.findChartRef = function(element) {
var id = element.getAttribute("jfreesvg:ref");
var found = false;
var current = element;
while (!found) {
current = current.parentNode;
if (current != null) {
id = current.getAttribute("jfreesvg:ref");
found = (id != null);
} else {
found = true;
}
}
return id;
}
// find the chart id by finding the group that is written for the entire chart
orsoncharts.Utils.findChartId = function(element) {
var id = null;
var found = false;
var current = element;
while (!found) {
current = current.parentNode;
if (current != null) {
var ref = current.getAttribute("jfreesvg:ref");
if (ref == 'ORSON_CHART_TOP_LEVEL') {
found = true;
id = current.getAttribute("id");
}
} else {
found = true;
}
}
return id;
}
orson-charts-2.1.0/js/src/XYZDataset.js 0000664 0000000 0000000 00000007427 14173312715 0017713 0 ustar 00root root 0000000 0000000 /*
* Copyright 2014-2022 by David Gilbert and KNIME AG
*/
"use strict";
orsoncharts = orsoncharts ? orsoncharts : {};
/**
* Constructor for a new XYZDataset
* @constructor
*/
orsoncharts.XYZDataset = function() {
this.data = { "series": [], "selections": []};
this.listeners = [];
};
// returns the number of series in the dataset
orsoncharts.XYZDataset.prototype.seriesCount = function() {
return this.data.series.length;
};
// returns an array of all series keys in the dataset
orsoncharts.XYZDataset.prototype.seriesKeys = function() {
return this.data.series.map(function(d) {
return d[0];
});
};
// returns the key for the series with the specified index
orsoncharts.XYZDataset.prototype.seriesKey = function(seriesIndex) {
return this.data.series[seriesIndex][0];
};
// returns the index for the series with the specified key, or -1
orsoncharts.XYZDataset.prototype.seriesIndex = function(seriesKey) {
if (!(typeof seriesKey === 'string')) throw new Error("The 'seriesKey' must be a string.");
var seriesArray = this.data.series;
var seriesCount = this.data.series.length;
for (var s = 0; s < seriesCount; s++) {
if (seriesArray[s][0] === seriesKey) {
return s;
}
}
return -1;
};
// returns the number of items in the specified series
orsoncharts.XYZDataset.prototype.itemCount = function(seriesIndex) {
return this.data.series[seriesIndex][1].length;
};
// returns the x-value for the specified item in a series
orsoncharts.XYZDataset.prototype.x = function(seriesIndex, itemIndex) {
return this.data.series[seriesIndex][1][itemIndex][0];
};
// returns the y-value for the specified item in a series
orsoncharts.XYZDataset.prototype.y = function(seriesIndex, itemIndex) {
return this.data.series[seriesIndex][1][itemIndex][1];
};
// returns the z-value for the specified item in a series
orsoncharts.XYZDataset.prototype.z = function(seriesIndex, itemIndex) {
return this.data.series[seriesIndex][1][itemIndex][2];
};
// returns an array [x, y, z] for the specified item in a series
orsoncharts.XYZDataset.prototype.item = function(seriesIndex, itemIndex) {
return this.data.series[seriesIndex][1][itemIndex];
};
// adds a data value (x, y) to the specified series (if the series does not
// already exist in the dataset it is created
orsoncharts.XYZDataset.prototype.add = function(seriesKey, x, y, z) {
if (!(typeof seriesKey === 'string')) throw new Error("The 'seriesKey' must be a string.");
var s = this.seriesIndex(seriesKey);
if (s < 0) this.addSeries(seriesKey);
var series = this.data.series;
series[series.length-1][1].push([x, y, z]);
};
// adds a new empty series with the specified key (which must be a string).
orsoncharts.XYZDataset.prototype.addSeries = function(seriesKey) {
if (!(typeof seriesKey === 'string')) throw new Error("The 'seriesKey' must be a string.");
var s = this.seriesIndex(seriesKey);
if (s >= 0) throw new Error("There is already a series with the key '" + seriesKey);
this.data["series"].push([seriesKey, []]);
};
// removeSeries
orsoncharts.XYZDataset.prototype.removeSeries = function(seriesKey) {
if (!(typeof seriesKey === 'string')) throw new Error("The 'seriesKey' must be a string.");
var s = this.seriesIndex(seriesKey);
if (s >= 0) {
this.data["series"].splice(s, 1);
}
};
// Adds a listener to the dataset (the listener method will be called whenever
// the dataset is modified)
orsoncharts.XYZDataset.prototype.addListener = function(listenerMethod) {
this.listeners.push(listenerMethod);
};
// Deregisters the specified listener so that it no longer receives
// notification of dataset changes
orsoncharts.XYZDataset.prototype.removeListener = function(listenerMethod) {
var i = this.listeners.indexOf(listenerMethod);
if (i >= 0) {
this.listeners.splice(i, 1);
}
};
orson-charts-2.1.0/js/src/XYZLabels.js 0000664 0000000 0000000 00000002114 14173312715 0017514 0 ustar 00root root 0000000 0000000 /*
* Orson Charts
* ------------
* Copyright 2014-2022 by David Gilbert.
*/
"use strict";
if (!orsoncharts) orsoncharts = {};
/**
* Constructor for a new XYZLabels instance
* @constructor
*/
orsoncharts.XYZLabels = function() {
if (!(this instanceof orsoncharts.XYZLabels)) {
return new orsoncharts.XYZLabels();
}
this.format = "{X}, {Y}, {Z} / {S}";
this.xDP = 2;
this.yDP = 2;
this.zDP = 2;
};
// Generates a label for an item in a XYZDataset.
orsoncharts.XYZLabels.prototype.itemLabel = function(dataset, seriesKey, itemIndex) {
var labelStr = new String(this.format);
var seriesKeyStr = seriesKey;
var seriesIndex = dataset.seriesIndex(seriesKey);
var item = dataset.item(seriesIndex, itemIndex);
var xStr = item[0].toFixed(this.xDP);
var yStr = item[1].toFixed(this.yDP);
var zStr = item[2].toFixed(this.zDP);
labelStr = labelStr.replace(/{X}/g, xStr);
labelStr = labelStr.replace(/{Y}/g, yStr);
labelStr = labelStr.replace(/{Z}/g, zStr);
labelStr = labelStr.replace(/{S}/g, seriesKeyStr);
return labelStr;
};
orson-charts-2.1.0/pom.xml 0000664 0000000 0000000 00000017153 14173312715 0015464 0 ustar 00root root 0000000 0000000 4.0.0Orson ChartsOrson Charts is a 3D chart library for the Java platform.https://github.com/orson-chartsorg.jfreeorg.jfree.chart3d2.1.0jarDavid Gilbertdave@jfree.orgGNU General Public License (GPL)http://www.gnu.org/licenses/gpl.txtrepohttps://github.com/jfree/orson-charts/issuesGitHub Issuesscm:git:git:https://github.com/jfree/orson-charts.githttps://github.com/jfree/orson-chartsUTF-81111org.junit.jupiterjunit-jupiter-api5.7.2testorg.junit.jupiterjunit-jupiter-engine5.7.2testorg.apache.maven.pluginsmaven-clean-plugin3.1.0org.apache.maven.pluginsmaven-resources-plugin3.2.0${project.build.sourceEncoding}org.apache.maven.pluginsmaven-compiler-plugin3.8.1${project.source.level}${project.target.level}${project.build.sourceEncoding}trueorg.apache.maven.pluginsmaven-javadoc-plugin3.3.1truetrue-Xdoclint:noneattach-javadocjarorg.apache.maven.pluginsmaven-jar-plugin3.2.0maven-surefire-plugin2.22.2maven-failsafe-plugin2.22.2org.apache.maven.pluginsmaven-install-plugin3.0.0-M1ossrhhttps://oss.sonatype.org/content/repositories/snapshotsreleaseorg.apache.maven.pluginsmaven-gpg-plugin1.5sign-artifactsverifysignorg.sonatype.pluginsnexus-staging-maven-plugin1.6.7trueossrhhttps://oss.sonatype.org/falsetrueorg.apache.maven.pluginsmaven-javadoc-plugin3.3.1${basedir}/src/main/java/overview.htmltrue-Xdoclint:noneattach-javadocjarorg.apache.maven.pluginsmaven-source-plugin2.2.1attach-sourcesjar-no-fork
orson-charts-2.1.0/src/ 0000775 0000000 0000000 00000000000 14173312715 0014727 5 ustar 00root root 0000000 0000000 orson-charts-2.1.0/src/main/ 0000775 0000000 0000000 00000000000 14173312715 0015653 5 ustar 00root root 0000000 0000000 orson-charts-2.1.0/src/main/java/ 0000775 0000000 0000000 00000000000 14173312715 0016574 5 ustar 00root root 0000000 0000000 orson-charts-2.1.0/src/main/java/module-info.java 0000664 0000000 0000000 00000011476 14173312715 0021666 0 ustar 00root root 0000000 0000000 /**
* Orson Charts is a chart library for the Java(tm) platform (module name
* {@code org.jfree.chart3d}) that can generate a wide variety of 3D charts for
* use in client-side applications (JavaFX and Swing) and server-side
* applications (with export to SVG, PDF, PNG and JPEG formats). Key features
* include:
*
*
multiple chart types: pie charts, bar charts (regular and
* stacked), line charts, area charts, scatter plots and surface charts;
*
a built-in lightweight 3D rendering engine - no additional or complex
* dependencies, resulting in easy deployment;
*
mouse-enabled chart viewing components (for both JavaFX and Swing)
* provide 360 degree rotation and zooming for precise end-user view
* control;
*
auto-adaptive axis labeling;
*
easy export of charts to PDF and SVG for reporting;
*
a clean and well-documented API, with a high degree of chart
* configurability;
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
Samples
*
*
* Orson Charts includes only 3D charts, for 2D charts we recommend the excellent
* JFreeChart library
* (by the same author).
*
* There is also a version of Orson Charts available for the Android platform.
*
* Getting Started
*
* To get started with Orson Charts, you simply need to add the
* org.jfree.chart3d module to your application and begin coding.
* Your first step is to set up some data that you want to display. Orson Charts
* reads data through a dataset interface - there are three key interfaces, one
* that is used for pie charts (PieDataset3D), one that is used for
* bar charts and other category based charts (CategoryDataset3D)
* and one for plots that use numerical data (XYZDataset). There
* are standard implementations of these interfaces includes in the library,
* making it straightforward to create a new dataset. Here is an example for a
* pie chart:
*
* Once your dataset is ready, the next step is to create a chart object - here the
* Chart3DFactory class can help, as it has utility methods to create
* some standard chart types:
*
* Finally, if you are developing a Swing application, you will want to place the
* chart somewhere in your UI. Here the Chart3DPanel class can
* be used:
*
* Chart3DPanel chartPanel = new Chart3DPanel(chart);
*
* You can find complete examples in the JFree Demos project at GitHub.
* You are encouraged to explore these example programs and review these Javadoc
* pages to learn more about Orson Charts.
*
* More Information
*
* Please visit
* http://www.object-refinery.com/orsoncharts/index.html for the latest
* information about Orson Charts.
*/
module org.jfree.chart3d {
requires java.base;
requires java.desktop;
requires java.logging;
exports org.jfree.chart3d;
exports org.jfree.chart3d.axis;
exports org.jfree.chart3d.data;
exports org.jfree.chart3d.data.category;
exports org.jfree.chart3d.data.function;
exports org.jfree.chart3d.data.xyz;
exports org.jfree.chart3d.export;
exports org.jfree.chart3d.graphics2d;
exports org.jfree.chart3d.graphics3d;
exports org.jfree.chart3d.graphics3d.swing;
exports org.jfree.chart3d.interaction;
exports org.jfree.chart3d.label;
exports org.jfree.chart3d.legend;
exports org.jfree.chart3d.marker;
exports org.jfree.chart3d.plot;
exports org.jfree.chart3d.renderer;
exports org.jfree.chart3d.renderer.category;
exports org.jfree.chart3d.renderer.xyz;
exports org.jfree.chart3d.style;
exports org.jfree.chart3d.table;
}
orson-charts-2.1.0/src/main/java/org/ 0000775 0000000 0000000 00000000000 14173312715 0017363 5 ustar 00root root 0000000 0000000 orson-charts-2.1.0/src/main/java/org/jfree/ 0000775 0000000 0000000 00000000000 14173312715 0020456 5 ustar 00root root 0000000 0000000 orson-charts-2.1.0/src/main/java/org/jfree/chart3d/ 0000775 0000000 0000000 00000000000 14173312715 0022006 5 ustar 00root root 0000000 0000000 orson-charts-2.1.0/src/main/java/org/jfree/chart3d/Chart3D.java 0000664 0000000 0000000 00000226710 14173312715 0024111 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d;
import java.awt.BasicStroke;
import java.awt.Stroke;
import java.awt.geom.Line2D;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Font;
import java.awt.Paint;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Dimension2D;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import javax.swing.event.EventListenerList;
import org.jfree.chart3d.graphics3d.internal.FaceSorter;
import org.jfree.chart3d.graphics3d.internal.StandardFaceSorter;
import org.jfree.chart3d.graphics3d.internal.Utils2D;
import org.jfree.chart3d.internal.ChartBox3D;
import org.jfree.chart3d.internal.ChartBox3D.ChartBoxFace;
import org.jfree.chart3d.internal.OnDrawHandler;
import org.jfree.chart3d.internal.Args;
import org.jfree.chart3d.internal.ObjectUtils;
import org.jfree.chart3d.internal.TextUtils;
import org.jfree.chart3d.axis.Axis3D;
import org.jfree.chart3d.axis.TickData;
import org.jfree.chart3d.axis.ValueAxis3D;
import org.jfree.chart3d.data.ItemKey;
import org.jfree.chart3d.graphics2d.Anchor2D;
import org.jfree.chart3d.graphics2d.RefPt2D;
import org.jfree.chart3d.graphics2d.TextAnchor;
import org.jfree.chart3d.graphics3d.Dimension3D;
import org.jfree.chart3d.graphics3d.DoubleSidedFace;
import org.jfree.chart3d.graphics3d.Drawable3D;
import org.jfree.chart3d.graphics3d.Face;
import org.jfree.chart3d.graphics3d.LabelFace;
import org.jfree.chart3d.graphics3d.Object3D;
import org.jfree.chart3d.graphics3d.Offset2D;
import org.jfree.chart3d.graphics3d.Point3D;
import org.jfree.chart3d.graphics3d.RenderedElement;
import org.jfree.chart3d.graphics3d.RenderingInfo;
import org.jfree.chart3d.graphics3d.ViewPoint3D;
import org.jfree.chart3d.graphics3d.World;
import org.jfree.chart3d.interaction.InteractiveElementType;
import org.jfree.chart3d.legend.LegendAnchor;
import org.jfree.chart3d.legend.LegendBuilder;
import org.jfree.chart3d.legend.StandardLegendBuilder;
import org.jfree.chart3d.marker.Marker;
import org.jfree.chart3d.marker.MarkerData;
import org.jfree.chart3d.plot.CategoryPlot3D;
import org.jfree.chart3d.plot.PiePlot3D;
import org.jfree.chart3d.plot.Plot3D;
import org.jfree.chart3d.plot.Plot3DChangeEvent;
import org.jfree.chart3d.plot.Plot3DChangeListener;
import org.jfree.chart3d.plot.XYZPlot;
import org.jfree.chart3d.style.ChartStyle;
import org.jfree.chart3d.style.ChartStyleChangeEvent;
import org.jfree.chart3d.style.ChartStyleChangeListener;
import org.jfree.chart3d.style.ChartStyler;
import org.jfree.chart3d.table.GradientRectanglePainter;
import org.jfree.chart3d.table.RectanglePainter;
import org.jfree.chart3d.table.StandardRectanglePainter;
import org.jfree.chart3d.table.TableElement;
import org.jfree.chart3d.table.TextElement;
/**
* A chart object for 3D charts (this is the umbrella object that manages all
* the components of the chart). The {@link Chart3DFactory} class provides
* some factory methods to construct common types of charts.
*
* All rendering is done via the Java2D API, so this object is able to draw to
* any implementation of the Graphics2D API (including
* JFreeSVG for
* SVG output, and
* OrsonPDF
* for PDF output).
*
* In the step prior to rendering, a chart is composed in a 3D model that is
* referred to as the "world". The dimensions of this 3D model are measured
* in "world units" and the overall size is controlled by the plot. You will
* see some attributes in the API that are specified in "world units", and these
* can be used to modify how objects are composed within the 3D world model.
* Once the objects (for example, bars in a bar chart) within the world have
* been composed, they are projected onto a 2D plane and rendered to the
* {@code Graphics2D} target (such as the screen, image, SVG file or
* PDF file).
*
* Charts can have simple titles or composite titles (anything that can be
* constructed as a {@link TableElement} instance. The {@link TitleUtils}
* class contains methods to create a common title/subtitle composite title.
* This is illustrated in some of the demo applications. The chart title
* and legend (and also the axis labels) are not part of the 3D world model,
* they are overlaid on the output after the 3D components have been
* rendered.
*
* NOTE: This class is serializable, but the serialization format is subject
* to change in future releases and should not be relied upon for persisting
* instances of this class.
*
* @see Chart3DFactory
* @see Chart3DPanel
*/
@SuppressWarnings("serial")
public class Chart3D implements Drawable3D, ChartElement,
Plot3DChangeListener, ChartStyleChangeListener, Serializable {
/**
* The default projection distance.
*
* @since 1.2
*/
public static final double DEFAULT_PROJ_DIST = 1500.0;
/**
* The key for a property that stores the interactive element type.
*
* @since 1.3
*/
public static final String INTERACTIVE_ELEMENT_TYPE
= "interactive_element_type";
/**
* The key for a property that stores the series key. This is used to
* store the series key on the {@link TableElement} representing a legend
* item, and also on a corresponding {@link RenderedElement} after
* chart rendering (in the {@link RenderingInfo}).
*
* @since 1.3
*/
public static final String SERIES_KEY = "series_key";
/** The chart id. */
private String id;
/** A background rectangle painter, if any. */
private RectanglePainter background;
/** The chart title (can be {@code null}). */
private TableElement title;
/** The anchor point for the title (never {@code null}). */
private Anchor2D titleAnchor;
/** A builder for the chart legend (can be {@code null}). */
private LegendBuilder legendBuilder;
/** The anchor point for the legend (never {@code null}). */
private Anchor2D legendAnchor;
/** The orientation for the legend (never {@code null}). */
private Orientation legendOrientation;
/** The plot. */
private Plot3D plot;
/** The view point. */
private ViewPoint3D viewPoint;
/** The projection distance. */
private double projDist;
/** The chart box color (never {@code null}). */
private Color chartBoxColor;
/**
* A translation factor applied to the chart when drawing. We use this
* to allow the user (optionally) to drag the chart from its center
* location to better align it with the chart title and legend.
*/
private Offset2D translate2D;
/** Storage for registered change listeners. */
private transient EventListenerList listenerList;
/**
* A flag that controls whether or not the chart will notify listeners
* of changes (defaults to {@code true}, but sometimes it is useful
* to disable this).
*/
private boolean notify;
/**
* Rendering hints that will be used for chart drawing. This can be
* empty but it should never be {@code null}.
*
* @since 1.1
*/
private transient RenderingHints renderingHints;
/**
* The chart style.
*
* @since 1.2
*/
private ChartStyle style;
/** A 3D model of the world (represents the chart). */
private transient World world;
/** An object that sorts faces for rendering (painter's algorithm). */
private FaceSorter faceSorter;
/**
* A flag that controls whether or not element hints are added to the
* {@code Graphics2D} output.
*/
private boolean elementHinting;
/**
* Creates a 3D chart for the specified plot using the default chart
* style. Note that a plot instance must be used in one chart instance
* only.
*
* @param title the chart title ({@code null} permitted).
* @param subtitle the chart subtitle ({@code null} permitted).
* @param plot the plot ({@code null} not permitted).
*
* @see Chart3DFactory
*/
public Chart3D(String title, String subtitle, Plot3D plot) {
this(title, subtitle, plot, Chart3DFactory.getDefaultChartStyle());
}
/**
* Creates a 3D chart for the specified plot using the supplied style.
*
* @param title the chart title ({@code null} permitted).
* @param subtitle the chart subtitle ({@code null} permitted).
* @param plot the plot ({@code null} not permitted).
* @param style the chart style ({@code null} not permitted).
*
* @since 1.2
*/
public Chart3D(String title, String subtitle, Plot3D plot,
ChartStyle style) {
Args.nullNotPermitted(plot, "plot");
Args.nullNotPermitted(style, "style");
plot.setChart(this);
this.background = new StandardRectanglePainter(Color.WHITE);
if (title != null) {
this.title = TitleUtils.createTitle(title, subtitle);
}
this.titleAnchor = TitleAnchor.TOP_LEFT;
this.legendBuilder = new StandardLegendBuilder();
this.legendAnchor = LegendAnchor.BOTTOM_RIGHT;
this.legendOrientation = Orientation.HORIZONTAL;
this.plot = plot;
this.plot.addChangeListener(this);
Dimension3D dim = this.plot.getDimensions();
float distance = (float) dim.getDiagonalLength() * 3.0f;
this.viewPoint = ViewPoint3D.createAboveViewPoint(distance);
this.projDist = DEFAULT_PROJ_DIST;
this.chartBoxColor = new Color(255, 255, 255, 100);
this.translate2D = new Offset2D();
this.faceSorter = new StandardFaceSorter();
this.renderingHints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
this.renderingHints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
this.elementHinting = false;
this.notify = true;
this.listenerList = new EventListenerList();
this.style = style;
this.style.addChangeListener(this);
receive(new ChartStyler(this.style));
}
/**
* Returns the chart id.
*
* @return The chart id (possibly {@code null}).
*
* @since 1.3
*/
public String getID() {
return this.id;
}
/**
* Sets the chart id.
*
* @param id the id ({@code null} permitted).
*
* @since 1.3
*/
public void setID(String id) {
this.id = id;
}
/**
* Returns the background painter (an object that is responsible for filling
* the background area before charts are rendered). The default value
* is an instance of {@link StandardRectanglePainter} that paints the
* background white.
*
* @return The background painter (possibly {@code null}).
*
* @see #setBackground(org.jfree.chart3d.table.RectanglePainter)
*/
public RectanglePainter getBackground() {
return this.background;
}
/**
* Sets the background painter and sends a {@link Chart3DChangeEvent} to
* all registered listeners. A background painter is used to fill in the
* background of the chart before the 3D rendering takes place. To fill
* the background with a color or image, you can use
* {@link StandardRectanglePainter}. To fill the background with a
* gradient paint, use {@link GradientRectanglePainter}.
*
* @param background the background painter ({@code null} permitted).
*
* @see #getBackground()
*/
public void setBackground(RectanglePainter background) {
this.background = background;
fireChangeEvent();
}
/**
* Returns the chart title. A {@link TableElement} is used for the title,
* since it allows a lot of flexibility in the types of title that can
* be displayed.
*
* @return The chart title (possibly {@code null}).
*/
public TableElement getTitle() {
return this.title;
}
/**
* Sets the chart title and sends a {@link Chart3DChangeEvent} to all
* registered listeners. This is a convenience method that constructs
* the required {@link TableElement} under-the-hood.
*
* @param title the title ({@code null} permitted).
*/
public void setTitle(String title) {
if (title == null) {
setTitle((TableElement) null);
} else {
setTitle(title, this.style.getTitleFont(),
TitleUtils.DEFAULT_TITLE_COLOR);
}
}
/**
* Sets the chart title and sends a {@link Chart3DChangeEvent} to all
* registered listeners. This is a convenience method that constructs
* the required {@link TableElement} under-the-hood.
*
* @param title the title ({@code null} not permitted).
* @param font the font ({@code null} not permitted).
* @param color the foreground color ({@code null} not permitted).
*/
public void setTitle(String title, Font font, Color color) {
// defer 'title' null check
Args.nullNotPermitted(font, "font");
Args.nullNotPermitted(color, "color");
TextElement te = new TextElement(title);
te.setTag("CHART_TITLE");
te.setFont(font);
te.setColor(color);
setTitle(te);
}
/**
* Sets the chart title and sends a {@link Chart3DChangeEvent} to all
* registered listeners. You can set the title to {@code null}, in
* which case there will be no chart title.
*
* @param title the title ({@code null} permitted).
*/
public void setTitle(TableElement title) {
this.title = title;
fireChangeEvent();
}
/**
* Returns the title anchor. This controls the position of the title
* in the chart area.
*
* @return The title anchor (never {@code null}).
*
* @see #setTitleAnchor(Anchor2D)
*/
public Anchor2D getTitleAnchor() {
return this.titleAnchor;
}
/**
* Sets the title anchor and sends a {@link Chart3DChangeEvent} to all
* registered listeners. There is a {@link TitleAnchor} class providing
* some useful default anchors.
*
* @param anchor the anchor ({@code null} not permitted).
*
* @see #getTitleAnchor()
*/
public void setTitleAnchor(Anchor2D anchor) {
Args.nullNotPermitted(anchor, "anchor");
this.titleAnchor = anchor;
fireChangeEvent();
}
/**
* Returns the plot, which manages the dataset, the axes (if any), the
* renderer (if any) and other attributes related to plotting data. The
* plot is specified via the constructor...there is no method to set a
* new plot for the chart, instead you need to create a new chart instance.
*
* @return The plot (never {@code null}).
*/
public Plot3D getPlot() {
return this.plot;
}
/**
* Returns the chart box color (the chart box is the visible, open-sided
* box inside which data is plotted for all charts except pie charts).
* The default value is {@code Color.WHITE}.
*
* @return The chart box color (never {@code null}).
*
* @see #setChartBoxColor(java.awt.Color)
*/
public Color getChartBoxColor() {
return this.chartBoxColor;
}
/**
* Sets the chart box color and sends a {@link Chart3DChangeEvent} to all
* registered listeners. Bear in mind that {@link PiePlot3D} does not
* display a chart box, so this attribute will be ignored for pie charts.
*
* @param color the color ({@code null} not permitted).
*
* @see #getChartBoxColor()
*/
public void setChartBoxColor(Color color) {
Args.nullNotPermitted(color, "color");
this.chartBoxColor = color;
fireChangeEvent();
}
/**
* Returns the dimensions of the 3D object.
*
* @return The dimensions (never {@code null}).
*/
@Override
public Dimension3D getDimensions() {
return this.plot.getDimensions();
}
/**
* Returns the view point.
*
* @return The view point (never {@code null}).
*/
@Override
public ViewPoint3D getViewPoint() {
return this.viewPoint;
}
/**
* Sets the view point.
*
* @param viewPoint the view point ({@code null} not permitted).
*/
@Override
public void setViewPoint(ViewPoint3D viewPoint) {
Args.nullNotPermitted(viewPoint, "viewPoint");
this.viewPoint = viewPoint;
fireChangeEvent();
}
/**
* Returns the projection distance. The default value is
* {@link #DEFAULT_PROJ_DIST}, higher numbers flatten out the perspective
* and reduce distortion in the projected image.
*
* @return The projection distance.
*
* @since 1.2
*/
@Override
public double getProjDistance() {
return this.projDist;
}
/**
* Sets the projection distance and sends a change event to all registered
* listeners.
*
* @param dist the distance.
*
* @since 1.2
*/
@Override
public void setProjDistance(double dist) {
this.projDist = dist;
fireChangeEvent();
}
/**
* Sets the offset in 2D-space for the rendering of the chart. The
* default value is {@code (0, 0)} but the user can modify it via
* ALT-mouse-drag in the chart panel, providing an easy way to get improved
* chart alignment in the panels (especially prior to export to PNG, SVG or
* PDF).
*
* @return The offset (never {@code null}).
*/
@Override
public Offset2D getTranslate2D() {
return this.translate2D;
}
/**
* Sets the offset in 2D-space for the rendering of the chart and sends a
* change event to all registered listeners.
*
* @param offset the new offset ({@code null} not permitted).
*/
@Override
public void setTranslate2D(Offset2D offset) {
Args.nullNotPermitted(offset, "offset");
this.translate2D = offset;
fireChangeEvent();
}
/**
* Returns the legend builder. The default value is an instance of
* {@link StandardLegendBuilder}. If the legend builder is {@code null},
* no legend will be displayed for the chart.
*
* @return The legend builder (possibly {@code null}).
*
* @see #setLegendBuilder(org.jfree.chart3d.legend.LegendBuilder)
* @see #setLegendAnchor(Anchor2D)
*/
public LegendBuilder getLegendBuilder() {
return this.legendBuilder;
}
/**
* Sets the legend builder and sends a change event to all registered
* listeners. When the legend builder is {@code null}, no legend
* will be displayed on the chart.
*
* @param legendBuilder the legend builder ({@code null} permitted).
*
* @see #setLegendAnchor(Anchor2D)
*/
public void setLegendBuilder(LegendBuilder legendBuilder) {
this.legendBuilder = legendBuilder;
fireChangeEvent();
}
/**
* Returns the legend anchor.
*
* @return The legend anchor (never {@code null}).
*
* @see #setLegendAnchor(Anchor2D)
*/
public Anchor2D getLegendAnchor() {
return this.legendAnchor;
}
/**
* Sets the legend anchor and sends a {@link Chart3DChangeEvent} to all
* registered listeners. There is a {@link LegendAnchor} class providing
* some useful default anchors.
*
* @param anchor the anchor ({@code null} not permitted).
*
* @see #getLegendAnchor()
*/
public void setLegendAnchor(Anchor2D anchor) {
Args.nullNotPermitted(anchor, "anchor");
this.legendAnchor = anchor;
fireChangeEvent();
}
/**
* Returns the orientation for the legend.
*
* @return The orientation (never {@code null}).
*
* @since 1.1
*/
public Orientation getLegendOrientation() {
return this.legendOrientation;
}
/**
* Sets the legend orientation and sends a {@link Chart3DChangeEvent}
* to all registered listeners.
*
* @param orientation the orientation ({@code null} not permitted).
*
* @since 1.1
*/
public void setLegendOrientation(Orientation orientation) {
Args.nullNotPermitted(orientation, "orientation");
this.legendOrientation = orientation;
fireChangeEvent();
}
/**
* Sets the legend position (both the anchor point and the orientation) and
* sends a {@link Chart3DChangeEvent} to all registered listeners.
* This is a convenience method that calls both the
* {@link #setLegendAnchor(Anchor2D)} and
* {@link #setLegendOrientation(Orientation)}
* methods.
*
* @param anchor the anchor ({@code null} not permitted).
* @param orientation the orientation ({@code null} not permitted).
*
* @since 1.1
*/
public void setLegendPosition(Anchor2D anchor, Orientation orientation) {
setNotify(false);
setLegendAnchor(anchor);
setLegendOrientation(orientation);
setNotify(true);
}
/**
* Returns the collection of rendering hints for the chart.
*
* @return The rendering hints for the chart (never {@code null}).
*
* @see #setRenderingHints(RenderingHints)
*
* @since 1.1
*/
public RenderingHints getRenderingHints() {
return this.renderingHints;
}
/**
* Sets the rendering hints for the chart. These will be added (using the
* {@code Graphics2D.addRenderingHints()} method) near the start of
* the chart rendering. Note that calling this method will replace all
* existing hints assigned to the chart. If you simply wish to add an
* additional hint, you can use {@code getRenderingHints().put(key, value)}.
*
* @param hints the rendering hints ({@code null} not permitted).
*
* @see #getRenderingHints()
*
* @since 1.1
*/
public void setRenderingHints(RenderingHints hints) {
Args.nullNotPermitted(hints, "hints");
this.renderingHints = hints;
fireChangeEvent();
}
/**
* Returns a flag that indicates whether or not anti-aliasing is used when
* the chart is drawn.
*
* @return The flag.
*
* @see #setAntiAlias(boolean)
* @since 1.1
*/
public boolean getAntiAlias() {
Object val = this.renderingHints.get(RenderingHints.KEY_ANTIALIASING);
return RenderingHints.VALUE_ANTIALIAS_ON.equals(val);
}
/**
* Sets a flag that indicates whether or not anti-aliasing is used when the
* chart is drawn.
*
* Anti-aliasing usually improves the appearance of charts, but is slower.
*
* @param flag the new value of the flag.
*
* @see #getAntiAlias()
* @since 1.1
*/
public void setAntiAlias(boolean flag) {
if (flag) {
this.renderingHints.put(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
} else {
this.renderingHints.put(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_OFF);
}
fireChangeEvent();
}
/**
* Returns the flag that controls whether or not element hints will be
* added to the {@code Graphics2D} output when the chart is rendered.
* The default value is {@code false}.
*
* @return A boolean.
*
* @since 1.3
*/
public boolean getElementHinting() {
return this.elementHinting;
}
/**
* Sets the flag that controls whether or not element hints will be
* added to the {@code Graphics2D} output when the chart is rendered
* and sends a change event to all registered listeners.
*
* @param hinting the new flag value.
*
* @since 1.3
*/
public void setElementHinting(boolean hinting) {
this.elementHinting = hinting;
fireChangeEvent();
}
/**
* Returns the chart style.
*
* @return The chart style (never {@code null}).
*
* @since 1.2
*/
public ChartStyle getStyle() {
return this.style;
}
/**
* Sets (and applies) the specified chart style.
*
* @param style the chart style ({@code null} not permitted).
*
* @since 1.2
*/
public void setStyle(ChartStyle style) {
Args.nullNotPermitted(style, "style");
this.style.removeChangeListener(this);
this.style = style;
this.style.addChangeListener(this);
setNotify(false);
receive(new ChartStyler(this.style));
setNotify(true);
}
/**
* Creates a world containing the chart and the supplied chart box.
*
* @param chartBox the chart box ({@code null} permitted).
*/
private World createWorld(ChartBox3D chartBox) {
World result = new World();
Dimension3D dim = this.plot.getDimensions();
double w = dim.getWidth();
double h = dim.getHeight();
double d = dim.getDepth();
if (chartBox != null) {
result.add("chartbox", chartBox.createObject3D());
}
this.plot.compose(result, -w / 2, -h / 2, -d / 2);
return result;
}
/**
* Draws the chart to the specified output target.
*
* @param g2 the output target ({@code null} not permitted).
*
* @return Information about the items rendered.
*/
@Override
public RenderingInfo draw(Graphics2D g2, Rectangle2D bounds) {
beginElement(g2, this.id, "ORSON_CHART_TOP_LEVEL");
Shape savedClip = g2.getClip();
g2.clip(bounds);
g2.addRenderingHints(this.renderingHints);
g2.setStroke(new BasicStroke(1.5f, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND, 1f));
Dimension3D dim3D = this.plot.getDimensions();
double w = dim3D.getWidth();
double h = dim3D.getHeight();
double depth = dim3D.getDepth();
ChartBox3D chartBox = null;
if (this.plot instanceof XYZPlot
|| this.plot instanceof CategoryPlot3D) {
double[] tickUnits = findAxisTickUnits(g2, w, h, depth);
chartBox = new ChartBox3D(w, h, depth, -w / 2, -h / 2, -depth / 2,
this.chartBoxColor);
chartBox.setXTicks(fetchXTickData(this.plot, tickUnits[0]));
chartBox.setYTicks(fetchYTickData(this.plot, tickUnits[1]));
chartBox.setZTicks(fetchZTickData(this.plot, tickUnits[2]));
chartBox.setXMarkers(fetchXMarkerData(this.plot));
chartBox.setYMarkers(fetchYMarkerData(this.plot));
chartBox.setZMarkers(fetchZMarkerData(this.plot));
}
if (this.world == null) {
this.world = createWorld(chartBox);
} else if (chartBox != null) {
this.world.clear("chartbox");
this.world.add("chartbox", chartBox.createObject3D());
}
if (this.background != null) {
this.background.fill(g2, bounds);
}
AffineTransform saved = g2.getTransform();
double dx = bounds.getX() + bounds.getWidth() / 2.0
+ this.translate2D.getDX();
double dy = bounds.getY() + bounds.getHeight() / 2.0
+ this.translate2D.getDY();
g2.translate(dx, dy);
Point3D[] eyePts = this.world.calculateEyeCoordinates(this.viewPoint);
Point2D[] pts = this.world.calculateProjectedPoints(this.viewPoint,
this.projDist);
// sort faces by z-order
List facesInPaintOrder = new ArrayList<>(world.getFaces());
facesInPaintOrder = this.faceSorter.sort(facesInPaintOrder, eyePts);
Line2D line = null;
Stroke stroke = new BasicStroke(1.0f);
for (Face f : facesInPaintOrder) {
// check for the special case where the face is just a line
if (f.getVertexCount() == 2) {
g2.setPaint(f.getColor());
if (line == null) {
line = new Line2D.Float();
}
int v0 = f.getVertexIndex(0);
int v1 = f.getVertexIndex(1);
line.setLine(pts[v0].getX(), pts[v0].getY(), pts[v1].getX(),
pts[v1].getY());
g2.setStroke(stroke);
g2.draw(line);
continue;
}
boolean drawOutline = f.getOutline();
double[] plane = f.calculateNormal(eyePts);
double inprod = plane[0] * world.getSunX() + plane[1]
* world.getSunY() + plane[2] * world.getSunZ();
double shade = (inprod + 1) / 2.0;
if (f instanceof DoubleSidedFace
|| Utils2D.area2(pts[f.getVertexIndex(0)],
pts[f.getVertexIndex(1)], pts[f.getVertexIndex(2)]) > 0.0) {
Color c = f.getColor();
Path2D p = f.createPath(pts);
g2.setPaint(new Color((int) (c.getRed() * shade),
(int) (c.getGreen() * shade),
(int) (c.getBlue() * shade), c.getAlpha()));
if (this.elementHinting) {
beginElementGroup(f, g2);
}
g2.fill(p);
if (drawOutline) {
g2.draw(p);
}
if (this.elementHinting) {
endElementGroup(f, g2);
}
if (f instanceof ChartBoxFace
&& (this.plot instanceof CategoryPlot3D
|| this.plot instanceof XYZPlot)) {
Stroke savedStroke = g2.getStroke();
ChartBoxFace cbf = (ChartBoxFace) f;
drawGridlines(g2, cbf, pts);
drawMarkers(g2, cbf, pts);
g2.setStroke(savedStroke);
}
} else if (f instanceof LabelFace) {
LabelFace lf = (LabelFace) f;
Path2D p = lf.createPath(pts);
Rectangle2D lb = p.getBounds2D();
g2.setFont(lf.getFont());
g2.setColor(lf.getBackgroundColor());
Rectangle2D bb = TextUtils.calcAlignedStringBounds(
lf.getLabel(), g2,
(float) lb.getCenterX(), (float) lb.getCenterY(),
TextAnchor.CENTER);
g2.fill(bb);
g2.setColor(lf.getTextColor());
Rectangle2D r = TextUtils.drawAlignedString(lf.getLabel(), g2,
(float) lb.getCenterX(), (float) lb.getCenterY(),
TextAnchor.CENTER);
lf.getOwner().setProperty("labelBounds", r);
}
}
RenderingInfo info = new RenderingInfo(facesInPaintOrder, pts, dx, dy);
OnDrawHandler onDrawHandler = new OnDrawHandler(info,
this.elementHinting);
// handle labels on pie plots...
if (this.plot instanceof PiePlot3D) {
drawPieLabels(g2, w, h, depth, info);
}
// handle axis labelling on non-pie plots...
if (this.plot instanceof XYZPlot || this.plot instanceof
CategoryPlot3D) {
drawAxes(g2, chartBox, pts, info);
}
g2.setTransform(saved);
// generate and draw the legend...
if (this.legendBuilder != null) {
TableElement legend = this.legendBuilder.createLegend(this.plot,
this.legendAnchor, this.legendOrientation, this.style);
if (legend != null) {
Dimension2D legendSize = legend.preferredSize(g2, bounds);
Rectangle2D legendArea = calculateDrawArea(legendSize,
this.legendAnchor, bounds);
legend.draw(g2, legendArea, onDrawHandler);
}
}
// draw the title...
if (this.title != null) {
Dimension2D titleSize = this.title.preferredSize(g2, bounds);
Rectangle2D titleArea = calculateDrawArea(titleSize,
this.titleAnchor, bounds);
this.title.draw(g2, titleArea, onDrawHandler);
}
g2.setClip(savedClip);
endElement(g2);
return info;
}
private void beginElementGroup(Face face, Graphics2D g2) {
Object3D owner = face.getOwner();
ItemKey itemKey = (ItemKey) owner.getProperty(Object3D.ITEM_KEY);
if (itemKey != null) {
Map m = new HashMap<>();
m.put("ref", itemKey.toJSONString());
g2.setRenderingHint(Chart3DHints.KEY_BEGIN_ELEMENT, m);
}
}
private void endElementGroup(Face face, Graphics2D g2) {
Object3D owner = face.getOwner();
ItemKey itemKey = (ItemKey) owner.getProperty(Object3D.ITEM_KEY);
if (itemKey != null) {
g2.setRenderingHint(Chart3DHints.KEY_END_ELEMENT, Boolean.TRUE);
}
}
/**
* An implementation method that fetches x-axis tick data from the plot,
* assuming it is either a {@link CategoryPlot3D} or an {@link XYZPlot}.
* On a category plot, the x-axis is the column axis (and the tickUnit is
* ignored).
*
* @param plot the plot.
* @param tickUnit the tick unit.
*
* @return A list of tick data instances representing the tick marks and
* values along the x-axis.
*/
private List fetchXTickData(Plot3D plot, double tickUnit) {
if (plot instanceof CategoryPlot3D) {
CategoryPlot3D cp = (CategoryPlot3D) plot;
return cp.getColumnAxis().generateTickDataForColumns(
cp.getDataset());
}
if (plot instanceof XYZPlot) {
XYZPlot xp = (XYZPlot) plot;
return xp.getXAxis().generateTickData(tickUnit);
}
return Collections.emptyList();
}
/**
* An implementation method that fetches y-axis tick data from the plot,
* assuming it is either a {@link CategoryPlot3D} or an {@link XYZPlot}.
* On a category plot, the y-axis is the value axis.
*
* @param plot the plot.
* @param tickUnit the tick unit.
*
* @return A list of tick data instances representing the tick marks and
* values along the y-axis.
*/
private List fetchYTickData(Plot3D plot, double tickUnit) {
if (plot instanceof CategoryPlot3D) {
CategoryPlot3D cp = (CategoryPlot3D) plot;
return cp.getValueAxis().generateTickData(tickUnit);
}
if (plot instanceof XYZPlot) {
XYZPlot xp = (XYZPlot) plot;
return xp.getYAxis().generateTickData(tickUnit);
}
return Collections.emptyList();
}
/**
* An implementation method that fetches z-axis tick data from the plot,
* assuming it is either a {@link CategoryPlot3D} or an {@link XYZPlot}.
* On a category plot, the z-axis is the row axis (and the tickUnit is
* ignored).
*
* @param plot the plot.
* @param tickUnit the tick unit.
*
* @return A list of tick data instances representing the tick marks and
* values along the y-axis.
*/
private List fetchZTickData(Plot3D plot, double tickUnit) {
if (plot instanceof CategoryPlot3D) {
CategoryPlot3D cp = (CategoryPlot3D) plot;
return cp.getRowAxis().generateTickDataForRows(cp.getDataset());
}
if (plot instanceof XYZPlot) {
XYZPlot xp = (XYZPlot) plot;
return xp.getZAxis().generateTickData(tickUnit);
}
return Collections.emptyList();
}
/**
* Fetches marker data for the plot's x-axis.
*
* @param plot the plot ({@code null} not permitted).
*
* @return A list of marker data items (possibly empty but never
* {@code null}).
*/
private List fetchXMarkerData(Plot3D plot) {
if (plot instanceof CategoryPlot3D) {
return ((CategoryPlot3D) plot).getColumnAxis().generateMarkerData();
}
if (plot instanceof XYZPlot) {
return ((XYZPlot) plot).getXAxis().generateMarkerData();
}
return new ArrayList<>(0);
}
/**
* Fetches marker data for the plot's x-axis.
*
* @param plot the plot ({@code null} not permitted).
*
* @return A list of marker data items (possibly empty but never
* {@code null}).
*/
private List fetchYMarkerData(Plot3D plot) {
if (plot instanceof CategoryPlot3D) {
return ((CategoryPlot3D) plot).getValueAxis().generateMarkerData();
}
if (plot instanceof XYZPlot) {
return ((XYZPlot) plot).getYAxis().generateMarkerData();
}
return new ArrayList<>(0);
}
/**
* Fetches marker data for the plot's x-axis.
*
* @param plot the plot ({@code null} not permitted).
*
* @return A list of marker data items (possibly empty but never
* {@code null}).
*/
private List fetchZMarkerData(Plot3D plot) {
if (plot instanceof CategoryPlot3D) {
return ((CategoryPlot3D) plot).getRowAxis().generateMarkerData();
}
if (plot instanceof XYZPlot) {
return ((XYZPlot) plot).getZAxis().generateMarkerData();
}
return new ArrayList<>(0);
}
/**
* Draw the gridlines for one chart box face.
*
* @param g2 the graphics target.
* @param face the face.
* @param pts the projection points.
*/
private void drawGridlines(Graphics2D g2, ChartBoxFace face,
Point2D[] pts) {
if (isGridlinesVisibleForX(this.plot)) {
g2.setPaint(fetchGridlinePaintX(this.plot));
g2.setStroke(fetchGridlineStrokeX(this.plot));
List xA = face.getXTicksA();
List xB = face.getXTicksB();
for (int i = 0; i < xA.size(); i++) {
Line2D line = new Line2D.Double(
pts[face.getOffset() + xA.get(i).getVertexIndex()],
pts[face.getOffset() + xB.get(i).getVertexIndex()]);
g2.draw(line);
}
}
if (isGridlinesVisibleForY(this.plot)) {
g2.setPaint(fetchGridlinePaintY(this.plot));
g2.setStroke(fetchGridlineStrokeY(this.plot));
List yA = face.getYTicksA();
List yB = face.getYTicksB();
for (int i = 0; i < yA.size(); i++) {
Line2D line = new Line2D.Double(
pts[face.getOffset() + yA.get(i).getVertexIndex()],
pts[face.getOffset() + yB.get(i).getVertexIndex()]);
g2.draw(line);
}
}
if (isGridlinesVisibleForZ(this.plot)) {
g2.setPaint(fetchGridlinePaintZ(this.plot));
g2.setStroke(fetchGridlineStrokeZ(this.plot));
List zA = face.getZTicksA();
List zB = face.getZTicksB();
for (int i = 0; i < zA.size(); i++) {
Line2D line = new Line2D.Double(
pts[face.getOffset() + zA.get(i).getVertexIndex()],
pts[face.getOffset() + zB.get(i).getVertexIndex()]);
g2.draw(line);
}
}
}
/**
* Returns {@code true} if gridlines are visible for the x-axis
* (column axis in the case of a {@link CategoryPlot3D}) and
* {@code false} otherwise. For pie charts, this method will always
* return {@code false}.
*
* @param plot the plot.
*
* @return A boolean.
*/
private boolean isGridlinesVisibleForX(Plot3D plot) {
if (plot instanceof CategoryPlot3D) {
CategoryPlot3D cp = (CategoryPlot3D) plot;
return cp.getGridlinesVisibleForColumns();
}
if (plot instanceof XYZPlot) {
XYZPlot xp = (XYZPlot) plot;
return xp.isGridlinesVisibleX();
}
return false;
}
/**
* Returns {@code true} if gridlines are visible for the y-axis
* (value axis in the case of a {@link CategoryPlot3D}) and
* {@code false} otherwise.
*
* @param plot the plot.
*
* @return A boolean.
*/
private boolean isGridlinesVisibleForY(Plot3D plot) {
if (plot instanceof CategoryPlot3D) {
CategoryPlot3D cp = (CategoryPlot3D) plot;
return cp.getGridlinesVisibleForValues();
}
if (plot instanceof XYZPlot) {
XYZPlot xp = (XYZPlot) plot;
return xp.isGridlinesVisibleY();
}
return false;
}
/**
* Returns {@code true} if gridlines are visible for the z-axis
* (row axis in the case of a {@link CategoryPlot3D}) and
* {@code false} otherwise.
*
* @param plot the plot.
*
* @return A boolean.
*/
private boolean isGridlinesVisibleForZ(Plot3D plot) {
if (plot instanceof CategoryPlot3D) {
CategoryPlot3D cp = (CategoryPlot3D) plot;
return cp.getGridlinesVisibleForRows();
}
if (plot instanceof XYZPlot) {
XYZPlot xp = (XYZPlot) plot;
return xp.isGridlinesVisibleZ();
}
return false;
}
/**
* Returns the paint used to draw gridlines on the x-axis (or column axis
* in the case of {@link CategoryPlot3D}).
*
* @param plot the plot.
*
* @return The paint.
*/
private Paint fetchGridlinePaintX(Plot3D plot) {
if (plot instanceof CategoryPlot3D) {
CategoryPlot3D cp = (CategoryPlot3D) plot;
return cp.getGridlinePaintForColumns();
}
if (plot instanceof XYZPlot) {
XYZPlot xp = (XYZPlot) plot;
return xp.getGridlinePaintX();
}
return null;
}
/**
* Returns the paint used to draw gridlines on the y-axis (or value axis
* in the case of {@link CategoryPlot3D}).
*
* @param plot the plot.
*
* @return The paint.
*/
private Paint fetchGridlinePaintY(Plot3D plot) {
if (plot instanceof CategoryPlot3D) {
CategoryPlot3D cp = (CategoryPlot3D) plot;
return cp.getGridlinePaintForValues();
}
if (plot instanceof XYZPlot) {
XYZPlot xp = (XYZPlot) plot;
return xp.getGridlinePaintY();
}
return null;
}
/**
* Returns the paint used to draw gridlines on the z-axis (or row axis
* in the case of {@link CategoryPlot3D}).
*
* @param plot the plot.
*
* @return The paint.
*/
private Paint fetchGridlinePaintZ(Plot3D plot) {
if (plot instanceof CategoryPlot3D) {
CategoryPlot3D cp = (CategoryPlot3D) plot;
return cp.getGridlinePaintForRows();
}
if (plot instanceof XYZPlot) {
XYZPlot xp = (XYZPlot) plot;
return xp.getGridlinePaintZ();
}
return null;
}
/**
* Returns the stroke used to draw gridlines on the x-axis (or column axis
* in the case of {@link CategoryPlot3D}).
*
* @param plot the plot.
*
* @return The stroke.
*/
private Stroke fetchGridlineStrokeX(Plot3D plot) {
if (plot instanceof CategoryPlot3D) {
CategoryPlot3D cp = (CategoryPlot3D) plot;
return cp.getGridlineStrokeForColumns();
}
if (plot instanceof XYZPlot) {
XYZPlot xp = (XYZPlot) plot;
return xp.getGridlineStrokeX();
}
return null;
}
/**
* Returns the stroke used to draw gridlines on the y-axis (or value axis
* in the case of {@link CategoryPlot3D}).
*
* @param plot the plot.
*
* @return The stroke.
*/
private Stroke fetchGridlineStrokeY(Plot3D plot) {
if (plot instanceof CategoryPlot3D) {
CategoryPlot3D cp = (CategoryPlot3D) plot;
return cp.getGridlineStrokeForValues();
}
if (plot instanceof XYZPlot) {
XYZPlot xp = (XYZPlot) plot;
return xp.getGridlineStrokeY();
}
return null;
}
/**
* Returns the stroke used to draw gridlines on the z-axis (or row axis
* in the case of {@link CategoryPlot3D}).
*
* @param plot the plot.
*
* @return The stroke.
*/
private Stroke fetchGridlineStrokeZ(Plot3D plot) {
if (plot instanceof CategoryPlot3D) {
CategoryPlot3D cp = (CategoryPlot3D) plot;
return cp.getGridlineStrokeForRows();
}
if (plot instanceof XYZPlot) {
XYZPlot xp = (XYZPlot) plot;
return xp.getGridlineStrokeZ();
}
return null;
}
/**
* Draws the pie labels for a {@link PiePlot3D} in 2D-space by creating a
* temporary world with vertices at anchor points for the labels, then
* projecting the points to 2D-space.
*
* @param g2 the graphics target.
* @param w the width.
* @param h the height.
* @param depth the depth.
* @param info the rendering info ({@code null} permitted).
*/
@SuppressWarnings("unchecked")
private void drawPieLabels(Graphics2D g2, double w, double h,
double depth, RenderingInfo info) {
PiePlot3D p = (PiePlot3D) this.plot;
World labelOverlay = new World();
List objs = p.getLabelFaces(-w / 2, -h / 2, -depth / 2);
for (Object3D obj : objs) {
labelOverlay.add(obj);
}
Point2D[] ppts = labelOverlay.calculateProjectedPoints(
this.viewPoint, this.projDist);
for (int i = 0; i < p.getDataset().getItemCount() * 2; i++) {
if (p.getDataset().getValue(i / 2) == null) {
continue;
}
Face f = labelOverlay.getFaces().get(i);
if (Utils2D.area2(ppts[f.getVertexIndex(0)],
ppts[f.getVertexIndex(1)],
ppts[f.getVertexIndex(2)]) > 0) {
Comparable> key = p.getDataset().getKey(i / 2);
g2.setColor(p.getSectionLabelColorSource().getColor(key));
g2.setFont(p.getSectionLabelFontSource().getFont(key));
Point2D pt = Utils2D.centerPoint(ppts[f.getVertexIndex(0)],
ppts[f.getVertexIndex(1)], ppts[f.getVertexIndex(2)],
ppts[f.getVertexIndex(3)]);
String label = p.getSectionLabelGenerator().generateLabel(
p.getDataset(), key);
String ref = "{\"type\": \"sectionLabel\", \"key\": \""
+ key.toString() + "\"}";
beginElementWithRef(g2, ref);
Rectangle2D bounds = TextUtils.drawAlignedString(label, g2,
(float) pt.getX(), (float) pt.getY(),
TextAnchor.CENTER);
endElement(g2);
if (info != null) {
RenderedElement pieLabelRE = new RenderedElement(
InteractiveElementType.SECTION_LABEL, bounds);
pieLabelRE.setProperty("key", key);
info.addOffsetElement(pieLabelRE);
}
}
}
}
private void beginElementWithRef(Graphics2D g2, String ref) {
beginElement(g2, null, ref);
}
private void beginElement(Graphics2D g2, String id, String ref) {
if (this.elementHinting) {
Map m = new HashMap<>();
if (id != null) {
m.put("id", id);
}
m.put("ref", ref);
g2.setRenderingHint(Chart3DHints.KEY_BEGIN_ELEMENT, m);
}
}
private void endElement(Graphics2D g2) {
if (this.elementHinting) {
g2.setRenderingHint(Chart3DHints.KEY_END_ELEMENT, Boolean.TRUE);
}
}
/**
* Determines appropriate tick units for the axes in the chart.
*
* @param g2 the graphics target.
* @param w the width.
* @param h the height.
* @param depth the depth.
*
* @return The tick sizes.
*/
private double[] findAxisTickUnits(Graphics2D g2, double w, double h,
double depth) {
World tempWorld = new World();
ChartBox3D chartBox = new ChartBox3D(w, h, depth, -w / 2.0, -h / 2.0,
-depth / 2.0, Color.WHITE);
tempWorld.add(chartBox.createObject3D());
Point2D[] axisPts2D = tempWorld.calculateProjectedPoints(
this.viewPoint, this.projDist);
// vertices
Point2D v0 = axisPts2D[0];
Point2D v1 = axisPts2D[1];
Point2D v2 = axisPts2D[2];
Point2D v3 = axisPts2D[3];
Point2D v4 = axisPts2D[4];
Point2D v5 = axisPts2D[5];
Point2D v6 = axisPts2D[6];
Point2D v7 = axisPts2D[7];
// faces
boolean a = chartBox.faceA().isFrontFacing(axisPts2D);
boolean b = chartBox.faceB().isFrontFacing(axisPts2D);
boolean c = chartBox.faceC().isFrontFacing(axisPts2D);
boolean d = chartBox.faceD().isFrontFacing(axisPts2D);
boolean e = chartBox.faceE().isFrontFacing(axisPts2D);
boolean f = chartBox.faceF().isFrontFacing(axisPts2D);
double xtick = 0, ytick = 0, ztick = 0;
Axis3D xAxis = null;
ValueAxis3D yAxis = null;
Axis3D zAxis = null;
if (this.plot instanceof XYZPlot) {
XYZPlot pp = (XYZPlot) this.plot;
xAxis = pp.getXAxis();
yAxis = pp.getYAxis();
zAxis = pp.getZAxis();
} else if (this.plot instanceof CategoryPlot3D) {
CategoryPlot3D pp = (CategoryPlot3D) this.plot;
xAxis = pp.getColumnAxis();
yAxis = pp.getValueAxis();
zAxis = pp.getRowAxis();
}
if (xAxis != null && yAxis != null && zAxis != null) {
double ab = (count(a, b) == 1 ? v0.distance(v1) : 0.0);
double bc = (count(b, c) == 1 ? v3.distance(v2) : 0.0);
double cd = (count(c, d) == 1 ? v4.distance(v7) : 0.0);
double da = (count(d, a) == 1 ? v5.distance(v6) : 0.0);
double be = (count(b, e) == 1 ? v0.distance(v3) : 0.0);
double bf = (count(b, f) == 1 ? v1.distance(v2) : 0.0);
double df = (count(d, f) == 1 ? v6.distance(v7) : 0.0);
double de = (count(d, e) == 1 ? v5.distance(v4) : 0.0);
double ae = (count(a, e) == 1 ? v0.distance(v5) : 0.0);
double af = (count(a, f) == 1 ? v1.distance(v6) : 0.0);
double cf = (count(c, f) == 1 ? v2.distance(v7) : 0.0);
double ce = (count(c, e) == 1 ? v3.distance(v4) : 0.0);
if (count(a, b) == 1 && longest(ab, bc, cd, da)) {
if (xAxis instanceof ValueAxis3D) {
xtick = ((ValueAxis3D) xAxis).selectTick(g2, v0, v1, v7);
}
}
if (count(b, c) == 1 && longest(bc, ab, cd, da)) {
if (xAxis instanceof ValueAxis3D) {
xtick = ((ValueAxis3D) xAxis).selectTick(g2, v3, v2, v6);
}
}
if (count(c, d) == 1 && longest(cd, ab, bc, da)) {
if (xAxis instanceof ValueAxis3D) {
xtick = ((ValueAxis3D) xAxis).selectTick(g2, v4, v7, v1);
}
}
if (count(d, a) == 1 && longest(da, ab, bc, cd)) {
if (xAxis instanceof ValueAxis3D) {
xtick = ((ValueAxis3D) xAxis).selectTick(g2, v5, v6, v3);
}
}
if (count(b, e) == 1 && longest(be, bf, df, de)) {
ytick = yAxis.selectTick(g2, v0, v3, v7);
}
if (count(b, f) == 1 && longest(bf, be, df, de)) {
ytick = yAxis.selectTick(g2, v1, v2, v4);
}
if (count(d, f) == 1 && longest(df, be, bf, de)) {
ytick = yAxis.selectTick(g2, v6, v7, v0);
}
if (count(d, e) == 1 && longest(de, be, bf, df)) {
ytick = yAxis.selectTick(g2, v5, v4, v1);
}
if (count(a, e) == 1 && longest(ae, af, cf, ce)) {
if (zAxis instanceof ValueAxis3D) {
ztick = ((ValueAxis3D) zAxis).selectTick(g2, v0, v5, v2);
}
}
if (count(a, f) == 1 && longest(af, ae, cf, ce)) {
if (zAxis instanceof ValueAxis3D) {
ztick = ((ValueAxis3D) zAxis).selectTick(g2, v1, v6, v3);
}
}
if (count(c, f) == 1 && longest(cf, ae, af, ce)) {
if (zAxis instanceof ValueAxis3D) {
ztick = ((ValueAxis3D) zAxis).selectTick(g2, v2, v7, v5);
}
}
if (count(c, e) == 1 && longest(ce, ae, af, cf)) {
if (zAxis instanceof ValueAxis3D) {
ztick = ((ValueAxis3D) zAxis).selectTick(g2, v3, v4, v6);
}
}
}
return new double[] { xtick, ytick, ztick };
}
private void populateAnchorPoints(List tickData, Point2D[] pts) {
for (TickData t : tickData) {
t.setAnchorPt(pts[t.getVertexIndex()]);
}
}
/**
* Draws the axes for a chart.
*
* @param g2 the graphics target ({@code null} not permitted).
* @param chartBox the chart box (this contains projected points for
* the tick marks and labels)
* @param pts the projected points.
* @param info an object to be populated with rendering info, if it is
* non-{@code null}.
*/
private void drawAxes(Graphics2D g2, ChartBox3D chartBox, Point2D[] pts,
RenderingInfo info) {
// vertices
Point2D v0 = pts[0];
Point2D v1 = pts[1];
Point2D v2 = pts[2];
Point2D v3 = pts[3];
Point2D v4 = pts[4];
Point2D v5 = pts[5];
Point2D v6 = pts[6];
Point2D v7 = pts[7];
// faces
boolean a = chartBox.faceA().isFrontFacing(pts);
boolean b = chartBox.faceB().isFrontFacing(pts);
boolean c = chartBox.faceC().isFrontFacing(pts);
boolean d = chartBox.faceD().isFrontFacing(pts);
boolean e = chartBox.faceE().isFrontFacing(pts);
boolean f = chartBox.faceF().isFrontFacing(pts);
Axis3D xAxis = null, yAxis = null, zAxis = null;
if (this.plot instanceof XYZPlot) {
XYZPlot pp = (XYZPlot) this.plot;
xAxis = pp.getXAxis();
yAxis = pp.getYAxis();
zAxis = pp.getZAxis();
} else if (this.plot instanceof CategoryPlot3D) {
CategoryPlot3D pp = (CategoryPlot3D) this.plot;
xAxis = pp.getColumnAxis();
yAxis = pp.getValueAxis();
zAxis = pp.getRowAxis();
}
if (xAxis != null && yAxis != null && zAxis != null) {
double ab = (count(a, b) == 1 ? v0.distance(v1) : 0.0);
double bc = (count(b, c) == 1 ? v3.distance(v2) : 0.0);
double cd = (count(c, d) == 1 ? v4.distance(v7) : 0.0);
double da = (count(d, a) == 1 ? v5.distance(v6) : 0.0);
double be = (count(b, e) == 1 ? v0.distance(v3) : 0.0);
double bf = (count(b, f) == 1 ? v1.distance(v2) : 0.0);
double df = (count(d, f) == 1 ? v6.distance(v7) : 0.0);
double de = (count(d, e) == 1 ? v5.distance(v4) : 0.0);
double ae = (count(a, e) == 1 ? v0.distance(v5) : 0.0);
double af = (count(a, f) == 1 ? v1.distance(v6) : 0.0);
double cf = (count(c, f) == 1 ? v2.distance(v7) : 0.0);
double ce = (count(c, e) == 1 ? v3.distance(v4) : 0.0);
List ticks;
if (count(a, b) == 1 && longest(ab, bc, cd, da)) {
ticks = chartBox.faceA().getXTicksA();
populateAnchorPoints(ticks, pts);
xAxis.draw(g2, v0, v1, v7, ticks, info, this.elementHinting);
}
if (count(b, c) == 1 && longest(bc, ab, cd, da)) {
ticks = chartBox.faceB().getXTicksB();
populateAnchorPoints(ticks, pts);
xAxis.draw(g2, v3, v2, v6, ticks, info, this.elementHinting);
}
if (count(c, d) == 1 && longest(cd, ab, bc, da)) {
ticks = chartBox.faceC().getXTicksB();
populateAnchorPoints(ticks, pts);
xAxis.draw(g2, v4, v7, v1, ticks, info, this.elementHinting);
}
if (count(d, a) == 1 && longest(da, ab, bc, cd)) {
ticks = chartBox.faceA().getXTicksB();
populateAnchorPoints(ticks, pts);
xAxis.draw(g2, v5, v6, v3, ticks, info, this.elementHinting);
}
if (count(b, e) == 1 && longest(be, bf, df, de)) {
ticks = chartBox.faceB().getYTicksA();
populateAnchorPoints(ticks, pts);
yAxis.draw(g2, v0, v3, v7, ticks, info, this.elementHinting);
}
if (count(b, f) == 1 && longest(bf, be, df, de)) {
ticks = chartBox.faceB().getYTicksB();
populateAnchorPoints(ticks, pts);
yAxis.draw(g2, v1, v2, v4, ticks, info, this.elementHinting);
}
if (count(d, f) == 1 && longest(df, be, bf, de)) {
ticks = chartBox.faceD().getYTicksA();
populateAnchorPoints(ticks, pts);
yAxis.draw(g2, v6, v7, v0, ticks, info, this.elementHinting);
}
if (count(d, e) == 1 && longest(de, be, bf, df)) {
ticks = chartBox.faceD().getYTicksB();
populateAnchorPoints(ticks, pts);
yAxis.draw(g2, v5, v4, v1, ticks, info, this.elementHinting);
}
if (count(a, e) == 1 && longest(ae, af, cf, ce)) {
ticks = chartBox.faceA().getZTicksA();
populateAnchorPoints(ticks, pts);
zAxis.draw(g2, v0, v5, v2, ticks, info, this.elementHinting);
}
if (count(a, f) == 1 && longest(af, ae, cf, ce)) {
ticks = chartBox.faceA().getZTicksB();
populateAnchorPoints(ticks, pts);
zAxis.draw(g2, v1, v6, v3, ticks, info, this.elementHinting);
}
if (count(c, f) == 1 && longest(cf, ae, af, ce)) {
ticks = chartBox.faceC().getZTicksB();
populateAnchorPoints(ticks, pts);
zAxis.draw(g2, v2, v7, v5, ticks, info, this.elementHinting);
}
if (count(c, e) == 1 && longest(ce, ae, af, cf)) {
ticks = chartBox.faceC().getZTicksA();
populateAnchorPoints(ticks, pts);
zAxis.draw(g2, v3, v4, v6, ticks, info, this.elementHinting);
}
}
}
/**
* Draws the markers for one face on a chart box. The {@code pts}
* array contains all the projected points for all the vertices in the
* world...the chart box face references the required points by index.
*
* @param g2 the graphics target ({@code null} not permitted).
* @param face the face of the chart box ({@code null} not permitted).
* @param pts the projected points for the whole world.
*/
private void drawMarkers(Graphics2D g2, ChartBoxFace face, Point2D[] pts) {
// x markers
List xmarkers = face.getXMarkers();
for (MarkerData m : xmarkers) {
m.updateProjection(pts);
Marker marker = fetchXMarker(this.plot, m.getMarkerKey());
beginElementWithRef(g2, "{\"type\": \"xMarker\", \"key\": \""
+ m.getMarkerKey() + "\"}");
marker.draw(g2, m, true);
endElement(g2);
}
// y markers
List ymarkers = face.getYMarkers();
for (MarkerData m : ymarkers) {
m.updateProjection(pts);
Marker marker = fetchYMarker(this.plot, m.getMarkerKey());
beginElementWithRef(g2, "{\"type\": \"yMarker\", \"key\": \""
+ m.getMarkerKey() + "\"}");
marker.draw(g2, m, false);
endElement(g2);
}
// z markers
List zmarkers = face.getZMarkers();
for (MarkerData m : zmarkers) {
m.updateProjection(pts);
beginElementWithRef(g2, "{\"type\": \"zMarker\", \"key\": \""
+ m.getMarkerKey() + "\"}");
Marker marker = fetchZMarker(this.plot, m.getMarkerKey());
marker.draw(g2, m, false);
endElement(g2);
}
}
/**
* Returns the marker from the plot's x-axis that has the specified key,
* or {@code null} if there is no marker with that key.
*
* @param plot the plot ({@code null} not permitted).
* @param key the marker key ({@code null} not permitted).
*
* @return The marker (possibly {@code null}).
*/
private Marker fetchXMarker(Plot3D plot, String key) {
if (plot instanceof CategoryPlot3D) {
return ((CategoryPlot3D) plot).getColumnAxis().getMarker(key);
} else if (plot instanceof XYZPlot) {
return ((XYZPlot) plot).getXAxis().getMarker(key);
}
return null;
}
/**
* Returns the marker from the plot's y-axis that has the specified key,
* or {@code null} if there is no marker with that key.
*
* @param plot the plot ({@code null} not permitted).
* @param key the marker key ({@code null} not permitted).
*
* @return The marker (possibly {@code null}).
*/
private Marker fetchYMarker(Plot3D plot, String key) {
if (plot instanceof CategoryPlot3D) {
return ((CategoryPlot3D) plot).getValueAxis().getMarker(key);
} else if (plot instanceof XYZPlot) {
return ((XYZPlot) plot).getYAxis().getMarker(key);
}
return null;
}
/**
* Returns the marker from the plot's z-axis that has the specified key,
* or {@code null} if there is no marker with that key.
*
* @param plot the plot ({@code null} not permitted).
* @param key the marker key ({@code null} not permitted).
*
* @return The marker (possibly {@code null}).
*/
private Marker fetchZMarker(Plot3D plot, String key) {
if (plot instanceof CategoryPlot3D) {
return ((CategoryPlot3D) plot).getRowAxis().getMarker(key);
} else if (plot instanceof XYZPlot) {
return ((XYZPlot) plot).getZAxis().getMarker(key);
}
return null;
}
/**
* Receives a visitor. The visitor is first directed to the plot, then
* the visit is completed for the chart.
*
* @param visitor the visitor.
*
* @since 1.2
*/
@Override
public void receive(ChartElementVisitor visitor) {
this.plot.receive(visitor);
visitor.visit(this);
}
/**
* Tests this chart for equality with an arbitrary object.
*
* @param obj the object ({@code null} not permitted).
*
* @return A boolean.
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Chart3D)) {
return false;
}
Chart3D that = (Chart3D) obj;
if (!ObjectUtils.equals(this.background, that.background)) {
return false;
}
if (!ObjectUtils.equals(this.title, that.title)) {
return false;
}
if (!this.titleAnchor.equals(that.titleAnchor)) {
return false;
}
if (!ObjectUtils.equalsPaint(this.chartBoxColor, that.chartBoxColor)) {
return false;
}
if (!ObjectUtils.equals(this.legendBuilder, that.legendBuilder)) {
return false;
}
if (!this.legendAnchor.equals(that.legendAnchor)) {
return false;
}
if (this.legendOrientation != that.legendOrientation) {
return false;
}
if (!this.renderingHints.equals(that.renderingHints)) {
return false;
}
if (this.projDist != that.projDist) {
return false;
}
return true;
}
/**
* A utility method that calculates a drawing area based on a bounding area
* and an anchor.
*
* @param dim the dimensions for the drawing area ({@code null} not
* permitted).
* @param anchor the anchor ({@code null} not permitted).
* @param bounds the bounds ({@code null} not permitted).
*
* @return A drawing area.
*/
private Rectangle2D calculateDrawArea(Dimension2D dim, Anchor2D anchor,
Rectangle2D bounds) {
Args.nullNotPermitted(dim, "dim");
Args.nullNotPermitted(anchor, "anchor");
Args.nullNotPermitted(bounds, "bounds");
double x, y;
double w = Math.min(dim.getWidth(), bounds.getWidth());
double h = Math.min(dim.getHeight(), bounds.getHeight());
if (anchor.getRefPt().equals(RefPt2D.CENTER)) {
x = bounds.getCenterX() - w / 2.0;
y = bounds.getCenterY() - h / 2.0;
} else if (anchor.getRefPt().equals(RefPt2D.CENTER_LEFT)) {
x = bounds.getX() + anchor.getOffset().getDX();
y = bounds.getCenterY() - h / 2.0;
} else if (anchor.getRefPt().equals(RefPt2D.CENTER_RIGHT)) {
x = bounds.getMaxX() - anchor.getOffset().getDX() - dim.getWidth();
y = bounds.getCenterY() - h / 2.0;
} else if (anchor.getRefPt().equals(RefPt2D.TOP_CENTER)) {
x = bounds.getCenterX() - w / 2.0;
y = bounds.getY() + anchor.getOffset().getDY();
} else if (anchor.getRefPt().equals(RefPt2D.TOP_LEFT)) {
x = bounds.getX() + anchor.getOffset().getDX();
y = bounds.getY() + anchor.getOffset().getDY();
} else if (anchor.getRefPt().equals(RefPt2D.TOP_RIGHT)) {
x = bounds.getMaxX() - anchor.getOffset().getDX() - dim.getWidth();
y = bounds.getY() + anchor.getOffset().getDY();
} else if (anchor.getRefPt().equals(RefPt2D.BOTTOM_CENTER)) {
x = bounds.getCenterX() - w / 2.0;
y = bounds.getMaxY() - anchor.getOffset().getDY() - dim.getHeight();
} else if (anchor.getRefPt().equals(RefPt2D.BOTTOM_RIGHT)) {
x = bounds.getMaxX() - anchor.getOffset().getDX() - dim.getWidth();
y = bounds.getMaxY() - anchor.getOffset().getDY() - dim.getHeight();
} else if (anchor.getRefPt().equals(RefPt2D.BOTTOM_LEFT)) {
x = bounds.getX() + anchor.getOffset().getDX();
y = bounds.getMaxY() - anchor.getOffset().getDY() - dim.getHeight();
} else {
x = 0.0;
y = 0.0;
}
return new Rectangle2D.Double(x, y, w, h);
}
/**
* Returns {@code true} if x is the longest of the four lengths,
* and {@code false} otherwise.
*
* @param x the x-length.
* @param a length 1.
* @param b length 2.
* @param c length 3.
*
* @return A boolean.
*/
private boolean longest(double x, double a, double b, double c) {
return x >= a && x >= b && x >= c;
}
/**
* Returns the number (0, 1 or 2) arguments that have the value
* {@code true}. We use this to examine the visibility of
* adjacent walls of the chart box...where only one wall is visible, there
* is an opportunity to display the axis along that edge.
*
* @param a boolean argument 1.
* @param b boolean argument 2.
*
* @return 0, 1, or 2.
*/
private int count(boolean a, boolean b) {
int result = 0;
if (a) {
result++;
}
if (b) {
result++;
}
return result;
}
/**
* Receives notification of a plot change event, refreshes the 3D model
* (world) and passes the event on, wrapped in a {@link Chart3DChangeEvent},
* to all registered listeners.
*
* @param event the plot change event.
*/
@Override
public void plotChanged(Plot3DChangeEvent event) {
if (event.requiresWorldUpdate()) {
this.world = null;
}
notifyListeners(new Chart3DChangeEvent(event, this));
}
@Override
public void styleChanged(ChartStyleChangeEvent event) {
ChartStyler styler = new ChartStyler(event.getChartStyle());
receive(styler);
// create a visitor that will visit all chart components and apply the
// style
notifyListeners(new Chart3DChangeEvent(event, this));
}
/**
* Registers a listener to receive notification of changes to the chart.
*
* @param listener the listener ({@code null} not permitted).
*/
public void addChangeListener(Chart3DChangeListener listener) {
this.listenerList.add(Chart3DChangeListener.class, listener);
}
/**
* Deregisters a listener so that it no longer receives notification of
* changes to the chart.
*
* @param listener the listener ({@code null} not permitted).
*/
public void removeChangeListener(Chart3DChangeListener listener) {
this.listenerList.remove(Chart3DChangeListener.class, listener);
}
/**
* Notifies all registered listeners that the chart has been modified.
*
* @param event information about the change event.
*/
public void notifyListeners(Chart3DChangeEvent event) {
// if the 'notify' flag has been switched to false, we don't notify
// the listeners
if (!this.notify) {
return;
}
Object[] listeners = this.listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == Chart3DChangeListener.class) {
((Chart3DChangeListener) listeners[i + 1]).chartChanged(event);
}
}
}
/**
* Returns a flag that controls whether or not change events are sent to
* registered listeners.
*
* @return A boolean.
*
* @see #setNotify(boolean)
*/
public boolean isNotify() {
return this.notify;
}
/**
* Sets a flag that controls whether or not listeners receive
* {@link Chart3DChangeEvent} notifications.
*
* @param notify a boolean.
*
* @see #isNotify()
*/
public void setNotify(boolean notify) {
this.notify = notify;
// if the flag is being set to true, there may be queued up changes...
if (notify) {
this.world = null;
fireChangeEvent();
}
}
/**
* Sends a {@link Chart3DChangeEvent} to all registered listeners.
*/
protected void fireChangeEvent() {
notifyListeners(new Chart3DChangeEvent(this, this));
}
/**
* Provides serialization support.
*
* @param stream the input stream.
*
* @throws IOException if there is an I/O error.
* @throws ClassNotFoundException if there is a classpath problem.
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
// recreate an empty listener list
this.listenerList = new EventListenerList();
this.plot.addChangeListener(this);
// RenderingHints is not easily serialized, so we just put back the
// defaults...
this.renderingHints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
this.renderingHints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
}
/**
* Returns a string representing the {@code element}, primarily for
* debugging purposes.
*
* @param element the element ({@code null} not permitted).
*
* @return A string (never {@code null}).
*/
public static String renderedElementToString(RenderedElement element) {
Object type = element.getProperty(RenderedElement.TYPE);
if (InteractiveElementType.SECTION_LABEL.equals(type)) {
StringBuilder sb = new StringBuilder();
sb.append("Section label with key '");
Object key = element.getProperty("key");
sb.append(key.toString());
sb.append("'");
return sb.toString();
} else if (InteractiveElementType.LEGEND_ITEM.equals(type)) {
StringBuilder sb = new StringBuilder();
sb.append("Legend item with section key '");
Object key = element.getProperty(Chart3D.SERIES_KEY);
sb.append(key);
sb.append("'");
return sb.toString();
} else if (InteractiveElementType.AXIS_LABEL.equals(type)) {
return "Axis label with the label '" + element.getProperty("label") + "'";
} else if (InteractiveElementType.CATEGORY_AXIS_TICK_LABEL.equals(type)) {
return "Axis tick label with the label '" + element.getProperty("label") + "'";
} else if (InteractiveElementType.VALUE_AXIS_TICK_LABEL.equals(type)) {
return "Axis tick label with the value '" + element.getProperty("value") + "'";
} else if ("obj3d".equals(type)) {
StringBuilder sb = new StringBuilder();
sb.append("An object in the 3D model");
ItemKey itemKey = (ItemKey) element.getProperty(Object3D.ITEM_KEY);
if (itemKey != null) {
sb.append(" representing the data item [");
sb.append(itemKey);
sb.append("]");
}
return sb.toString();
} else {
return element.toString();
}
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/Chart3DChangeEvent.java 0000664 0000000 0000000 00000005523 14173312715 0026216 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d;
import java.util.EventObject;
import org.jfree.chart3d.internal.Args;
/**
* An event indicating some change in the attributes of a chart. Typically
* this indicates that the chart needs to be redrawn. Any object that
* implements the {@link Chart3DChangeListener} interface can register
* with a chart to receive change event notification.
*
* NOTE: This class is serializable, but the serialization format is subject
* to change in future releases and should not be relied upon for persisting
* instances of this class.
*/
@SuppressWarnings("serial")
public class Chart3DChangeEvent extends EventObject {
/** The chart that is the source of the event. */
private Chart3D chart;
/**
* Creates a new event.
*
* @param chart the chart that is the source of the event ({@code null}
* not permitted).
*/
public Chart3DChangeEvent(Chart3D chart) {
this(chart, chart);
}
/**
* Creates a new event.
*
* @param source the source.
* @param chart the chart that is the source of the event ({@code null}
* not permitted).
*/
public Chart3DChangeEvent(Object source, Chart3D chart) {
super(source);
Args.nullNotPermitted(chart, "chart");
this.chart = chart;
}
/**
* Returns the chart that this event is associated with.
*
* @return The chart (never {@code null}).
*/
public Chart3D getChart() {
return this.chart;
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/Chart3DChangeListener.java 0000664 0000000 0000000 00000003657 14173312715 0026730 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d;
import java.util.EventListener;
/**
* An interface for receiving notification of changes to a {@link Chart3D}
* instance.
*
* The {@link Chart3DPanel} class implements this interface so that it can
* receive notification of changes to the chart being displayed in the
* panel (whenever the chart changes, the panel is repainted).
*/
public interface Chart3DChangeListener extends EventListener {
/**
* Called to inform that a chart change event has occurred.
*
* @param event the event.
*/
void chartChanged(Chart3DChangeEvent event);
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/Chart3DFactory.java 0000664 0000000 0000000 00000046321 14173312715 0025437 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d;
import org.jfree.chart3d.axis.CategoryAxis3D;
import org.jfree.chart3d.axis.LabelOrientation;
import org.jfree.chart3d.axis.NumberAxis3D;
import org.jfree.chart3d.axis.StandardCategoryAxis3D;
import org.jfree.chart3d.axis.ValueAxis3D;
import org.jfree.chart3d.data.PieDataset3D;
import org.jfree.chart3d.data.Range;
import org.jfree.chart3d.data.category.CategoryDataset3D;
import org.jfree.chart3d.data.function.Function3D;
import org.jfree.chart3d.data.xyz.XYZDataset;
import org.jfree.chart3d.data.xyz.XYZSeriesCollection;
import org.jfree.chart3d.internal.Args;
import org.jfree.chart3d.legend.ColorScaleLegendBuilder;
import org.jfree.chart3d.plot.CategoryPlot3D;
import org.jfree.chart3d.plot.PiePlot3D;
import org.jfree.chart3d.plot.XYZPlot;
import org.jfree.chart3d.renderer.category.AreaRenderer3D;
import org.jfree.chart3d.renderer.category.BarRenderer3D;
import org.jfree.chart3d.renderer.category.CategoryRenderer3D;
import org.jfree.chart3d.renderer.category.LineRenderer3D;
import org.jfree.chart3d.renderer.category.StackedBarRenderer3D;
import org.jfree.chart3d.renderer.xyz.BarXYZRenderer;
import org.jfree.chart3d.renderer.xyz.LineXYZRenderer;
import org.jfree.chart3d.renderer.xyz.ScatterXYZRenderer;
import org.jfree.chart3d.renderer.xyz.SurfaceRenderer;
import org.jfree.chart3d.renderer.xyz.XYZRenderer;
import org.jfree.chart3d.style.ChartStyle;
import org.jfree.chart3d.style.StandardChartStyle;
/**
* Utility methods for constructing common chart types. Charts can be
* assembled piece-wise, but usually it is simpler to use the methods in this
* class then customise the resulting chart as necessary.
*/
public class Chart3DFactory {
/**
* Private constructor prevents instantiation which is unnecessary.
*/
private Chart3DFactory() {
// no need to instantiate this ever
}
/** The chart style that will be used when creating a new chart. */
static ChartStyle defaultStyle = new StandardChartStyle();
/**
* Returns a new instance of the default chart style (so that, by default,
* all charts will have an independent style instance).
*
* @return The default chart style (never {@code null}).
*
* @since 1.2
*/
public static ChartStyle getDefaultChartStyle() {
return defaultStyle.clone();
}
/**
* Sets the style that will be used when creating new charts.
*
* @param style the style ({@code null} not permitted).
*
* @since 1.2
*/
public static void setDefaultChartStyle(ChartStyle style) {
Args.nullNotPermitted(style, "style");
defaultStyle = style.clone();
}
/**
* Creates and returns a pie chart based on the supplied dataset. The
* chart returned by this method will be constructed using a
* {@link PiePlot3D} instance (so it is safe to cast the result of
* {@code chart.getPlot()}).
*
* For reference, here is a sample pie chart:
*
*
*
*
* @param title the chart title ({@code null} permitted).
* @param subtitle the chart subtitle ({@code null} permitted).
* @param dataset the dataset ({@code null} not permitted).
*
* @return A pie chart (never {@code null}).
*/
public static Chart3D createPieChart(String title, String subtitle,
PieDataset3D extends Comparable> dataset) {
PiePlot3D plot = new PiePlot3D(dataset);
return new Chart3D(title, subtitle, plot);
}
/**
* Creates and returns a bar chart based on the supplied dataset. The chart
* returned by this method will be constructed with a
* {@link CategoryPlot3D} using a {@link BarRenderer3D} (so it is
* safe to cast the plot and/or renderer to customise attributes that are
* specific to those subclasses).
*
* For reference, here is a sample bar chart:
*
*
*
*
* @param title the chart title ({@code null} permitted).
* @param subtitle the chart subtitle ({@code null} permitted).
* @param dataset the dataset ({@code null} not permitted).
* @param rowAxisLabel the row axis label ({@code null} permitted).
* @param columnAxisLabel the column axis label ({@code null}
* permitted).
* @param valueAxisLabel the value axis label ({@code null} permitted).
*
* @return A bar chart (never {@code null}).
*/
public static Chart3D createBarChart(String title, String subtitle,
CategoryDataset3D dataset, String rowAxisLabel,
String columnAxisLabel, String valueAxisLabel) {
StandardCategoryAxis3D rowAxis
= new StandardCategoryAxis3D(rowAxisLabel);
rowAxis.setTickLabelOrientation(LabelOrientation.PERPENDICULAR);
CategoryAxis3D columnAxis = new StandardCategoryAxis3D(columnAxisLabel);
NumberAxis3D valueAxis = new NumberAxis3D(valueAxisLabel,
new Range(0.0, 1.0));
valueAxis.setTickLabelOrientation(LabelOrientation.PERPENDICULAR);
CategoryRenderer3D renderer = new BarRenderer3D();
CategoryPlot3D plot = new CategoryPlot3D(dataset, renderer,
rowAxis, columnAxis, valueAxis);
return new Chart3D(title, subtitle, plot);
}
/**
* Creates and returns a stacked bar chart based on the supplied dataset.
* The chart returned by this method will be constructed with a
* {@link CategoryPlot3D} using a {@link StackedBarRenderer3D} (so it is
* safe to cast the plot and/or renderer to customise attributes that
* are specific to those subclasses).
*
* For reference, here is a sample stacked bar chart:
*
*
*
*
* @param title the chart title ({@code null} permitted).
* @param subtitle the chart subtitle ({@code null} permitted).
* @param dataset the dataset ({@code null} not permitted).
* @param rowAxisLabel the row axis label ({@code null} permitted).
* @param columnAxisLabel the column axis label ({@code null} permitted).
* @param valueAxisLabel the value axis label ({@code null} permitted).
*
* @return A stacked bar chart (never {@code null}).
*/
public static Chart3D createStackedBarChart(String title, String subtitle,
CategoryDataset3D dataset, String rowAxisLabel,
String columnAxisLabel, String valueAxisLabel) {
StandardCategoryAxis3D rowAxis
= new StandardCategoryAxis3D(rowAxisLabel);
rowAxis.setTickLabelOrientation(LabelOrientation.PERPENDICULAR);
CategoryAxis3D columnAxis = new StandardCategoryAxis3D(columnAxisLabel);
NumberAxis3D valueAxis = new NumberAxis3D(valueAxisLabel,
new Range(0.0, 1.0));
valueAxis.setTickLabelOrientation(LabelOrientation.PERPENDICULAR);
CategoryRenderer3D renderer = new StackedBarRenderer3D();
CategoryPlot3D plot = new CategoryPlot3D(dataset, renderer, rowAxis,
columnAxis, valueAxis);
return new Chart3D(title, subtitle, plot);
}
/**
* Creates and returns an area chart based on the supplied dataset. The
* chart returned by this method will be constructed with a
* {@link CategoryPlot3D} using an {@link AreaRenderer3D} (so it is safe
* to cast the plot and/or renderer to customise attributes that are
* specific to those subclasses).
*
* For reference, here is a sample area chart:
*
*
*
*
* @param title the chart title ({@code null} permitted).
* @param subtitle the chart subtitle ({@code null} permitted).
* @param dataset the dataset ({@code null} not permitted).
* @param rowAxisLabel the row axis label ({@code null} permitted).
* @param columnAxisLabel the column axis label ({@code null} permitted).
* @param valueAxisLabel the value axis label ({@code null} permitted).
*
* @return An area chart (never {@code null}).
*/
public static Chart3D createAreaChart(String title, String subtitle,
CategoryDataset3D dataset, String rowAxisLabel,
String columnAxisLabel, String valueAxisLabel) {
StandardCategoryAxis3D rowAxis
= new StandardCategoryAxis3D(rowAxisLabel);
rowAxis.setTickLabelOrientation(LabelOrientation.PERPENDICULAR);
StandardCategoryAxis3D columnAxis = new StandardCategoryAxis3D(
columnAxisLabel);
columnAxis.setFirstCategoryHalfWidth(true);
columnAxis.setLastCategoryHalfWidth(true);
NumberAxis3D valueAxis = new NumberAxis3D(valueAxisLabel,
new Range(0.0, 1.0));
valueAxis.setTickLabelOrientation(LabelOrientation.PERPENDICULAR);
CategoryRenderer3D renderer = new AreaRenderer3D();
CategoryPlot3D plot = new CategoryPlot3D(dataset, renderer, rowAxis,
columnAxis, valueAxis);
return new Chart3D(title, subtitle, plot);
}
/**
* Creates and returns a line chart based on the supplied dataset. The
* chart returned by this method will be constructed with a
* {@link CategoryPlot3D} using a {@link LineRenderer3D} (so it is safe
* to cast the plot and/or renderer to customise attributes that are
* specific to those subclasses).
*
* For reference, here is a sample line chart:
*
*
*
*
* @param title the chart title ({@code null} permitted).
* @param subtitle the chart subtitle ({@code null} permitted).
* @param dataset the dataset ({@code null} not permitted).
* @param rowAxisLabel the row axis label ({@code null} permitted).
* @param columnAxisLabel the column axis label ({@code null} permitted).
* @param valueAxisLabel the value axis label ({@code null} permitted).
*
* @return A line chart (never {@code null}).
*/
public static Chart3D createLineChart(String title, String subtitle,
CategoryDataset3D dataset, String rowAxisLabel,
String columnAxisLabel, String valueAxisLabel) {
StandardCategoryAxis3D rowAxis
= new StandardCategoryAxis3D(rowAxisLabel);
rowAxis.setTickLabelOrientation(LabelOrientation.PERPENDICULAR);
StandardCategoryAxis3D columnAxis
= new StandardCategoryAxis3D(columnAxisLabel);
columnAxis.setFirstCategoryHalfWidth(true);
columnAxis.setLastCategoryHalfWidth(true);
NumberAxis3D valueAxis = new NumberAxis3D(valueAxisLabel,
new Range(0.0, 1.0));
valueAxis.setTickLabelOrientation(LabelOrientation.PERPENDICULAR);
CategoryRenderer3D renderer = new LineRenderer3D();
CategoryPlot3D plot = new CategoryPlot3D(dataset, renderer, rowAxis,
columnAxis, valueAxis);
return new Chart3D(title, subtitle, plot);
}
/**
* Creates and returns a scatter plot based on the supplied dataset
* (containing one or more series of {@code (x, y, z)} values). The
* chart returned by this method will be constructed with an
* {@link XYZPlot} using a {@link ScatterXYZRenderer} (so it is safe
* to cast the plot and/or renderer to customise attributes that are
* specific to those subclasses).
*
* For reference, here is a sample scatter chart:
*
*
*
*
* @param title the chart title ({@code null} permitted).
* @param subtitle the chart subtitle ({@code null} permitted).
* @param dataset the dataset ({@code null} not permitted).
* @param xAxisLabel the x-axis label ({@code null} permitted).
* @param yAxisLabel the y-axis label ({@code null} permitted).
* @param zAxisLabel the z-axis label ({@code null} permitted).
*
* @return The chart.
*/
public static Chart3D createScatterChart(String title, String subtitle,
XYZDataset dataset, String xAxisLabel, String yAxisLabel,
String zAxisLabel) {
NumberAxis3D xAxis = new NumberAxis3D(xAxisLabel);
NumberAxis3D yAxis = new NumberAxis3D(yAxisLabel);
yAxis.setTickLabelOrientation(LabelOrientation.PERPENDICULAR);
NumberAxis3D zAxis = new NumberAxis3D(zAxisLabel);
XYZRenderer renderer = new ScatterXYZRenderer();
XYZPlot plot = new XYZPlot(dataset, renderer, xAxis, yAxis, zAxis);
return new Chart3D(title, subtitle, plot);
}
/**
* Creates a surface chart for the specified function.
*
* For reference, here is a sample surface chart:
*
*
*
*
* @param title the chart title ({@code null} permitted).
* @param subtitle the chart subtitle ({@code null} permitted).
* @param function the function ({@code null} not permitted).
* @param xAxisLabel the x-axis label ({@code null} permitted).
* @param yAxisLabel the y-axis label ({@code null} permitted).
* @param zAxisLabel the z-axis label ({@code null} permitted).
*
* @return The chart.
*
* @since 1.1
*/
public static Chart3D createSurfaceChart(String title, String subtitle,
Function3D function, String xAxisLabel, String yAxisLabel,
String zAxisLabel) {
NumberAxis3D xAxis = new NumberAxis3D(xAxisLabel);
NumberAxis3D yAxis = new NumberAxis3D(yAxisLabel);
yAxis.setTickLabelOrientation(LabelOrientation.PERPENDICULAR);
NumberAxis3D zAxis = new NumberAxis3D(zAxisLabel);
XYZRenderer renderer = new SurfaceRenderer(function);
// we pass an empty dataset because the plot must have a non-null
// dataset, but the renderer never looks at it...
XYZPlot plot = new XYZPlot(new XYZSeriesCollection(), renderer, xAxis,
yAxis, zAxis);
Chart3D chart = new Chart3D(title, subtitle, plot);
chart.setLegendBuilder(new ColorScaleLegendBuilder());
return chart;
}
/**
* Creates and returns a bar chart based on the supplied dataset (this is
* for special cases, most general cases will be covered by the
* {@link #createBarChart(String, String, CategoryDataset3D, String, String, String) }
* method). The chart returned by this method will be constructed with an
* {@link XYZPlot} using a {@link BarXYZRenderer} (so it is safe
* to cast the plot and/or renderer to customise attributes that are
* specific to those subclasses).
*
* For reference, here is a sample XYZ bar chart:
*
*
*
*
* @param title the chart title ({@code null} permitted).
* @param subtitle the chart subtitle ({@code null} permitted).
* @param dataset the dataset ({@code null} not permitted).
* @param xAxisLabel the x-axis label ({@code null} permitted).
* @param yAxisLabel the y-axis label ({@code null} permitted).
* @param zAxisLabel the z-axis label ({@code null} permitted).
*
* @return The chart.
*/
public static Chart3D createXYZBarChart(String title, String subtitle,
XYZDataset dataset, String xAxisLabel, String yAxisLabel,
String zAxisLabel) {
ValueAxis3D xAxis = new NumberAxis3D(xAxisLabel);
NumberAxis3D yAxis = new NumberAxis3D(yAxisLabel);
yAxis.setTickLabelOrientation(LabelOrientation.PERPENDICULAR);
ValueAxis3D zAxis = new NumberAxis3D(zAxisLabel);
XYZRenderer renderer = new BarXYZRenderer();
XYZPlot plot = new XYZPlot(dataset, renderer, xAxis, yAxis, zAxis);
return new Chart3D(title, subtitle, plot);
}
/**
* Creates and returns a line chart based on the supplied dataset. The
* chart returned by this method will be constructed with an
* {@link XYZPlot} using a {@link LineXYZRenderer} (so it is safe
* to cast the plot and/or renderer to customise attributes that are
* specific to those subclasses).
*
* @param title the chart title ({@code null} permitted).
* @param subtitle the chart subtitle ({@code null} permitted).
* @param dataset the dataset ({@code null} not permitted).
* @param xAxisLabel the x-axis label ({@code null} permitted).
* @param yAxisLabel the y-axis label ({@code null} permitted).
* @param zAxisLabel the z-axis label ({@code null} permitted).
*
* @return The chart.
*
* @since 1.5
*/
public static Chart3D createXYZLineChart(String title, String subtitle,
XYZDataset dataset, String xAxisLabel, String yAxisLabel,
String zAxisLabel) {
ValueAxis3D xAxis = new NumberAxis3D(xAxisLabel);
NumberAxis3D yAxis = new NumberAxis3D(yAxisLabel);
yAxis.setTickLabelOrientation(LabelOrientation.PERPENDICULAR);
ValueAxis3D zAxis = new NumberAxis3D(zAxisLabel);
XYZRenderer renderer = new LineXYZRenderer();
XYZPlot plot = new XYZPlot(dataset, renderer, xAxis, yAxis, zAxis);
return new Chart3D(title, subtitle, plot);
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/Chart3DHints.java 0000664 0000000 0000000 00000006720 14173312715 0025114 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d;
import java.util.Map;
/**
* Special rendering hints that used internally by Orson Charts to provide
* links between rendered items and the chart elements that they represent.
* Most {@code Graphics2D} implementations will ignore these hints but
* some, for example the {@code SVGGraphics2D} class in
* JFreeSVG, will use the hints
* to drive the output content.
*
* @since 1.3
*/
public final class Chart3DHints {
private Chart3DHints() {
// no need to instantiate this
}
/**
* The key for a hint to signal the beginning of an element. The value
* should be a string containing the element id or, alternatively, a Map
* containing the 'id' (String) and 'ref' (String in JSON format).
*/
public static final Key KEY_BEGIN_ELEMENT = new Chart3DHints.Key(0);
/**
* The key for a hint that ends an element.
*/
public static final Key KEY_END_ELEMENT = new Chart3DHints.Key(1);
/**
* A key for rendering hints that can be used with Orson Charts (in
* addition to the regular Java2D rendering hints).
*/
public static class Key extends java.awt.RenderingHints.Key {
/**
* Creates a new key.
*
* @param privateKey the private key.
*/
public Key(int privateKey) {
super(privateKey);
}
/**
* Returns {@code true} if {@code val} is a value that is
* compatible with this key, and {@code false} otherwise.
*
* @param val the value ({@code null} permitted).
*
* @return A boolean.
*/
@Override
public boolean isCompatibleValue(Object val) {
switch (intKey()) {
case 0:
return val == null || val instanceof String
|| val instanceof Map;
case 1:
return val == null || val instanceof Object;
default:
throw new RuntimeException("Not possible!");
}
}
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/Chart3DPanel.java 0000664 0000000 0000000 00000021357 14173312715 0025071 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.EventListener;
import javax.swing.event.EventListenerList;
import org.jfree.chart3d.data.ItemKey;
import org.jfree.chart3d.graphics3d.Object3D;
import org.jfree.chart3d.graphics3d.RenderedElement;
import org.jfree.chart3d.graphics3d.RenderingInfo;
import org.jfree.chart3d.graphics3d.swing.DisplayPanel3D;
import org.jfree.chart3d.graphics3d.swing.Panel3D;
import org.jfree.chart3d.interaction.Chart3DMouseEvent;
import org.jfree.chart3d.interaction.Chart3DMouseListener;
import org.jfree.chart3d.internal.Args;
/**
* A panel designed to display a {@link Chart3D} in a Swing-based desktop
* application. The panel registers with the chart to receive change
* notifications, and when these are received the chart is automatically
* repainted.
*
* This panel will display the chart, but does not include additional features
* such as the view toolbar and popup menu (these are provided by the
* {@link DisplayPanel3D} class).
*
* NOTE: This class is serializable, but the serialization format is subject
* to change in future releases and should not be relied upon for persisting
* instances of this class.
*/
@SuppressWarnings("serial")
public class Chart3DPanel extends Panel3D implements Chart3DChangeListener,
ComponentListener {
/**
* The chart being rendered.
*/
private final Chart3D chart;
/** Auto-fit the chart on resize? */
private final boolean autoFitOnPanelResize;
/** Storage for registered (chart) mouse listeners. */
private transient EventListenerList chartMouseListeners;
/**
* Creates a new chart panel to display the specified chart.
*
* @param chart the chart.
*/
public Chart3DPanel(Chart3D chart) {
super(chart);
this.chartMouseListeners = new EventListenerList();
this.chart = chart;
this.chart.addChangeListener(this);
addComponentListener(this);
this.autoFitOnPanelResize = false;
registerForTooltips();
}
/**
* Returns the chart being displayed in this panel.
*
* @return The chart (never {@code null}).
*
* @since 1.3
*/
public Chart3D getChart() {
return this.chart;
}
/**
* Receives notification when the chart has been modified, and responds
* by completely repainting the panel and chart.
*
* @param event the event.
*/
@Override
public void chartChanged(Chart3DChangeEvent event) {
repaint();
}
@Override
public void componentResized(ComponentEvent e) {
if (this.autoFitOnPanelResize) {
zoomToFit();
}
}
@Override
public void componentMoved(ComponentEvent e) {
// do nothing
}
@Override
public void componentShown(ComponentEvent e) {
// do nothing
}
@Override
public void componentHidden(ComponentEvent e) {
// do nothing
}
@Override
public String getToolTipText(MouseEvent e) {
RenderingInfo info = getRenderingInfo();
if (info == null) {
return null;
}
Object3D object = info.fetchObjectAt(e.getX(), e.getY());
if (object != null) {
ItemKey key = (ItemKey) object.getProperty(Object3D.ITEM_KEY);
if (key != null) {
return chart.getPlot().generateToolTipText(key);
}
}
return null;
}
/**
* Receives a mouse event and passes it on to registered
* {@link Chart3DMouseListener}s along with the underlying rendered
* element if any.
*
* @param e the mouse event.
*/
@Override
public void mouseClicked(MouseEvent e) {
Object[] listeners = this.chartMouseListeners.getListeners(
Chart3DMouseListener.class);
if (listeners.length == 0) {
return;
}
RenderedElement element = null;
RenderingInfo info = getRenderingInfo();
if (info != null) {
element = info.findElementAt(e.getX(), e.getY());
}
Chart3DMouseEvent chartEvent = new Chart3DMouseEvent(this.chart, e,
element);
for (int i = listeners.length - 1; i >= 0; i -= 1) {
((Chart3DMouseListener) listeners[i]).chartMouseClicked(chartEvent);
}
super.mouseClicked(e);
}
/**
* Receives a mouse event and passes it on to registered
* {@link Chart3DMouseListener}s along with the underlying rendered
* element if any.
*
* @param e the mouse event.
*/
@Override
public void mouseMoved(MouseEvent e) {
Object[] listeners = this.chartMouseListeners.getListeners(
Chart3DMouseListener.class);
if (listeners.length == 0) {
return;
}
RenderedElement element = null;
RenderingInfo info = getRenderingInfo();
if (info != null) {
element = info.findElementAt(e.getX(), e.getY());
}
Chart3DMouseEvent chartEvent = new Chart3DMouseEvent(this.chart, e,
element);
for (int i = listeners.length - 1; i >= 0; i -= 1) {
((Chart3DMouseListener) listeners[i]).chartMouseMoved(chartEvent);
}
super.mouseMoved(e);
}
/**
* Adds a listener to the list of objects listening for chart mouse events.
*
* @param listener the listener ({@code null} not permitted).
*
* @since 1.3
*/
public void addChartMouseListener(Chart3DMouseListener listener) {
Args.nullNotPermitted(listener, "listener");
this.chartMouseListeners.add(Chart3DMouseListener.class, listener);
}
/**
* Removes a listener from the list of objects listening for chart mouse
* events.
*
* @param listener the listener ({@code null} not permitted).
*
* @since 1.3
*/
public void removeChartMouseListener(Chart3DMouseListener listener) {
Args.nullNotPermitted(listener, "listener");
this.chartMouseListeners.remove(Chart3DMouseListener.class, listener);
}
/**
* Returns an array of the listeners of the given type registered with the
* panel.
*
* @param listenerType the listener type.
*
* @return An array of listeners.
*/
@Override
public T[] getListeners(Class listenerType) {
if (listenerType == Chart3DMouseListener.class) {
// fetch listeners from local storage
return this.chartMouseListeners.getListeners(listenerType);
}
else {
return super.getListeners(listenerType);
}
}
/**
* Provides serialization support.
*
* @param stream the input stream.
*
* @throws IOException if there is an I/O error.
* @throws ClassNotFoundException if there is a classpath problem.
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
// we create a new but empty chartMouseListeners list
this.chartMouseListeners = new EventListenerList();
// register as a listener with sub-components...
if (this.chart != null) {
this.chart.addChangeListener(this);
}
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/ChartElement.java 0000664 0000000 0000000 00000003342 14173312715 0025226 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d;
/**
* A chart element used to implement the Visitor pattern for applying changes
* to the chart. This is used by the chart styling feature.
*
* @since 1.2
*/
public interface ChartElement {
/**
* Receives a visitor to the element.
*
* @param visitor the visitor ({@code null} not permitted).
*/
void receive(ChartElementVisitor visitor);
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/ChartElementVisitor.java 0000664 0000000 0000000 00000003152 14173312715 0026605 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d;
/**
* A visitor for a {@link ChartElement}.
*/
public interface ChartElementVisitor {
/**
* Visit a chart element.
*
* @param element the chart element ({@code null} not permitted).
*/
void visit(ChartElement element);
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/Colors.java 0000664 0000000 0000000 00000055033 14173312715 0024120 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d;
import java.awt.Color;
/**
* A utility class that creates and returns color swatches that can be used
* in charts. The "i want hue" utility has been used to generate a number
* of these color sets.
* See
* http://tools.medialab.sciences-po.fr/iwanthue/.
*/
public class Colors {
private Colors() {
// no need to instantiate this class
}
/**
* Returns the default colors.
*
* @return The default colors.
*/
public static Color[] getDefaultColors() {
return createShadesColors();
}
/**
* Returns a palette of ten colors created using the "i want hue" utility,
* using the preset "Fancy (light background)" settings. A new array
* instance is created for each call to this method. A link to the
* "i want hue" utility is given in the class description.
*
* @return An array of ten colors (never {@code null}).
*
* @since 1.2
*/
public static Color[] createFancyLightColors() {
Color[] result = new Color[10];
result[0] = new Color(239, 164, 127);
result[1] = new Color(140, 228, 139);
result[2] = new Color(155, 208, 227);
result[3] = new Color(221, 228, 95);
result[4] = new Color(118, 223, 194);
result[5] = new Color(240, 166, 184);
result[6] = new Color(231, 185, 98);
result[7] = new Color(186, 214, 150);
result[8] = new Color(217, 184, 226);
result[9] = new Color(201, 212, 116);
return result;
}
/**
* Returns a palette of ten colors created using the "i want hue" utility,
* using the preset "Fancy (dark background)" settings. A new array
* instance is created for each call to this method. A link to the
* "i want hue" utility is given in the class description.
*
* @return An array of ten colors (never {@code null}).
*
* @since 1.2
*/
public static Color[] createFancyDarkColors() {
Color[] result = new Color[10];
result[0] = new Color(78, 81, 97);
result[1] = new Color(91, 104, 51);
result[2] = new Color(138, 75, 65);
result[3] = new Color(72, 62, 34);
result[4] = new Color(58, 100, 75);
result[5] = new Color(39, 63, 59);
result[6] = new Color(105, 68, 75);
result[7] = new Color(120, 90, 120);
result[8] = new Color(119, 90, 50);
result[9] = new Color(59, 103, 111);
return result;
}
/**
* Returns a palette of ten colors created using the "i want hue" utility,
* using the preset "Shades" settings. A new array instance is created for
* each call to this method. A link to the "i want hue" utility is given
* in the class description.
*
* @return An array of ten colors (never {@code null}).
*
* @since 1.2
*/
public static Color[] createShadesColors() {
Color[] result = new Color[10];
result[0] = new Color(137, 132, 104);
result[1] = new Color(217, 232, 208);
result[2] = new Color(53, 48, 40);
result[3] = new Color(240, 225, 172);
result[4] = new Color(196, 160, 128);
result[5] = new Color(92, 96, 87);
result[6] = new Color(136, 141, 136);
result[7] = new Color(106, 93, 66);
result[8] = new Color(205, 199, 168);
result[9] = new Color(158, 168, 143);
return result;
}
/**
* Returns a palette of ten colors created using the "i want hue" utility,
* using the preset "Tarnish" settings. A new array instance is created for
* each call to this method. A link to the "i want hue" utility is given
* in the class description.
*
* @return An array of ten colors (never {@code null}).
*
* @since 1.2
*/
public static Color[] createTarnishColors() {
Color[] result = new Color[10];
result[0] = new Color(148, 129, 121);
result[1] = new Color(179, 181, 136);
result[2] = new Color(204, 163, 140);
result[3] = new Color(102, 93, 80);
result[4] = new Color(164, 178, 159);
result[5] = new Color(156, 130, 100);
result[6] = new Color(129, 142, 124);
result[7] = new Color(186, 168, 159);
result[8] = new Color(144, 148, 108);
result[9] = new Color(189, 169, 131);
return result;
}
/**
* Returns a palette of ten colors created using the "i want hue" utility,
* using the preset "Pastel" settings. A new array instance is created for
* each call to this method. A link to the "i want hue" utility is given
* in the class description.
*
* @return An array of ten colors (never {@code null}).
*
* @since 1.2
*/
public static Color[] createPastelColors() {
Color[] result = new Color[10];
result[0] = new Color(232, 177, 165);
result[1] = new Color(207, 235, 142);
result[2] = new Color(142, 220, 220);
result[3] = new Color(228, 186, 115);
result[4] = new Color(187, 200, 230);
result[5] = new Color(157, 222, 177);
result[6] = new Color(234, 183, 210);
result[7] = new Color(213, 206, 169);
result[8] = new Color(202, 214, 205);
result[9] = new Color(195, 204, 133);
return result;
}
/**
* Returns a palette of ten colors created using the "i want hue" utility,
* using the preset "Pimp" settings. A new array instance is created for
* each call to this method. A link to the "i want hue" utility is given
* in the class description.
*
* @return An array of ten colors (never {@code null}).
*
* @since 1.2
*/
public static Color[] createPimpColors() {
Color[] result = new Color[10];
result[0] = new Color(197, 75, 103);
result[1] = new Color(85, 154, 48);
result[2] = new Color(122, 110, 206);
result[3] = new Color(190, 100, 50);
result[4] = new Color(201, 79, 209);
result[5] = new Color(95, 127, 170);
result[6] = new Color(147, 129, 39);
result[7] = new Color(63, 142, 96);
result[8] = new Color(186, 84, 150);
result[9] = new Color(219, 66, 52);
return result;
}
/**
* Returns a palette of ten colors created using the "i want hue" utility,
* using the preset "Intense" settings. A new array instance is created for
* each call to this method. A link to the "i want hue" utility is given
* in the class description.
*
* @return An array of ten colors (never {@code null}).
*
* @since 1.2
*/
public static Color[] createIntenseColors() {
Color[] result = new Color[10];
result[0] = new Color(107, 122, 160);
result[1] = new Color(99, 176, 67);
result[2] = new Color(214, 85, 52);
result[3] = new Color(202, 79, 200);
result[4] = new Color(184, 149, 57);
result[5] = new Color(82, 168, 146);
result[6] = new Color(194, 84, 128);
result[7] = new Color(77, 102, 50);
result[8] = new Color(132, 108, 197);
result[9] = new Color(144, 74, 61);
return result;
}
/**
* Returns a palette of ten colors created using the "i want hue" utility,
* using the preset "Fluo" settings. A new array instance is created for
* each call to this method. A link to the "i want hue" utility is given
* in the class description.
*
* @return An array of ten colors (never {@code null}).
*
* @since 1.2
*/
public static Color[] createFluoColors() {
Color[] result = new Color[10];
result[0] = new Color(108, 236, 137);
result[1] = new Color(253, 187, 46);
result[2] = new Color(56, 236, 216);
result[3] = new Color(171, 231, 51);
result[4] = new Color(221, 214, 74);
result[5] = new Color(106, 238, 70);
result[6] = new Color(172, 230, 100);
result[7] = new Color(242, 191, 82);
result[8] = new Color(221, 233, 56);
result[9] = new Color(242, 206, 47);
return result;
}
/**
* Returns a palette of ten colors created using the "i want hue" utility,
* using the preset "Red Roses" settings. A new array instance is created
* for each call to this method. A link to the "i want hue" utility is
* given in the class description.
*
* @return An array of ten colors (never {@code null}).
*
* @since 1.2
*/
public static Color[] createRedRosesColors() {
Color[] result = new Color[10];
result[0] = new Color(230, 129, 128);
result[1] = new Color(233, 56, 39);
result[2] = new Color(225, 45, 102);
result[3] = new Color(172, 79, 55);
result[4] = new Color(214, 154, 128);
result[5] = new Color(156, 96, 81);
result[6] = new Color(190, 77, 91);
result[7] = new Color(228, 121, 91);
result[8] = new Color(216, 63, 80);
result[9] = new Color(209, 75, 46);
return result;
}
/**
* Returns a palette of ten colors created using the "i want hue" utility,
* using the preset "Ochre Sand" settings. A new array instance is created
* for each call to this method. A link to the "i want hue" utility is
* given in the class description.
*
* @return An array of ten colors (never {@code null}).
*
* @since 1.2
*/
public static Color[] createOchreSandColors() {
Color[] result = new Color[10];
result[0] = new Color(218, 180, 125);
result[1] = new Color(245, 184, 36);
result[2] = new Color(159, 103, 28);
result[3] = new Color(124, 96, 55);
result[4] = new Color(224, 132, 56);
result[5] = new Color(185, 143, 48);
result[6] = new Color(229, 171, 97);
result[7] = new Color(232, 165, 54);
result[8] = new Color(171, 102, 53);
result[9] = new Color(160, 122, 71);
return result;
}
/**
* Returns a palette of ten colors created using the "i want hue" utility,
* using the preset "Yellow Lime" settings. A new array instance is created
* for each call to this method. A link to the "i want hue" utility is
* given in the class description.
*
* @return An array of ten colors (never {@code null}).
*
* @since 1.2
*/
public static Color[] createYellowLimeColors() {
Color[] result = new Color[10];
result[0] = new Color(235, 203, 59);
result[1] = new Color(113, 108, 56);
result[2] = new Color(222, 206, 134);
result[3] = new Color(169, 166, 62);
result[4] = new Color(214, 230, 54);
result[5] = new Color(225, 221, 105);
result[6] = new Color(128, 104, 23);
result[7] = new Color(162, 151, 86);
result[8] = new Color(117, 121, 25);
result[9] = new Color(183, 179, 40);
return result;
}
/**
* Returns a palette of ten colors created using the "i want hue" utility,
* using the preset "Green Mint" settings. A new array instance is created
* for each call to this method. A link to the "i want hue" utility is
* given in the class description.
*
* @return An array of ten colors (never {@code null}).
*
* @since 1.2
*/
public static Color[] createGreenMintColors() {
Color[] result = new Color[10];
result[0] = new Color(99, 224, 113);
result[1] = new Color(98, 132, 83);
result[2] = new Color(145, 234, 49);
result[3] = new Color(181, 215, 158);
result[4] = new Color(95, 171, 43);
result[5] = new Color(100, 208, 142);
result[6] = new Color(172, 222, 84);
result[7] = new Color(75, 139, 53);
result[8] = new Color(177, 216, 123);
result[9] = new Color(83, 223, 60);
return result;
}
/**
* Returns a palette of ten colors created using the "i want hue" utility,
* using the preset "Ice Cube" settings. A new array instance is created
* for each call to this method. A link to the "i want hue" utility is
* given in the class description.
*
* @return An array of ten colors (never {@code null}).
*
* @since 1.2
*/
public static Color[] createIceCubeColors() {
Color[] result = new Color[10];
result[0] = new Color(112, 235, 233);
result[1] = new Color(54, 110, 100);
result[2] = new Color(211, 232, 208);
result[3] = new Color(94, 230, 191);
result[4] = new Color(76, 154, 155);
result[5] = new Color(156, 181, 157);
result[6] = new Color(67, 152, 126);
result[7] = new Color(112, 135, 119);
result[8] = new Color(155, 213, 192);
result[9] = new Color(80, 195, 190);
return result;
}
/**
* Returns a palette of ten colors created using the "i want hue" utility,
* using the preset "Blue Ocean" settings. A new array instance is created
* for each call to this method. A link to the "i want hue" utility is
* given in the class description.
*
* @return An array of ten colors (never {@code null}).
*
* @since 1.2
*/
public static Color[] createBlueOceanColors() {
Color[] result = new Color[10];
result[0] = new Color(53, 84, 154);
result[1] = new Color(41, 46, 57);
result[2] = new Color(115, 124, 151);
result[3] = new Color(38, 52, 91);
result[4] = new Color(84, 117, 211);
result[5] = new Color(76, 125, 181);
result[6] = new Color(109, 108, 112);
result[7] = new Color(48, 105, 134);
result[8] = new Color(72, 82, 107);
result[9] = new Color(91, 99, 144);
return result;
}
/**
* Returns a palette of ten colors created using the "i want hue" utility,
* using the preset "Indigo Night" settings. A new array instance is
* created for each call to this method. A link to the "i want hue"
* utility is given in the class description.
*
* @return An array of ten colors (never {@code null}).
*
* @since 1.2
*/
public static Color[] createIndigoNightColors() {
Color[] result = new Color[10];
result[0] = new Color(127, 88, 147);
result[1] = new Color(201, 67, 217);
result[2] = new Color(112, 97, 218);
result[3] = new Color(219, 134, 222);
result[4] = new Color(154, 80, 172);
result[5] = new Color(170, 106, 231);
result[6] = new Color(142, 111, 210);
result[7] = new Color(194, 149, 235);
result[8] = new Color(152, 118, 188);
result[9] = new Color(214, 101, 237);
return result;
}
/**
* Returns a palette of ten colors created using the "i want hue" utility,
* using the preset "Purple Wine" settings. A new array instance is created
* for each call to this method. A link to the "i want hue" utility is
* given in the class description.
*
* @return An array of ten colors (never {@code null}).
*
* @since 1.2
*/
public static Color[] createPurpleWineColors() {
Color[] result = new Color[10];
result[0] = new Color(116, 28, 93);
result[1] = new Color(112, 79, 75);
result[2] = new Color(178, 37, 101);
result[3] = new Color(109, 24, 56);
result[4] = new Color(167, 42, 140);
result[5] = new Color(66, 30, 40);
result[6] = new Color(128, 70, 95);
result[7] = new Color(78, 20, 56);
result[8] = new Color(155, 62, 111);
result[9] = new Color(139, 61, 75);
return result;
}
/**
* Returns a palette of 7 colors with earth tones.
*
* @return An array of 7 colors (never {@code null}).
*/
public static Color[] getEarthColors() {
Color[] result = new Color[7];
result[0] = new Color(98, 98, 98);
result[1] = new Color(159, 87, 43);
result[2] = new Color(194, 176, 46);
result[3] = new Color(134, 155, 64);
result[4] = new Color(57, 118, 40);
result[5] = new Color(40, 114, 110);
result[6] = new Color(78, 79, 62);
return result;
}
/**
* Returns a newly created array containing 9 colors from the the
* ColorBrewer tool. This is a high-contrast set of colors, good for
* pie charts.
*
* http://colorbrewer2.org/?type=qualitative&scheme=Set1&n=9
*
* @return A color array.
*/
public static Color[] getBrewerQualitativeSet1N9Colors() {
Color[] result = new Color[9];
result[0] = new Color(228, 26, 28);
result[1] = new Color(55, 126, 184);
result[2] = new Color(77, 175, 74);
result[3] = new Color(152, 78, 163);
result[4] = new Color(255, 127, 0);
result[5] = new Color(255, 255, 51);
result[6] = new Color(166, 86, 40);
result[7] = new Color(247, 129, 191);
result[8] = new Color(153, 153, 153);
return result;
}
/**
* Returns a newly created array containing 12 colors from the the
* ColorBrewer tool.
*
* http://colorbrewer2.org/?type=qualitative&scheme=Paired&n=12
*
* @return A color array.
*/
public static Color[] getBrewerQualitativePairedN12Colors() {
Color[] result = new Color[12];
result[0] = new Color(166, 206, 227);
result[1] = new Color(31, 120, 180);
result[2] = new Color(178, 223, 138);
result[3] = new Color(51, 160, 44);
result[4] = new Color(251, 154, 153);
result[5] = new Color(227, 26, 28);
result[6] = new Color(253, 191, 111);
result[7] = new Color(255, 127, 0);
result[8] = new Color(202, 178, 214);
result[9] = new Color(106, 61, 154);
result[10] = new Color(255, 255, 153);
result[11] = new Color(177, 89, 40);
return result;
}
/**
* Returns a newly created array containing 11 colors from the the
* ColorBrewer tool. Good for pie charts and bar charts, not so good for
* scatter plots.
*
* http://colorbrewer2.org/?type=qualitative&scheme=Set3&n=12
*
* @return A color array.
*/
public static Color[] getBrewerQualitativeSet3N12Colors() {
Color[] result = new Color[12];
result[0] = new Color(141, 211, 199);
result[1] = new Color(255, 255, 179);
result[2] = new Color(190, 186, 218);
result[3] = new Color(251, 128, 114);
result[4] = new Color(128, 177, 211);
result[5] = new Color(253, 180, 98);
result[6] = new Color(179, 222, 105);
result[7] = new Color(252, 205, 229);
result[8] = new Color(217, 217, 217);
result[9] = new Color(188, 128, 189);
result[10] = new Color(204, 235, 197);
result[11] = new Color(255, 237, 111);
return result;
}
/**
* Returns a set of colors sourced from
* http://www.sapdesignguild.org/goodies/diagram_guidelines/index.html.
*
* @return A color array.
*/
public static Color[] getSAPMultiColor() {
return new Color[] {
new Color(255, 248, 163),
new Color(169, 204, 143),
new Color(178, 200, 217),
new Color(190, 163, 122),
new Color(243, 170, 121),
new Color(181, 181, 169),
new Color(230, 165, 164),
new Color(248, 215, 83),
new Color(92, 151, 70),
new Color(62, 117, 167),
new Color(122, 101, 62),
new Color(225, 102, 42),
new Color(116, 121, 111),
new Color(196, 56, 79)
};
}
/**
* Returns an array of four colors.
*
* @return An array of four colors.
*/
public static Color[] getColors1() {
return new Color[] { new Color(0, 55, 122),
new Color(24, 123, 58), Color.RED, Color.YELLOW };
}
/**
* Returns an array of four colors.
*
* @return An array of four colors.
*/
public static Color[] getColors2() {
return new Color[] {new Color(0x1A9641), new Color(0xA6D96A),
new Color(0xFDAE61), new Color(0xFFFFBF)};
}
/**
* Returns an array of six colors
* (source: http://blog.design-seeds.com/generating-color/).
*
* @return An array of six colors.
*/
public static Color[] getDesignSeedsShells() {
return new Color[] {
new Color(228, 233, 239),
new Color(184, 197, 219),
new Color(111, 122, 143),
new Color(95, 89, 89),
new Color(206, 167, 145),
new Color(188, 182, 173)
};
}
/**
* Returns an array of six colors
* (source: http://blog.design-seeds.com/generating-color/).
*
* @return An array of six colors.
*/
public static Color[] getDesignSeedsPepper() {
return new Color[] {
new Color(255, 219, 142),
new Color(220, 21, 20),
new Color(149, 0, 1),
new Color(82, 102, 41),
new Color(142, 101, 72),
new Color(199, 169, 128)
};
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/Orientation.java 0000664 0000000 0000000 00000003244 14173312715 0025147 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d;
/**
* General enumeration of orientations. One application for this is to
* specify whether the legend should be created for a vertical or horizontal
* layout.
*
* @since 1.1
*/
public enum Orientation {
/** Horizontal orientation. */
HORIZONTAL,
/** Vertical orientation. */
VERTICAL
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/Resources.java 0000664 0000000 0000000 00000005304 14173312715 0024625 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d;
import java.util.Locale;
import java.util.ResourceBundle;
import org.jfree.chart3d.internal.Args;
/**
* Provides centralised access to localised resources.
*
* @since 1.2
*/
public class Resources {
/** The locale for the resources. */
private static Locale locale = Locale.getDefault();
/** Localised resources. */
private static ResourceBundle resources = ResourceBundle.getBundle(
"org.jfree.chart3d.Resources", Locale.getDefault());
/**
* Returns the locale that is being used for supplying resources. The
* default value is {@code Locale.getDefault()}.
*
* @return The locale (never {@code null}).
*/
public static Locale getLocale() {
return locale;
}
/**
* Sets the locale to use for supplying resources.
*
* @param l the locale ({@code null} not permitted).
*/
public static void setLocale(Locale l) {
Args.nullNotPermitted(l, "l");
locale = l;
resources = ResourceBundle.getBundle("org.jfree.chart3d.Resources",
locale);
}
/**
* Returns a localised string.
*
* @param key the key ({@code null} not permitted).
*
* @return A localised string.
*/
public static String localString(String key) {
return resources.getString(key);
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/TitleAnchor.java 0000664 0000000 0000000 00000006516 14173312715 0025075 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d;
import org.jfree.chart3d.graphics2d.Anchor2D;
import org.jfree.chart3d.graphics2d.RefPt2D;
/**
* Predefined title anchor points, provided for convenience. These anchor
* points are instances of the {@link Anchor2D} class, initialised with an
* offset of 4 units from each corner of the target rectangle.
*/
public final class TitleAnchor {
/**
* An anchor point at the top-left of the chart area.
*/
public static final Anchor2D TOP_LEFT = new Anchor2D(RefPt2D.TOP_LEFT);
/**
* An anchor point at the top-right of the chart area.
*/
public static final Anchor2D TOP_RIGHT = new Anchor2D(RefPt2D.TOP_RIGHT);
/**
* An anchor point at the top-center of the chart area.
*/
public static final Anchor2D TOP_CENTER = new Anchor2D(RefPt2D.TOP_CENTER);
/**
* An anchor point at the center-left of the chart area.
*/
public static final Anchor2D CENTER_LEFT
= new Anchor2D(RefPt2D.CENTER_LEFT);
/**
* An anchor point at the center of the chart area (provided for
* completeness, you wouldn't normally anchor a chart title at the
* center).
*/
public static final Anchor2D CENTER = new Anchor2D(RefPt2D.CENTER);
/**
* An anchor point at the center-right of the chart area.
*/
public static final Anchor2D CENTER_RIGHT
= new Anchor2D(RefPt2D.CENTER_RIGHT);
/**
* An anchor point at the bottom-center of the chart area.
*/
public static final Anchor2D BOTTOM_CENTER
= new Anchor2D(RefPt2D.BOTTOM_CENTER);
/**
* An anchor point at the bottom-left of the chart area.
*/
public static final Anchor2D BOTTOM_LEFT
= new Anchor2D(RefPt2D.BOTTOM_LEFT);
/**
* An anchor point at the bottom-right of the chart area.
*/
public static final Anchor2D BOTTOM_RIGHT
= new Anchor2D(RefPt2D.BOTTOM_RIGHT);
private TitleAnchor() {
// no need to instantiate this
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/TitleUtils.java 0000664 0000000 0000000 00000013263 14173312715 0024760 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d;
import java.awt.Color;
import java.awt.Font;
import org.jfree.chart3d.graphics2d.Anchor2D;
import org.jfree.chart3d.interaction.InteractiveElementType;
import org.jfree.chart3d.table.GridElement;
import org.jfree.chart3d.table.HAlign;
import org.jfree.chart3d.table.TableElement;
import org.jfree.chart3d.table.TextElement;
/**
* Some utility methods for creating chart titles.
*/
public class TitleUtils {
/** The default title font. */
public static final Font DEFAULT_TITLE_FONT = new Font("Dialog", Font.BOLD,
20);
/** The default foreground color for titles. */
public static final Color DEFAULT_TITLE_COLOR = Color.BLACK;
/** The default sub-title font. */
public static final Font DEFAULT_SUBTITLE_FONT = new Font("Dialog",
Font.PLAIN, 12);
private TitleUtils() {
// no need to instantiate this class
}
/**
* Creates a chart title using the default font and alignment.
*
* @param title the chart title ({@code null} not permitted).
*
* @return The chart title.
*/
public static TableElement createTitle(String title) {
return createTitle(title, null);
}
/**
* Creates a chart title and subtitle using default fonts and left
* alignment. The {@code subtitle} is optional.
*
* @param title the title text ({@code null} not permitted).
* @param subtitle the subtitle text ({@code null} permitted).
*
* @return A composite title.
*/
public static TableElement createTitle(String title, String subtitle) {
return createTitle(title, subtitle, TitleAnchor.TOP_LEFT);
}
/**
* Creates a chart title and subtitle (optional) using default fonts and
* alignment that is standard for the specified anchor point (that is, left
* alignment when the title is anchored left, center alignment when the
* title is anchored centrally, and right alignment when the title is
* anchored to the right).
*
* @param title the title text ({@code null} not permitted).
* @param subtitle the subtitle text ({@code null} permitted).
* @param anchor the anchor point ({@code null} not permitted).
*
* @return A composite title.
*/
public static TableElement createTitle(String title, String subtitle,
Anchor2D anchor) {
HAlign alignment = HAlign.LEFT;
if (anchor.getRefPt().isHorizontalCenter()) {
alignment = HAlign.CENTER;
} else if (anchor.getRefPt().isRight()) {
alignment = HAlign.RIGHT;
}
return createTitle(title, DEFAULT_TITLE_FONT, subtitle,
DEFAULT_SUBTITLE_FONT, alignment);
}
/**
* Creates a chart title and subtitle using the specified fonts and
* alignment.
*
* @param title the title text ({@code null} not permitted).
* @param titleFont the title font ({@code null} not permitted).
* @param subtitle the subtitle text ({@code null} permitted).
* @param subtitleFont the subtitle font ({@code null} permitted).
* @param alignment the horizontal alignment ({@code null} not
* permitted).
*
* @return A chart title (never {@code null}).
*/
public static TableElement createTitle(String title, Font titleFont,
String subtitle, Font subtitleFont, HAlign alignment) {
TextElement t = new TextElement(title, titleFont);
t.setHorizontalAligment(alignment);
t.setColor(DEFAULT_TITLE_COLOR);
t.setTag("CHART_TITLE");
t.setProperty(TableElement.CLASS, InteractiveElementType.TITLE);
if (subtitle == null) {
return t;
}
if (subtitleFont == null) {
throw new IllegalArgumentException(
"A subtitleFont is required when there is a subtitle.");
}
GridElement compositeTitle = new GridElement<>();
TextElement st = new TextElement(subtitle, subtitleFont);
st.setHorizontalAligment(alignment);
st.setColor(DEFAULT_TITLE_COLOR);
st.setTag("CHART_SUBTITLE");
st.setProperty(TableElement.CLASS, InteractiveElementType.SUBTITLE);
compositeTitle.setElement(t, "R1", "C1");
compositeTitle.setElement(st, "R2", "C1");
return compositeTitle;
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/axis/ 0000775 0000000 0000000 00000000000 14173312715 0022752 5 ustar 00root root 0000000 0000000 orson-charts-2.1.0/src/main/java/org/jfree/chart3d/axis/AbstractAxis3D.java 0000664 0000000 0000000 00000053772 14173312715 0026412 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.axis;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.event.EventListenerList;
import org.jfree.chart3d.internal.Args;
import org.jfree.chart3d.internal.ObjectUtils;
import org.jfree.chart3d.internal.SerialUtils;
import org.jfree.chart3d.internal.TextUtils;
import org.jfree.chart3d.Chart3DHints;
import org.jfree.chart3d.ChartElementVisitor;
import org.jfree.chart3d.graphics2d.TextAnchor;
import org.jfree.chart3d.graphics3d.RenderedElement;
import org.jfree.chart3d.graphics3d.RenderingInfo;
import org.jfree.chart3d.graphics3d.internal.Utils2D;
import org.jfree.chart3d.interaction.InteractiveElementType;
import org.jfree.chart3d.marker.MarkerChangeEvent;
import org.jfree.chart3d.marker.MarkerChangeListener;
import org.jfree.chart3d.plot.CategoryPlot3D;
/**
* A base class that can be used to create an {@link Axis3D} implementation.
* This class implements the core axis attributes as well as the change
* listener mechanism required to enable automatic repainting of charts.
*
* NOTE: This class is serializable, but the serialization format is subject
* to change in future releases and should not be relied upon for persisting
* instances of this class.
*/
public abstract class AbstractAxis3D implements Axis3D, MarkerChangeListener,
Serializable {
/**
* The default axis label font (in most circumstances this will be
* overridden by the chart style).
*
* @since 1.2
*/
public static final Font DEFAULT_LABEL_FONT = new Font("Dialog", Font.BOLD,
12);
/**
* The default axis label color (in most circumstances this will be
* overridden by the chart style).
*
* @since 1.2
*/
public static final Color DEFAULT_LABEL_COLOR = Color.BLACK;
/**
* The default label offset.
*
* @since 1.2
*/
public static final double DEFAULT_LABEL_OFFSET = 10;
/**
* The default tick label font (in most circumstances this will be
* overridden by the chart style).
*
* @since 1.2
*/
public static final Font DEFAULT_TICK_LABEL_FONT = new Font("Dialog",
Font.PLAIN, 12);
/**
* The default tick label color (in most circumstances this will be
* overridden by the chart style).
*
* @since 1.2
*/
public static final Color DEFAULT_TICK_LABEL_COLOR = Color.BLACK;
/**
* The default stroke for the axis line.
*
* @since 1.2
*/
public static final Stroke DEFAULT_LINE_STROKE = new BasicStroke(0f);
/**
* The default color for the axis line.
*
* @since 1.2
*/
public static final Color DEFAULT_LINE_COLOR = Color.GRAY;
/** A flag that determines whether or not the axis will be drawn. */
private boolean visible;
/** The axis label (if {@code null}, no label is displayed). */
private String label;
/** The label font (never {@code null}). */
private Font labelFont;
/** The color used to draw the axis label (never {@code null}). */
private Color labelColor;
/** The offset between the tick labels and the label. */
private double labelOffset;
/** The stroke used to draw the axis line. */
private transient Stroke lineStroke;
/** The color used to draw the axis line. */
private Color lineColor;
/** Draw the tick labels? */
private boolean tickLabelsVisible;
/** The font used to display tick labels (never {@code null}) */
private Font tickLabelFont;
/** The tick label paint (never {@code null}). */
private Color tickLabelColor;
/** Storage for registered change listeners. */
private final transient EventListenerList listenerList;
/**
* Creates a new label with the specified label. If the supplied label
* is {@code null}, the axis will be shown without a label.
*
* @param label the axis label ({@code null} permitted).
*/
public AbstractAxis3D(String label) {
this.visible = true;
this.label = label;
this.labelFont = DEFAULT_LABEL_FONT;
this.labelColor = DEFAULT_LABEL_COLOR;
this.labelOffset = DEFAULT_LABEL_OFFSET;
this.lineStroke = DEFAULT_LINE_STROKE;
this.lineColor = DEFAULT_LINE_COLOR;
this.tickLabelsVisible = true;
this.tickLabelFont = DEFAULT_TICK_LABEL_FONT;
this.tickLabelColor = DEFAULT_TICK_LABEL_COLOR;
this.listenerList = new EventListenerList();
}
/**
* Returns the flag that determines whether or not the axis is drawn
* on the chart.
*
* @return A boolean.
*
* @see #setVisible(boolean)
*/
@Override
public boolean isVisible() {
return this.visible;
}
/**
* Sets the flag that determines whether or not the axis is drawn on the
* chart and sends an {@link Axis3DChangeEvent} to all registered listeners.
*
* @param visible the flag.
*
* @see #isVisible()
*/
@Override
public void setVisible(boolean visible) {
this.visible = visible;
fireChangeEvent(false);
}
/**
* Returns the axis label - the text that describes what the axis measures.
* The description should usually specify the units. When this attribute
* is {@code null}, the axis is drawn without a label.
*
* @return The axis label (possibly {@code null}).
*/
public String getLabel() {
return this.label;
}
/**
* Sets the axis label and sends an {@link Axis3DChangeEvent} to all
* registered listeners. If the supplied label is {@code null},
* the axis will be drawn without a label.
*
* @param label the label ({@code null} permitted).
*/
public void setLabel(String label) {
this.label = label;
fireChangeEvent(false);
}
/**
* Returns the font used to display the main axis label. The default value
* is {@code Font("SansSerif", Font.BOLD, 12)}.
*
* @return The font used to display the axis label (never {@code null}).
*/
@Override
public Font getLabelFont() {
return this.labelFont;
}
/**
* Sets the font used to display the main axis label and sends an
* {@link Axis3DChangeEvent} to all registered listeners.
*
* @param font the new font ({@code null} not permitted).
*/
@Override
public void setLabelFont(Font font) {
Args.nullNotPermitted(font, "font");
this.labelFont = font;
fireChangeEvent(false);
}
/**
* Returns the color used for the label. The default value is
* {@code Color.BLACK}.
*
* @return The label paint (never {@code null}).
*/
@Override
public Color getLabelColor() {
return this.labelColor;
}
/**
* Sets the color used to draw the axis label and sends an
* {@link Axis3DChangeEvent} to all registered listeners.
*
* @param color the color ({@code null} not permitted).
*/
@Override
public void setLabelColor(Color color) {
Args.nullNotPermitted(color, "color");
this.labelColor = color;
fireChangeEvent(false);
}
/**
* Returns the offset between the tick labels and the axis label, measured
* in Java2D units. The default value is {@link #DEFAULT_LABEL_OFFSET}.
*
* @return The offset.
*
* @since 1.2
*/
public double getLabelOffset() {
return this.labelOffset;
}
/**
* Sets the offset between the tick labels and the axis label and sends
* an {@link Axis3DChangeEvent} to all registered listeners.
*
* @param offset the offset.
*
* @since 1.2
*/
public void setLabelOffset(double offset) {
this.labelOffset = offset;
fireChangeEvent(false);
}
/**
* Returns the stroke used to draw the axis line. The default value is
* {@link #DEFAULT_LINE_STROKE}.
*
* @return The stroke used to draw the axis line (never {@code null}).
*/
public Stroke getLineStroke() {
return this.lineStroke;
}
/**
* Sets the stroke used to draw the axis line and sends an
* {@link Axis3DChangeEvent} to all registered listeners.
*
* @param stroke the new stroke ({@code null} not permitted).
*/
public void setLineStroke(Stroke stroke) {
Args.nullNotPermitted(stroke, "stroke");
this.lineStroke = stroke;
fireChangeEvent(false);
}
/**
* Returns the color used to draw the axis line. The default value is
* {@link #DEFAULT_LINE_COLOR}.
*
* @return The color used to draw the axis line (never {@code null}).
*/
public Color getLineColor() {
return this.lineColor;
}
/**
* Sets the color used to draw the axis line and sends an
* {@link Axis3DChangeEvent} to all registered listeners.
*
* @param color the new color ({@code null} not permitted).
*/
public void setLineColor(Color color) {
Args.nullNotPermitted(color, "color");
this.lineColor = color;
fireChangeEvent(false);
}
/**
* Returns the flag that controls whether or not the tick labels are
* drawn. The default value is {@code true}.
*
* @return A boolean.
*/
public boolean getTickLabelsVisible() {
return this.tickLabelsVisible;
}
/**
* Sets the flag that controls whether or not the tick labels are drawn,
* and sends a change event to all registered listeners. You should think
* carefully before setting this flag to {@code false}, because if
* the tick labels are not shown it will be hard for the reader to
* understand the resulting chart.
*
* @param visible visible?
*/
public void setTickLabelsVisible(boolean visible) {
this.tickLabelsVisible = visible;
fireChangeEvent(false);
}
/**
* Returns the font used to display the tick labels. The default value
* is {@link #DEFAULT_TICK_LABEL_FONT}.
*
* @return The font (never {@code null}).
*/
@Override
public Font getTickLabelFont() {
return this.tickLabelFont;
}
/**
* Sets the font used to display tick labels and sends an
* {@link Axis3DChangeEvent} to all registered listeners.
*
* @param font the font ({@code null} not permitted).
*/
@Override
public void setTickLabelFont(Font font) {
Args.nullNotPermitted(font, "font");
this.tickLabelFont = font;
fireChangeEvent(false);
}
/**
* Returns the foreground color for the tick labels. The default value
* is {@link #DEFAULT_LABEL_COLOR}.
*
* @return The foreground color (never {@code null}).
*/
@Override
public Color getTickLabelColor() {
return this.tickLabelColor;
}
/**
* Sets the foreground color for the tick labels and sends an
* {@link Axis3DChangeEvent} to all registered listeners.
*
* @param color the color ({@code null} not permitted).
*/
@Override
public void setTickLabelColor(Color color) {
Args.nullNotPermitted(color, "color");
this.tickLabelColor = color;
fireChangeEvent(false);
}
/**
* Receives a {@link ChartElementVisitor}. This method is part of a general
* mechanism for traversing the chart structure and performing operations
* on each element in the chart. You will not normally call this method
* directly.
*
* @param visitor the visitor ({@code null} not permitted).
*
* @since 1.2
*/
@Override
public abstract void receive(ChartElementVisitor visitor);
/**
* Draws the specified text as the axis label and returns a bounding
* shape (2D) for the text.
*
* @param label the label ({@code null} not permitted).
* @param g2 the graphics target ({@code null} not permitted).
* @param axisLine the axis line ({@code null} not permitted).
* @param opposingPt an opposing point ({@code null} not permitted).
* @param offset the offset.
* @param info collects rendering info ({@code null} permitted).
* @param hinting perform element hinting?
*
* @return A bounding shape.
*/
protected Shape drawAxisLabel(String label, Graphics2D g2,
Line2D axisLine, Point2D opposingPt, double offset,
RenderingInfo info, boolean hinting) {
Args.nullNotPermitted(label, "label");
Args.nullNotPermitted(g2, "g2");
Args.nullNotPermitted(axisLine, "axisLine");
Args.nullNotPermitted(opposingPt, "opposingPt");
g2.setFont(getLabelFont());
g2.setPaint(getLabelColor());
Line2D labelPosLine = Utils2D.createPerpendicularLine(axisLine, 0.5,
offset, opposingPt);
double theta = Utils2D.calculateTheta(axisLine);
if (theta < -Math.PI / 2.0) {
theta = theta + Math.PI;
}
if (theta > Math.PI / 2.0) {
theta = theta - Math.PI;
}
if (hinting) {
Map m = new HashMap<>();
m.put("ref", "{\"type\": \"axisLabel\", \"axis\": \"" + axisStr()
+ "\", \"label\": \"" + getLabel() + "\"}");
g2.setRenderingHint(Chart3DHints.KEY_BEGIN_ELEMENT, m);
}
Shape bounds = TextUtils.drawRotatedString(getLabel(), g2,
(float) labelPosLine.getX2(), (float) labelPosLine.getY2(),
TextAnchor.CENTER, theta, TextAnchor.CENTER);
if (hinting) {
g2.setRenderingHint(Chart3DHints.KEY_END_ELEMENT, true);
}
if (info != null) {
RenderedElement labelElement = new RenderedElement(
InteractiveElementType.AXIS_LABEL, bounds);
labelElement.setProperty("axis", axisStr());
labelElement.setProperty("label", getLabel());
info.addOffsetElement(labelElement);
}
return bounds;
}
/**
* Returns a string representing the configured type of the axis ("row",
* "column", "value", "x", "y" or "z" - other values may be possible in the
* future). A row axis on a {@link CategoryPlot3D} is in the
* position of a z-axis (depth), a column axis is in the position
* of an x-axis (width), a value axis is in the position of a
* y-axis (height).
*
* @return A string (never {@code null}).
*
* @since 1.3
*/
protected abstract String axisStr();
/**
* Draws the axis along an arbitrary line (between {@code startPt}
* and {@code endPt}). The opposing point is used as a reference
* point to know on which side of the axis to draw the labels.
*
* @param g2 the graphics target ({@code null} not permitted).
* @param startPt the starting point ({@code null} not permitted).
* @param endPt the end point ({@code null} not permitted)
* @param opposingPt an opposing point ({@code null} not permitted).
* @param tickData info about the ticks to draw ({@code null} not
* permitted).
* @param info an object to be populated with rendering info
* ({@code null} permitted).
* @param hinting a flag that controls whether or not element hinting
* should be performed.
*/
@Override
public abstract void draw(Graphics2D g2, Point2D startPt, Point2D endPt,
Point2D opposingPt, List tickData, RenderingInfo info,
boolean hinting);
/**
* Tests this instance for equality with an arbitrary object.
*
* @param obj the object to test against ({@code null} permitted).
*
* @return A boolean.
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof AbstractAxis3D)) {
return false;
}
AbstractAxis3D that = (AbstractAxis3D) obj;
if (this.visible != that.visible) {
return false;
}
if (!ObjectUtils.equals(this.label, that.label)) {
return false;
}
if (!this.labelFont.equals(that.labelFont)) {
return false;
}
if (!this.labelColor.equals(that.labelColor)) {
return false;
}
if (!this.lineStroke.equals(that.lineStroke)) {
return false;
}
if (!this.lineColor.equals(that.lineColor)) {
return false;
}
if (this.tickLabelsVisible != that.tickLabelsVisible) {
return false;
}
if (!this.tickLabelFont.equals(that.tickLabelFont)) {
return false;
}
if (!this.tickLabelColor.equals(that.tickLabelColor)) {
return false;
}
return true;
}
/**
* Returns a hash code for this instance.
*
* @return A hash code.
*/
@Override
public int hashCode() {
int hash = 5;
hash = 83 * hash + (this.visible ? 1 : 0);
hash = 83 * hash + ObjectUtils.hashCode(this.label);
hash = 83 * hash + ObjectUtils.hashCode(this.labelFont);
hash = 83 * hash + ObjectUtils.hashCode(this.labelColor);
hash = 83 * hash + ObjectUtils.hashCode(this.lineStroke);
hash = 83 * hash + ObjectUtils.hashCode(this.lineColor);
hash = 83 * hash + (this.tickLabelsVisible ? 1 : 0);
hash = 83 * hash + ObjectUtils.hashCode(this.tickLabelFont);
hash = 83 * hash + ObjectUtils.hashCode(this.tickLabelColor);
return hash;
}
/**
* Registers a listener so that it will receive axis change events.
*
* @param listener the listener ({@code null} not permitted).
*/
@Override
public void addChangeListener(Axis3DChangeListener listener) {
this.listenerList.add(Axis3DChangeListener.class, listener);
}
/**
* Unregisters a listener so that it will no longer receive axis
* change events.
*
* @param listener the listener ({@code null} not permitted).
*/
@Override
public void removeChangeListener(Axis3DChangeListener listener) {
this.listenerList.remove(Axis3DChangeListener.class, listener);
}
/**
* Notifies all registered listeners that the axis has been modified.
*
* @param event information about the change event.
*/
public void notifyListeners(Axis3DChangeEvent event) {
Object[] listeners = this.listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == Axis3DChangeListener.class) {
((Axis3DChangeListener) listeners[i + 1]).axisChanged(event);
}
}
}
/**
* Sends an {@link Axis3DChangeEvent} to all registered listeners.
*
* @param requiresWorldUpdate a flag indicating whether this change
* requires the 3D world to be updated.
*/
protected void fireChangeEvent(boolean requiresWorldUpdate) {
notifyListeners(new Axis3DChangeEvent(this, requiresWorldUpdate));
}
/**
* Receives notification of a change to a marker managed by this axis - the
* response is to fire a change event for the axis (to eventually trigger
* a repaint of the chart). Marker changes don't require the world model
* to be updated.
*
* @param event the event.
*
* @since 1.2
*/
@Override
public void markerChanged(MarkerChangeEvent event) {
fireChangeEvent(false);
}
/**
* Provides serialization support.
*
* @param stream the output stream.
*
* @throws IOException if there is an I/O error.
*/
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
SerialUtils.writeStroke(this.lineStroke, stream);
}
/**
* Provides serialization support.
*
* @param stream the input stream.
*
* @throws IOException if there is an I/O error.
* @throws ClassNotFoundException if there is a classpath problem.
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
this.lineStroke = SerialUtils.readStroke(stream);
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/axis/AbstractValueAxis3D.java 0000664 0000000 0000000 00000064270 14173312715 0027402 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.axis;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Paint;
import java.awt.Stroke;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.jfree.chart3d.internal.Args;
import org.jfree.chart3d.internal.ObjectUtils;
import org.jfree.chart3d.internal.SerialUtils;
import org.jfree.chart3d.ChartElementVisitor;
import org.jfree.chart3d.data.Range;
import org.jfree.chart3d.data.category.CategoryDataset3D;
import org.jfree.chart3d.marker.MarkerData;
import org.jfree.chart3d.marker.NumberMarker;
import org.jfree.chart3d.marker.RangeMarker;
import org.jfree.chart3d.marker.ValueMarker;
import org.jfree.chart3d.plot.CategoryPlot3D;
import org.jfree.chart3d.plot.XYZPlot;
/**
* A base class for implementing numerical axes.
*
* NOTE: This class is serializable, but the serialization format is subject
* to change in future releases and should not be relied upon for persisting
* instances of this class.
*/
@SuppressWarnings("serial")
public abstract class AbstractValueAxis3D extends AbstractAxis3D
implements ValueAxis3D, Serializable{
/** The type of use for which the axis has been configured. */
private ValueAxis3DType configuredType;
/** The axis range. */
protected Range range;
private boolean inverted;
/**
* A flag that controls whether or not the axis range is automatically
* adjusted to display all of the data items in the dataset.
*/
private boolean autoAdjustRange;
/** The percentage margin to leave at the lower end of the axis. */
private double lowerMargin;
/** The percentage margin to leave at the upper end of the axis. */
private double upperMargin;
/**
* The default range to apply when there is no data in the dataset and the
* autoAdjustRange flag is true. A sensible default is going to depend on
* the context, so the user should change it as necessary.
*/
private Range defaultAutoRange;
/**
* The minimum length for the axis range when auto-calculated. This will
* be applied, for example, when the dataset contains just a single value.
*/
private double minAutoRangeLength;
/** The tick label offset (number of Java2D units). */
private double tickLabelOffset;
/** The length of tick marks (in Java2D units). Can be set to 0.0. */
private double tickMarkLength;
/** The tick mark stroke (never {@code null}). */
private transient Stroke tickMarkStroke;
/** The tick mark paint (never {@code null}). */
private transient Paint tickMarkPaint;
/** The orientation for the tick labels. */
private LabelOrientation tickLabelOrientation;
/** The tick label factor (defaults to 1.4). */
private double tickLabelFactor;
/** Storage for value markers for the axis (empty by default). */
private final Map valueMarkers;
/**
* Creates a new axis instance.
*
* @param label the axis label ({@code null} permitted).
* @param range the axis range ({@code null} not permitted).
*/
public AbstractValueAxis3D(String label, Range range) {
super(label);
Args.nullNotPermitted(range, "range");
this.configuredType = null;
this.range = range;
this.autoAdjustRange = true;
this.lowerMargin = 0.05;
this.upperMargin = 0.05;
this.defaultAutoRange = new Range(0.0, 1.0);
this.minAutoRangeLength = 0.001;
this.tickLabelOffset = 5.0;
this.tickLabelOrientation = LabelOrientation.PARALLEL;
this.tickLabelFactor = 1.4;
this.tickMarkLength = 3.0;
this.tickMarkStroke = new BasicStroke(0.5f);
this.tickMarkPaint = Color.GRAY;
this.valueMarkers = new LinkedHashMap<>();
}
/**
* Returns the configured type for the axis.
*
* @return The configured type ({@code null} if the axis has not yet
* been assigned to a plot).
*
* @since 1.3
*/
@Override
public ValueAxis3DType getConfiguredType() {
return this.configuredType;
}
/**
* Returns a string representing the configured type of the axis.
*
* @return A string.
*/
@Override
protected String axisStr() {
if (this.configuredType == null) {
return "";
}
if (this.configuredType.equals(ValueAxis3DType.VALUE)) {
return "value";
}
if (this.configuredType.equals(ValueAxis3DType.X)) {
return "x";
}
if (this.configuredType.equals(ValueAxis3DType.Y)) {
return "y";
}
if (this.configuredType.equals(ValueAxis3DType.Z)) {
return "z";
}
return "";
}
/**
* Returns the axis range. You can set the axis range manually or you can
* rely on the autoAdjustRange feature to set the axis range to match
* the data being plotted.
*
* @return the axis range (never {@code null}).
*/
@Override
public Range getRange() {
return this.range;
}
/**
* Sets the axis range (bounds) and sends an {@link Axis3DChangeEvent} to
* all registered listeners.
*
* @param range the new range (must have positive length and
* {@code null} is not permitted).
*/
@Override
public void setRange(Range range) {
Args.nullNotPermitted(range, "range");
if (range.getLength() <= 0.0) {
throw new IllegalArgumentException(
"Requires a range with length > 0");
}
this.range = range;
this.autoAdjustRange = false;
fireChangeEvent(true);
}
/**
* Updates the axis range (used by the auto-range calculation) without
* notifying listeners.
*
* @param range the new range.
*/
protected void updateRange(Range range) {
this.range = range;
}
/**
* Sets the axis range and sends an {@link Axis3DChangeEvent} to all
* registered listeners.
*
* @param min the lower bound for the range (requires min < max).
* @param max the upper bound for the range (requires max > min).
*/
@Override
public void setRange(double min, double max) {
setRange(new Range(min, max));
}
/**
* Returns the flag that controls whether or not the axis range is
* automatically updated in response to dataset changes. The default
* value is {@code true}.
*
* @return A boolean.
*/
public boolean isAutoAdjustRange() {
return this.autoAdjustRange;
}
/**
* Sets the flag that controls whether or not the axis range is
* automatically updated in response to dataset changes, and sends an
* {@link Axis3DChangeEvent} to all registered listeners.
*
* @param autoAdjust the new flag value.
*/
public void setAutoAdjustRange(boolean autoAdjust) {
this.autoAdjustRange = autoAdjust;
fireChangeEvent(true);
}
/**
* Returns the size of the lower margin that is added by the auto-range
* calculation, as a percentage of the data range. This margin is used to
* prevent data items from being plotted right at the edges of the chart.
* The default value is {@code 0.05} (five percent).
*
* @return The lower margin.
*/
public double getLowerMargin() {
return this.lowerMargin;
}
/**
* Sets the size of the lower margin that will be added by the auto-range
* calculation and sends an {@link Axis3DChangeEvent} to all registered
* listeners.
*
* @param margin the margin as a percentage of the data range
* (0.05 = five percent).
*
* @see #setUpperMargin(double)
*/
public void setLowerMargin(double margin) {
this.lowerMargin = margin;
fireChangeEvent(true);
}
/**
* Returns the size of the upper margin that is added by the auto-range
* calculation, as a percentage of the data range. This margin is used to
* prevent data items from being plotted right at the edges of the chart.
* The default value is {@code 0.05} (five percent).
*
* @return The upper margin.
*/
public double getUpperMargin() {
return this.upperMargin;
}
/**
* Sets the size of the upper margin that will be added by the auto-range
* calculation and sends an {@link Axis3DChangeEvent} to all registered
* listeners.
*
* @param margin the margin as a percentage of the data range
* (0.05 = five percent).
*
* @see #setLowerMargin(double)
*/
public void setUpperMargin(double margin) {
this.upperMargin = margin;
fireChangeEvent(true);
}
/**
* Returns the default range used when the {@code autoAdjustRange}
* flag is {@code true} but the dataset contains no values. The
* default range is {@code (0.0 to 1.0)}, depending on the context
* you may want to change this.
*
* @return The default range (never {@code null}).
*
* @see #setDefaultAutoRange(Range)
*/
public Range getDefaultAutoRange() {
return this.defaultAutoRange;
}
/**
* Sets the default range used when the {@code autoAdjustRange}
* flag is {@code true} but the dataset contains no values, and sends
* an {@link Axis3DChangeEvent} to all registered listeners.
*
* @param range the range ({@code null} not permitted).
*
* @see #getDefaultAutoRange()
*/
public void setDefaultAutoRange(Range range) {
Args.nullNotPermitted(range, "range");
this.defaultAutoRange = range;
fireChangeEvent(true);
}
/**
* Returns the minimum length for the axis range when auto-calculated.
* The default value is 0.001.
*
* @return The minimum length.
*
* @since 1.4
*/
public double getMinAutoRangeLength() {
return this.minAutoRangeLength;
}
/**
* Sets the minimum length for the axis range when it is auto-calculated
* and sends a change event to all registered listeners.
*
* @param length the new minimum length.
*
* @since 1.4
*/
public void setMinAutoRangeLength(double length) {
Args.positiveRequired(length, "length");
this.minAutoRangeLength = length;
fireChangeEvent(this.range.getLength() < length);
}
/**
* Returns the flag that determines whether or not the order of values on
* the axis is inverted. The default value is {@code false}.
*
* @return A boolean.
*
* @since 1.5
*/
@Override
public boolean isInverted() {
return this.inverted;
}
/**
* Sets the flag that determines whether or not the order of values on the
* axis is inverted, and sends an {@link Axis3DChangeEvent} to all
* registered listeners.
*
* @param inverted the new flag value.
*
* @since 1.5
*/
@Override
public void setInverted(boolean inverted) {
this.inverted = inverted;
fireChangeEvent(true);
}
/**
* Returns the orientation for the tick labels. The default value is
* {@link LabelOrientation#PARALLEL}.
*
* @return The orientation for the tick labels (never {@code null}).
*
* @since 1.2
*/
public LabelOrientation getTickLabelOrientation() {
return this.tickLabelOrientation;
}
/**
* Sets the orientation for the tick labels and sends a change event to
* all registered listeners. In general, {@code PARALLEL} is the
* best setting for X and Z axes, and {@code PERPENDICULAR} is the
* best setting for Y axes.
*
* @param orientation the orientation ({@code null} not permitted).
*
* @since 1.2
*/
public void setTickLabelOrientation(LabelOrientation orientation) {
Args.nullNotPermitted(orientation, "orientation");
this.tickLabelOrientation = orientation;
fireChangeEvent(false);
}
/**
* Returns the tick label factor, a multiplier for the label height to
* determine the maximum number of tick labels that can be displayed.
* The default value is {@code 1.4}.
*
* @return The tick label factor.
*/
public double getTickLabelFactor() {
return this.tickLabelFactor;
}
/**
* Sets the tick label factor and sends an {@link Axis3DChangeEvent}
* to all registered listeners. This should be at least 1.0, higher values
* will result in larger gaps between the tick marks.
*
* @param factor the factor.
*/
public void setTickLabelFactor(double factor) {
this.tickLabelFactor = factor;
fireChangeEvent(false);
}
/**
* Returns the tick label offset, the gap between the tick marks and the
* tick labels (in Java2D units). The default value is {@code 5.0}.
*
* @return The tick label offset.
*/
public double getTickLabelOffset() {
return this.tickLabelOffset;
}
/**
* Sets the tick label offset and sends an {@link Axis3DChangeEvent} to
* all registered listeners.
*
* @param offset the offset.
*/
public void setTickLabelOffset(double offset) {
this.tickLabelOffset = offset;
}
/**
* Returns the length of the tick marks (in Java2D units). The default
* value is {@code 3.0}.
*
* @return The length of the tick marks.
*/
public double getTickMarkLength() {
return this.tickMarkLength;
}
/**
* Sets the length of the tick marks and sends an {@link Axis3DChangeEvent}
* to all registered listeners. You can set this to {@code 0.0} if
* you prefer no tick marks to be displayed on the axis.
*
* @param length the length (in Java2D units).
*/
public void setTickMarkLength(double length) {
this.tickMarkLength = length;
fireChangeEvent(false);
}
/**
* Returns the stroke used to draw the tick marks. The default value is
* {@code BasicStroke(0.5f)}.
*
* @return The tick mark stroke (never {@code null}).
*/
public Stroke getTickMarkStroke() {
return this.tickMarkStroke;
}
/**
* Sets the stroke used to draw the tick marks and sends an
* {@link Axis3DChangeEvent} to all registered listeners.
*
* @param stroke the stroke ({@code null} not permitted).
*/
public void setTickMarkStroke(Stroke stroke) {
Args.nullNotPermitted(stroke, "stroke");
this.tickMarkStroke = stroke;
fireChangeEvent(false);
}
/**
* Returns the paint used to draw the tick marks. The default value is
* {@code Color.GRAY}.
*
* @return The tick mark paint (never {@code null}).
*/
public Paint getTickMarkPaint() {
return this.tickMarkPaint;
}
/**
* Sets the paint used to draw the tick marks and sends an
* {@link Axis3DChangeEvent} to all registered listeners.
*
* @param paint the paint ({@code null} not permitted).
*/
public void setTickMarkPaint(Paint paint) {
Args.nullNotPermitted(paint, "paint");
this.tickMarkPaint = paint;
fireChangeEvent(false);
}
/**
* Configures the axis to be used as the value axis for the specified
* plot. This method is used internally, you should not need to call it
* directly.
*
* @param plot the plot ({@code null} not permitted).
*/
@Override @SuppressWarnings("unchecked")
public void configureAsValueAxis(CategoryPlot3D plot) {
this.configuredType = ValueAxis3DType.VALUE;
if (this.autoAdjustRange) {
CategoryDataset3D dataset = plot.getDataset();
Range valueRange = plot.getRenderer().findValueRange(dataset);
if (valueRange != null) {
updateRange(adjustedDataRange(valueRange));
} else {
updateRange(this.defaultAutoRange);
}
}
}
/**
* Configures the axis to be used as the x-axis for the specified plot.
* This method is used internally, you should not need to call it
* directly.
*
* @param plot the plot ({@code null} not permitted).
*/
@Override
public void configureAsXAxis(XYZPlot plot) {
this.configuredType = ValueAxis3DType.X;
if (this.autoAdjustRange) {
Range xRange = plot.getRenderer().findXRange(plot.getDataset());
if (xRange != null) {
updateRange(adjustedDataRange(xRange));
} else {
updateRange(this.defaultAutoRange);
}
}
}
/**
* Configures the axis to be used as the y-axis for the specified plot.
* This method is used internally, you should not need to call it
* directly.
*
* @param plot the plot ({@code null} not permitted).
*/
@Override
public void configureAsYAxis(XYZPlot plot) {
this.configuredType = ValueAxis3DType.Y;
if (this.autoAdjustRange) {
Range yRange = plot.getRenderer().findYRange(plot.getDataset());
if (yRange != null) {
updateRange(adjustedDataRange(yRange));
} else {
updateRange(this.defaultAutoRange);
}
}
}
/**
* Configures the axis to be used as the z-axis for the specified plot.
* This method is used internally, you should not need to call it
* directly.
*
* @param plot the plot ({@code null} not permitted).
*/
@Override
public void configureAsZAxis(XYZPlot plot) {
this.configuredType = ValueAxis3DType.Z;
if (this.autoAdjustRange) {
Range zRange = plot.getRenderer().findZRange(plot.getDataset());
if (zRange != null) {
updateRange(adjustedDataRange(zRange));
} else {
updateRange(this.defaultAutoRange);
}
}
}
/**
* Adjusts the range by adding the lower and upper margins and taking into
* account any other settings.
*
* @param range the range ({@code null} not permitted).
*
* @return The adjusted range.
*/
protected abstract Range adjustedDataRange(Range range);
/**
* Returns the marker with the specified key, if there is one.
*
* @param key the key ({@code null} not permitted).
*
* @return The marker (possibly {@code null}).
*
* @since 1.2
*/
@Override
public ValueMarker getMarker(String key) {
return this.valueMarkers.get(key);
}
/**
* Sets the marker for the specified key and sends a change event to
* all registered listeners. If there is an existing marker it is replaced
* (the axis will no longer listen for change events on the previous
* marker).
*
* @param key the key that identifies the marker ({@code null} not
* permitted).
* @param marker the marker ({@code null} permitted).
*
* @since 1.2
*/
public void setMarker(String key, ValueMarker marker) {
ValueMarker existing = this.valueMarkers.get(key);
if (existing != null) {
existing.removeChangeListener(this);
}
this.valueMarkers.put(key, marker);
marker.addChangeListener(this);
fireChangeEvent(false);
}
/**
* Returns a new map containing the markers assigned to this axis.
*
* @return A map.
*
* @since 1.2
*/
public Map getMarkers() {
return new LinkedHashMap<>(this.valueMarkers);
}
/**
* Generates and returns a list of marker data items for the axis.
*
* @return A list of marker data items (never {@code null}).
*/
@Override
public List generateMarkerData() {
List result = new ArrayList<>();
Range range = getRange();
for (Map.Entry entry
: this.valueMarkers.entrySet()) {
ValueMarker vm = entry.getValue();
if (range.intersects(vm.getRange())) {
MarkerData markerData;
if (vm instanceof NumberMarker) {
NumberMarker nm = (NumberMarker) vm;
markerData = new MarkerData(entry.getKey(),
range.percent(nm.getValue()));
markerData.setLabelAnchor(nm.getLabel() != null
? nm.getLabelAnchor() : null);
} else if (vm instanceof RangeMarker) {
RangeMarker rm = (RangeMarker) vm;
double startValue = rm.getStart().getValue();
boolean startPegged = false;
if (!range.contains(startValue)) {
startValue = range.peggedValue(startValue);
startPegged = true;
}
double startPos = range.percent(startValue);
double endValue = rm.getEnd().getValue();
boolean endPegged = false;
if (!range.contains(endValue)) {
endValue = range.peggedValue(endValue);
endPegged = true;
}
double endPos = range.percent(endValue);
markerData = new MarkerData(entry.getKey(), startPos,
startPegged, endPos, endPegged);
markerData.setLabelAnchor(rm.getLabel() != null
? rm.getLabelAnchor() : null);
} else {
throw new RuntimeException("Unrecognised marker.");
}
result.add(markerData);
}
}
return result;
}
/**
* Receives a {@link ChartElementVisitor}. This method is part of a general
* mechanism for traversing the chart structure and performing operations
* on each element in the chart. You will not normally call this method
* directly.
*
* @param visitor the visitor ({@code null} not permitted).
*
* @since 1.2
*/
@Override
public void receive(ChartElementVisitor visitor) {
for (ValueMarker marker : this.valueMarkers.values()) {
marker.receive(visitor);
}
visitor.visit(this);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof AbstractValueAxis3D)) {
return false;
}
AbstractValueAxis3D that = (AbstractValueAxis3D) obj;
if (!this.range.equals(that.range)) {
return false;
}
if (this.autoAdjustRange != that.autoAdjustRange) {
return false;
}
if (this.lowerMargin != that.lowerMargin) {
return false;
}
if (this.upperMargin != that.upperMargin) {
return false;
}
if (!this.defaultAutoRange.equals(that.defaultAutoRange)) {
return false;
}
if (this.tickLabelOffset != that.tickLabelOffset) {
return false;
}
if (this.tickLabelFactor != that.tickLabelFactor) {
return false;
}
if (!this.tickLabelOrientation.equals(that.tickLabelOrientation)) {
return false;
}
if (this.tickMarkLength != that.tickMarkLength) {
return false;
}
if (!ObjectUtils.equalsPaint(this.tickMarkPaint, that.tickMarkPaint)) {
return false;
}
if (!this.tickMarkStroke.equals(that.tickMarkStroke)) {
return false;
}
return super.equals(obj);
}
/**
* Provides serialization support.
*
* @param stream the output stream.
*
* @throws IOException if there is an I/O error.
*/
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
SerialUtils.writePaint(this.tickMarkPaint, stream);
SerialUtils.writeStroke(this.tickMarkStroke, stream);
}
/**
* Provides serialization support.
*
* @param stream the input stream.
*
* @throws IOException if there is an I/O error.
* @throws ClassNotFoundException if there is a classpath problem.
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
this.tickMarkPaint = SerialUtils.readPaint(stream);
this.tickMarkStroke = SerialUtils.readStroke(stream);
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/axis/Axis3D.java 0000664 0000000 0000000 00000016507 14173312715 0024721 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.axis;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.awt.Font;
import java.util.List;
import org.jfree.chart3d.ChartElement;
import org.jfree.chart3d.data.Range;
import org.jfree.chart3d.graphics3d.RenderingInfo;
/**
* An interface that must be supported by axes for 3D plots.
*/
public interface Axis3D extends ChartElement {
/**
* Returns the flag that determines whether or not the axis is drawn
* on the chart.
*
* @return A boolean.
*
* @see #setVisible(boolean)
*/
boolean isVisible();
/**
* Sets the flag that determines whether or not the axis is drawn on the
* chart and sends an {@link Axis3DChangeEvent} to all registered listeners.
*
* @param visible the flag.
*
* @see #isVisible()
*/
void setVisible(boolean visible);
/**
* Returns the font that is used to display the main axis label.
*
* @return The font (never {@code null}).
*/
Font getLabelFont();
/**
* Sets the font for the axis label (the main label, not the tick labels)
* and sends an {@link Axis3DChangeEvent} to all registered listeners.
*
* @param font the font ({@code null} not permitted).
*/
void setLabelFont(Font font);
/**
* Returns the color used to draw the axis label.
*
* @return The color (never {@code null}).
*
* @since 1.2
*/
Color getLabelColor();
/**
* Sets the color used to draw the axis label and sends an
* {@link Axis3DChangeEvent} to all registered listeners.
*
* @param color the color ({@code null} not permitted).
*
* @since 1.2
*/
void setLabelColor(Color color);
/**
* Returns the font that is used to display the tick labels.
*
* @return The font (never {@code null}).
*/
Font getTickLabelFont();
/**
* Sets the font for the tick labels and sends an {@link Axis3DChangeEvent}
* to all registered listeners.
*
* @param font the font ({@code null} not permitted).
*/
void setTickLabelFont(Font font);
/**
* Returns the color used to draw the axis tick labels.
*
* @return The color (never {@code null}).
*
* @since 1.2
*/
Color getTickLabelColor();
/**
* Sets the color used to draw the axis tick labels and sends an
* {@link Axis3DChangeEvent} to all registered listeners.
*
* @param color the color ({@code null} not permitted).
*
* @since 1.2
*/
void setTickLabelColor(Color color);
/**
* Returns the axis range (the minimum and maximum values displayed on
* the axis). Note that even categorical axes will have a range, although
* since numerical values are not displayed the range is often set to
* {@code (0.0, 1.0)} for convenience.
*
* @return The axis range (never {@code null}).
*/
Range getRange();
/**
* Sets the axis range and sends an {@link Axis3DChangeEvent} to all
* registered listeners.
*
* @param range the range ({@code null} not permitted).
*/
void setRange(Range range);
/**
* Sets the axis range and sends an {@link Axis3DChangeEvent} to all
* registered listeners.
*
* @param min the lower bound for the axis.
* @param max the upper bound for the axis.
*/
void setRange(double min, double max);
/**
* Returns the flag that determines whether or not the order of values on
* the axis is inverted. The default value is {@code false}.
*
* @return A boolean.
*
* @since 1.5
*/
boolean isInverted();
/**
* Sets the flag that determines whether or not the order of values on the
* axis is inverted, and sends an {@link Axis3DChangeEvent} to all
* registered listeners.
*
* @param inverted the new flag value.
*
* @since 1.5
*/
void setInverted(boolean inverted);
/**
* Translates a data value to a world coordinate. Since we draw the charts
* in a box that has one corner at {@code (0, 0, 0)}, we only need to
* know the length of the side of the box along which we are translating in
* order to do the calculation.
*
* @param value the data value.
* @param length the box side length.
*
* @return The translated value.
*/
double translateToWorld(double value, double length);
/**
* Draws the axis along an arbitrary line (between {@code startPt}
* and {@code endPt}). The opposing point is used as a reference
* point to know on which side of the axis to draw the labels.
*
* @param g2 the graphics target ({@code null} not permitted).
* @param startPt the starting point ({@code null} not permitted).
* @param endPt the end point ({@code null} not permitted)
* @param opposingPt an opposing point ({@code null} not permitted).
* @param tickData info about the ticks to draw ({@code null} not
* permitted).
* @param info an object to be populated with rendering info
* ({@code null} permitted).
* @param hinting a flag that controls whether or not element hinting
* should be performed.
*
* @since 1.3
*/
void draw(Graphics2D g2, Point2D startPt, Point2D endPt, Point2D opposingPt,
List tickData, RenderingInfo info, boolean hinting);
/**
* Registers a listener so that it receives notification of changes to the
* axis.
*
* @param listener the listener ({@code null} not permitted).
*/
void addChangeListener(Axis3DChangeListener listener);
/**
* Deregisters a listener so that it no longer receives notification of
* changes to the axis.
*
* @param listener the listener ({@code null} not permitted).
*/
void removeChangeListener(Axis3DChangeListener listener);
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/axis/Axis3DChangeEvent.java 0000664 0000000 0000000 00000007113 14173312715 0027022 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.axis;
import java.util.EventObject;
import org.jfree.chart3d.internal.Args;
/**
* An event associated with a change to an {@link Axis3D}. These change
* events will be generated by an axis and broadcast to the plot that owns the
* axis (in the standard setup, the plot will then trigger its own change event
* to notify the chart that a subcomponent of the plot has changed).
*
* NOTE: This class is serializable, but the serialization format is subject
* to change in future releases and should not be relied upon for persisting
* instances of this class.
*/
@SuppressWarnings("serial")
public class Axis3DChangeEvent extends EventObject {
/** The axis associated with this event. */
private Axis3D axis;
/**
* A flag indicating whether or not the change requires the 3D world to
* be updated.
*/
private boolean requiresWorldUpdate;
/**
* Creates a new event.
*
* @param axis the axis ({@code null} not permitted).
* @param requiresWorldUpdate a flag indicating whether or not this change
* requires the 3D world to be updated.
*/
public Axis3DChangeEvent(Axis3D axis, boolean requiresWorldUpdate) {
this(axis, axis, requiresWorldUpdate);
}
/**
* Creates a new event.
*
* @param source the event source.
* @param axis the axis ({@code null} not permitted).
* @param requiresWorldUpdate a flag indicating whether or not this change
* requires the 3D world to be updated.
*/
public Axis3DChangeEvent(Object source, Axis3D axis,
boolean requiresWorldUpdate) {
super(source);
Args.nullNotPermitted(axis, "axis");
this.axis = axis;
this.requiresWorldUpdate = requiresWorldUpdate;
}
/**
* Returns the axis associated with this event.
*
* @return The axis (never {@code null}).
*/
public Axis3D getAxis() {
return this.axis;
}
/**
* Returns the flag that indicates whether or not this change will require
* the 3D world to be updated.
*
* @return A boolean.
*
* @since 1.2
*/
public boolean requiresWorldUpdate() {
return this.requiresWorldUpdate;
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/axis/Axis3DChangeListener.java 0000664 0000000 0000000 00000003706 14173312715 0027532 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.axis;
import java.util.EventListener;
import org.jfree.chart3d.plot.CategoryPlot3D;
import org.jfree.chart3d.plot.XYZPlot;
/**
* A listener for axis change events. The plot classes that have axes
* ({@link CategoryPlot3D} and {@link XYZPlot}) implement this interface so
* that they can receive notification when the axes are modified.
*/
public interface Axis3DChangeListener extends EventListener {
/**
* Called to inform that an axis change event has occurred.
*
* @param event the event ({@code null} not permitted).
*/
void axisChanged(Axis3DChangeEvent event);
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/axis/CategoryAxis3D.java 0000664 0000000 0000000 00000012333 14173312715 0026410 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.axis;
import java.util.List;
import org.jfree.chart3d.data.category.CategoryDataset3D;
import org.jfree.chart3d.marker.CategoryMarker;
import org.jfree.chart3d.marker.MarkerData;
import org.jfree.chart3d.plot.CategoryPlot3D;
/**
* An axis that displays categories and is used with a {@link CategoryPlot3D}
* for the row and column axes. Most of the methods in this interface are
* intended to be called by the plot that the axis is assigned to, they won't
* normally be called by external code.
*/
public interface CategoryAxis3D extends Axis3D {
/**
* Returns {@code true} if this axis is being used as the row axis
* in a plot, and {@code false} otherwise.
*
* @return A boolean.
*
* @since 1.3
*/
boolean isRowAxis();
/**
* Returns {@code true} if this axis is being used as the column axis
* in a plot, and {@code false} otherwise.
*
* @return A boolean.
*
* @since 1.3
*/
boolean isColumnAxis();
/**
* Configure the axis as a row axis for the specified plot. Note that this
* method will be called by the plot, it will not normally be called by
* external code.
*
* @param plot the plot ({@code null} not permitted).
*/
void configureAsRowAxis(CategoryPlot3D plot);
/**
* Configure the axis as a column axis for the specified plot. Note that
* this method will be called by the plot, it will not normally be called
* by external code.
*
* @param plot the plot ({@code null} not permitted).
*/
void configureAsColumnAxis(CategoryPlot3D plot);
/**
* Returns the width of a single category in units corresponding to
* the current axis range.
*
* @return The width of a single category.
*/
double getCategoryWidth();
/**
* Returns the numerical value along the axis that corresponds to the
* specified category. If the category is unknown, this method will
* return {@code Double.NaN}.
*
* @param category the category ({@code null} not permitted).
*
* @return The axis value.
*/
double getCategoryValue(Comparable> category);
/**
* Generates the tick data for the axis (assumes the axis is being used
* as the row axis). The dataset is passed as an argument to provide the
* opportunity to incorporate dataset-specific info into tick labels (for
* example, a row label might show the total for that row in the dataset)
* ---whether or not this is used depends on the axis implementation.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The tick data.
*
* @since 1.2
*/
List generateTickDataForRows(CategoryDataset3D dataset);
/**
* Generates the tick data for the axis (assumes the axis is being used
* as the row axis). The dataset is passed as an argument to provide the
* opportunity to incorporate dataset-specific info into tick labels (for
* example, a row label might show the total for that row in the dataset)
* ---whether or not this is used depends on the axis implementation.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The tick data.
*
* @since 1.2
*/
List generateTickDataForColumns(CategoryDataset3D dataset);
/**
* Returns a list of marker data instances for the markers that fall
* within the current axis range.
*
* @return A list of marker data.
*/
List generateMarkerData();
/**
* Returns the marker with the specified key, if there is one.
*
* @param key the key ({@code null} not permitted).
*
* @return The marker (possibly {@code null}).
*
* @since 1.2
*/
CategoryMarker getMarker(String key);
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/axis/IntegerTickSelector.java 0000664 0000000 0000000 00000012556 14173312715 0027537 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.axis;
import java.io.Serializable;
import java.text.DecimalFormat;
import java.text.Format;
import org.jfree.chart3d.internal.Args;
/**
* A {@link TickSelector} implementation that selects tick units in multiples
* of 1, 2 and 5 and only displays integer values.
*
* NOTE: This class is serializable, but the serialization format is subject
* to change in future releases and should not be relied upon for persisting
* instances of this class.
*
* @since 1.5
*/
@SuppressWarnings("serial")
public class IntegerTickSelector implements TickSelector, Serializable {
private int power = 0;
private int factor = 1;
/**
* Creates a new instance.
*/
public IntegerTickSelector() {
this.power = 0;
this.factor = 1;
}
/**
* Selects and returns a standard tick size that is greater than or equal to
* the specified reference value and, ideally, as close to it as possible
* (to minimise the number of iterations used by axes to determine the tick
* size to use). After a call to this method, the
* {@link #getCurrentTickSize()} method should return the selected tick
* size (there is a "pointer" to this tick size), the {@link #next()}
* method should move the pointer to the next (larger) standard tick size,
* and the {@link #previous()} method should move the pointer to the
* previous (smaller) standard tick size.
*
* @param reference the reference value (must be positive and finite).
*
* @return The selected tick size.
*/
@Override
public double select(double reference) {
Args.finitePositiveRequired(reference, "reference");
this.power = (int) Math.ceil(Math.log10(reference));
this.factor = 1;
return getCurrentTickSize();
}
/**
* Move the cursor to the next (larger) tick size, if there is one.
* Returns {@code true} in the case that the cursor is moved, and
* {@code false} where there are a finite number of tick sizes and the
* current tick size is the largest available.
*/
@Override
public boolean next() {
if (factor == 1) {
factor = 2;
return true;
}
if (factor == 2) {
factor = 5;
return true;
}
if (factor == 5) {
power++;
factor = 1;
return true;
}
throw new IllegalStateException("We should never get here.");
}
/**
* Move the cursor to the previous (smaller) tick size, if there is one.
* Returns {@code true} in the case that the cursor is moved, and
* {@code false} where there are a finite number of tick sizes and the
* current tick size is the smallest available.
*/
@Override
public boolean previous() {
if (factor == 1) {
if (power == 0) {
return false;
}
factor = 5;
power--;
return true;
}
if (factor == 2) {
factor = 1;
return true;
}
if (factor == 5) {
factor = 2;
return true;
}
throw new IllegalStateException("We should never get here.");
}
@Override
public double getCurrentTickSize() {
return this.factor * Math.pow(10.0, this.power);
}
private final DecimalFormat df0 = new DecimalFormat("#,##0");
@Override
public Format getCurrentTickLabelFormat() {
if (power >= 0 && power <= 6) {
return df0;
}
return new DecimalFormat("0.0000E0");
}
/**
* Tests this instance for equality with an arbitrary object.
*
* @param obj the object ({@code null} permitted).
*
* @return A boolean.
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof IntegerTickSelector)) {
return false;
}
return true;
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/axis/LabelOrientation.java 0000664 0000000 0000000 00000003154 14173312715 0027053 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.axis;
/**
* The orientation of an axis label relative to the axis.
*/
public enum LabelOrientation {
/** Labels are drawn perpendicular to the axis line. */
PERPENDICULAR,
/** Labels are drawn parallel to the axis line. */
PARALLEL
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/axis/LogAxis3D.java 0000664 0000000 0000000 00000064670 14173312715 0025367 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.axis;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.text.AttributedString;
import java.text.DecimalFormat;
import java.text.Format;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import org.jfree.chart3d.Chart3DHints;
import org.jfree.chart3d.data.Range;
import org.jfree.chart3d.graphics2d.TextAnchor;
import org.jfree.chart3d.graphics3d.RenderingInfo;
import org.jfree.chart3d.graphics3d.internal.Utils2D;
import org.jfree.chart3d.internal.Args;
import org.jfree.chart3d.internal.ObjectUtils;
import org.jfree.chart3d.internal.TextUtils;
/**
* A numerical axis with a logarithmic scale.
*
* NOTE: This class is serializable, but the serialization format is subject
* to change in future releases and should not be relied upon for persisting
* instances of this class.
*
* @since 1.2
*/
@SuppressWarnings("serial")
public class LogAxis3D extends AbstractValueAxis3D implements ValueAxis3D {
/** The default value for the smallest value attribute. */
public static final double DEFAULT_SMALLEST_VALUE = 1E-100;
/** The logarithm base. */
private double base = 10.0;
/** The logarithm of the base value - cached for performance. */
private double baseLog;
/** The logarithms of the current axis range. */
private Range logRange;
/**
* The smallest value for the axis. In general, only positive values
* can be plotted against a log axis but to simplify the generation of
* bar charts (where the base of the bars is typically at 0.0) the axis
* will return {@code smallestValue} as the translated value for 0.0.
* It is important to make sure there are no real data values smaller
* than this value.
*/
private double smallestValue;
/**
* The symbol used to represent the log base on the tick labels. If this
* is {@code null} the numerical value will be displayed.
*/
private String baseSymbol;
/**
* The number formatter for the base value.
*/
private NumberFormat baseFormatter = new DecimalFormat("0");
/**
* The tick selector (if not {@code null}, then auto-tick selection is
* used).
*/
private TickSelector tickSelector = new NumberTickSelector();
/**
* The tick size. If the tickSelector is not {@code null} then it is
* used to auto-select an appropriate tick size and format.
*/
private double tickSize = 1.0;
/** The tick formatter (never {@code null}). */
private Format tickLabelFormatter = new DecimalFormat("0.0");
/**
* Creates a new log axis with a default base of 10.
*
* @param label the axis label ({@code null} permitted).
*/
public LogAxis3D(String label) {
super(label, new Range(DEFAULT_SMALLEST_VALUE, 1.0));
this.base = 10.0;
this.baseLog = Math.log(this.base);
this.logRange = new Range(calculateLog(DEFAULT_SMALLEST_VALUE),
calculateLog(1.0));
this.smallestValue = DEFAULT_SMALLEST_VALUE;
}
/**
* Returns the logarithmic base value. The default value is {@code 10}.
*
* @return The logarithmic base value.
*/
public double getBase() {
return this.base;
}
/**
* Sets the logarithmic base value and sends an {@code Axis3DChangeEvent}
* to all registered listeners.
*
* @param base the base value.
*/
public void setBase(double base) {
this.base = base;
this.baseLog = Math.log(base);
fireChangeEvent(true);
}
/**
* Returns the base symbol, used in tick labels for the axis. A typical
* value would be "e" when using a natural logarithm scale. If this is
* {@code null}, the tick labels will display the numerical base value.
* The default value is {@code null}.
*
* @return The base symbol (possibly {@code null}).
*/
public String getBaseSymbol() {
return this.baseSymbol;
}
/**
* Sets the base symbol and sends an {@code Axis3DChangeEvent} to all
* registered listeners. If you set this to {@code null}, the tick labels
* will display a numerical representation of the base value.
*
* @param symbol the base symbol ({@code null} permitted).
*/
public void setBaseSymbol(String symbol) {
this.baseSymbol = symbol;
fireChangeEvent(false);
}
/**
* Returns the formatter used for the log base value when it is displayed
* in tick labels. The default value is {@code NumberFormat("0")}.
*
* @return The base formatter (never {@code null}).
*/
public NumberFormat getBaseFormatter() {
return this.baseFormatter;
}
/**
* Sets the formatter for the log base value and sends an
* {@code Axis3DChangeEvent} to all registered listeners.
*
* @param formatter the formatter ({@code null} not permitted).
*/
public void setBaseFormatter(NumberFormat formatter) {
Args.nullNotPermitted(formatter, "formatter");
this.baseFormatter = formatter;
fireChangeEvent(false);
}
/**
* Returns the smallest positive data value that will be represented on
* the axis. This will be used as the lower bound for the axis if the
* data range contains any value from {@code 0.0} up to this value.
*
* @return The smallest value.
*/
public double getSmallestValue() {
return this.smallestValue;
}
/**
* Sets the smallest positive data value that will be represented on the
* axis and sends an {@code Axis3DChangeEvent} to all registered listeners.
*
* @param smallestValue the value (must be positive).
*/
public void setSmallestValue(double smallestValue) {
Args.positiveRequired(smallestValue, "smallestValue");
this.smallestValue = smallestValue;
fireChangeEvent(true);
}
/**
* Returns the tick selector for the axis.
*
* @return The tick selector (possibly {@code null}).
*/
public TickSelector getTickSelector() {
return this.tickSelector;
}
/**
* Sets the tick selector and sends an {@code Axis3DChangeEvent} to all
* registered listeners.
*
* @param selector the selector ({@code null} permitted).
*/
public void setTickSelector(TickSelector selector) {
this.tickSelector = selector;
fireChangeEvent(false);
}
/**
* Returns the tick size to be used when the tick selector is
* {@code null}.
*
* @return The tick size.
*/
public double getTickSize() {
return this.tickSize;
}
/**
* Sets the tick size and sends an {@code Axis3DChangeEvent} to all
* registered listeners.
*
* @param tickSize the new tick size.
*/
public void setTickSize(double tickSize) {
this.tickSize = tickSize;
fireChangeEvent(false);
}
/**
* Returns the tick label formatter. The default value is
* {@code DecimalFormat("0.0")}.
*
* @return The tick label formatter (never {@code null}).
*/
public Format getTickLabelFormatter() {
return this.tickLabelFormatter;
}
/**
* Sets the formatter for the tick labels and sends an
* {@code Axis3DChangeEvent} to all registered listeners.
*
* @param formatter the formatter ({@code null} not permitted).
*/
public void setTickLabelFormatter(Format formatter) {
Args.nullNotPermitted(formatter, "formatter");
this.tickLabelFormatter = formatter;
fireChangeEvent(false);
}
/**
* Sets the range for the axis. This method is overridden to check that
* the range does not contain negative values, and to update the log values
* for the range.
*
* @param range the range ({@code nul} not permitted).
*/
@Override
public void setRange(Range range) {
Args.nullNotPermitted(range, "range");
this.range = new Range(Math.max(range.getMin(), this.smallestValue),
range.getMax());
this.logRange = new Range(calculateLog(this.range.getMin()),
calculateLog(this.range.getMax()));
fireChangeEvent(true);
}
/**
* Sets the range for the axis. This method is overridden to check that
* the range does not contain negative values, and to update the log values
* for the range.
*
* @param min the lower bound for the range.
* @param max the upper bound for the range.
*/
@Override
public void setRange(double min, double max) {
Args.negativeNotPermitted(min, "min");
this.range = new Range(Math.max(min, this.smallestValue), max);
this.logRange = new Range(calculateLog(this.range.getMin()),
calculateLog(this.range.getMax()));
fireChangeEvent(true);
}
@Override
protected void updateRange(Range range) {
this.range = range;
this.logRange = new Range(calculateLog(this.range.getMin()),
calculateLog(this.range.getMax()));
}
/**
* Calculates the log of the given {@code value}, using the current base.
*
* @param value the value (negatives not permitted).
*
* @return The log of the given value.
*
* @see #calculateValue(double)
* @see #getBase()
*/
public final double calculateLog(double value) {
return Math.log(value) / this.baseLog;
}
/**
* Calculates the value from a given log value.
*
* @param log the log value.
*
* @return The value with the given log.
*
* @see #calculateLog(double)
* @see #getBase()
*/
public final double calculateValue(double log) {
return Math.pow(this.base, log);
}
/**
* Translates a data value to a world coordinate, assuming that the axis
* begins at the origin and has the specified length.
*
* @param value the data value.
* @param length the axis length in world coordinates.
*
* @return The world coordinate of this data value on the axis.
*/
@Override
public double translateToWorld(double value, double length) {
double logv = calculateLog(value);
double percent = this.logRange.percent(logv);
if (isInverted()) {
percent = 1.0 - percent;
}
return percent * length;
}
/**
* Draws the axis.
*
* @param g2 the graphics target ({@code null} not permitted).
* @param startPt the starting point.
* @param endPt the ending point.
* @param opposingPt an opposing point (labels will be on the other side
* of the line).
* @param tickData the tick data (including anchor points calculated by
* the 3D engine).
* @param info an object to be populated with rendering info
* ({@code null} permitted).
* @param hinting perform element hinting?
*/
@Override
public void draw(Graphics2D g2, Point2D startPt, Point2D endPt,
Point2D opposingPt, List tickData, RenderingInfo info,
boolean hinting) {
if (!isVisible()) {
return;
}
// draw a line for the axis
g2.setStroke(getLineStroke());
g2.setPaint(getLineColor());
Line2D axisLine = new Line2D.Float(startPt, endPt);
g2.draw(axisLine);
// draw the tick marks and labels
double tickMarkLength = getTickMarkLength();
double tickLabelOffset = getTickLabelOffset();
g2.setPaint(getTickMarkPaint());
g2.setStroke(getTickMarkStroke());
for (TickData t : tickData) {
if (tickMarkLength > 0.0) {
Line2D tickLine = Utils2D.createPerpendicularLine(axisLine,
t.getAnchorPt(), tickMarkLength, opposingPt);
g2.draw(tickLine);
}
}
double maxTickLabelDim = 0.0;
if (getTickLabelsVisible()) {
g2.setFont(getTickLabelFont());
g2.setPaint(getTickLabelColor());
LabelOrientation orientation = getTickLabelOrientation();
if (orientation.equals(LabelOrientation.PERPENDICULAR)) {
maxTickLabelDim = drawPerpendicularTickLabels(g2, axisLine,
opposingPt, tickData, hinting);
} else if (orientation.equals(LabelOrientation.PARALLEL)) {
maxTickLabelDim = g2.getFontMetrics().getHeight();
double adj = g2.getFontMetrics().getAscent() / 2.0;
drawParallelTickLabels(g2, axisLine, opposingPt, tickData, adj,
hinting);
}
}
// draw the axis label (if any)...
if (getLabel() != null) {
/* Shape labelBounds = */drawAxisLabel(getLabel(), g2, axisLine,
opposingPt, maxTickLabelDim + tickMarkLength
+ tickLabelOffset + getLabelOffset(), info, hinting);
}
}
private double drawPerpendicularTickLabels(Graphics2D g2, Line2D axisLine,
Point2D opposingPt, List tickData, boolean hinting) {
double result = 0.0;
for (TickData t : tickData) {
double theta = Utils2D.calculateTheta(axisLine);
double thetaAdj = theta + Math.PI / 2.0;
if (thetaAdj < -Math.PI / 2.0) {
thetaAdj = thetaAdj + Math.PI;
}
if (thetaAdj > Math.PI / 2.0) {
thetaAdj = thetaAdj - Math.PI;
}
Line2D perpLine = Utils2D.createPerpendicularLine(axisLine,
t.getAnchorPt(), getTickMarkLength()
+ getTickLabelOffset(), opposingPt);
double perpTheta = Utils2D.calculateTheta(perpLine);
TextAnchor textAnchor = TextAnchor.CENTER_LEFT;
if (Math.abs(perpTheta) > Math.PI / 2.0) {
textAnchor = TextAnchor.CENTER_RIGHT;
}
double logy = calculateLog(t.getDataValue());
AttributedString as = createTickLabelAttributedString(logy,
this.tickLabelFormatter);
Rectangle2D nonRotatedBounds = new Rectangle2D.Double();
if (hinting) {
Map m = new HashMap<>();
m.put("ref", "{\"type\": \"valueTickLabel\", \"axis\": "
+ axisStr() + ", \"value\": \""
+ t.getDataValue() + "\"}");
g2.setRenderingHint(Chart3DHints.KEY_BEGIN_ELEMENT, m);
}
TextUtils.drawRotatedString(as, g2,
(float) perpLine.getX2(), (float) perpLine.getY2(),
textAnchor, thetaAdj, textAnchor, nonRotatedBounds);
if (hinting) {
g2.setRenderingHint(Chart3DHints.KEY_END_ELEMENT, true);
}
result = Math.max(result, nonRotatedBounds.getWidth());
}
return result;
}
private void drawParallelTickLabels(Graphics2D g2, Line2D axisLine,
Point2D opposingPt, List tickData, double adj,
boolean hinting) {
for (TickData t : tickData) {
double theta = Utils2D.calculateTheta(axisLine);
TextAnchor anchor = TextAnchor.CENTER;
if (theta < -Math.PI / 2.0) {
theta = theta + Math.PI;
anchor = TextAnchor.CENTER;
}
if (theta > Math.PI / 2.0) {
theta = theta - Math.PI;
anchor = TextAnchor.CENTER;
}
Line2D perpLine = Utils2D.createPerpendicularLine(axisLine,
t.getAnchorPt(), getTickMarkLength()
+ getTickLabelOffset() + adj, opposingPt);
double logy = calculateLog(t.getDataValue());
AttributedString as = createTickLabelAttributedString(logy,
this.tickSelector.getCurrentTickLabelFormat());
if (hinting) {
Map m = new HashMap<>();
m.put("ref", "{\"type\": \"valueTickLabel\", \"axis\": "
+ axisStr() + ", \"value\": \""
+ t.getDataValue() + "\"}");
g2.setRenderingHint(Chart3DHints.KEY_BEGIN_ELEMENT, m);
}
TextUtils.drawRotatedString(as, g2,
(float) perpLine.getX2(), (float) perpLine.getY2(),
anchor, theta, anchor, null);
if (hinting) {
g2.setRenderingHint(Chart3DHints.KEY_END_ELEMENT, true);
}
}
}
private AttributedString createTickLabelAttributedString(double logy,
Format exponentFormatter) {
String baseStr = this.baseSymbol;
if (baseStr == null) {
baseStr = this.baseFormatter.format(this.base);
}
String exponentStr = exponentFormatter.format(logy);
AttributedString as = new AttributedString(baseStr + exponentStr);
as.addAttributes(getTickLabelFont().getAttributes(), 0, (baseStr
+ exponentStr).length());
as.addAttribute(TextAttribute.SUPERSCRIPT,
TextAttribute.SUPERSCRIPT_SUPER, baseStr.length(),
baseStr.length() + exponentStr.length());
return as;
}
/**
* Adjusts the range by adding the lower and upper margins on the
* logarithmic range.
*
* @param range the range ({@code nul} not permitted).
*
* @return The adjusted range.
*/
@Override
protected Range adjustedDataRange(Range range) {
Args.nullNotPermitted(range, "range");
double logmin = calculateLog(Math.max(range.getMin(),
this.smallestValue));
double logmax = calculateLog(range.getMax());
double length = logmax - logmin;
double lm = length * getLowerMargin();
double um = length * getUpperMargin();
double lowerBound = calculateValue(logmin - lm);
double upperBound = calculateValue(logmax + um);
return new Range(lowerBound, upperBound);
}
/**
* Selects a standard tick unit on the logarithmic range.
*
* @param g2 the graphics target ({@code null} not permitted).
* @param pt0 the starting point.
* @param pt1 the ending point.
* @param opposingPt an opposing point.
*
* @return The tick unit (log increment).
*/
@Override
public double selectTick(Graphics2D g2, Point2D pt0, Point2D pt1,
Point2D opposingPt) {
if (this.tickSelector == null) {
return this.tickSize;
}
g2.setFont(getTickLabelFont());
FontMetrics fm = g2.getFontMetrics();
double length = pt0.distance(pt1);
double rangeLength = this.logRange.getLength();
LabelOrientation orientation = getTickLabelOrientation();
if (orientation.equals(LabelOrientation.PERPENDICULAR)) {
// based on the font height, we can determine roughly how many tick
// labels will fit in the length available
int height = fm.getHeight();
// the tickLabelFactor allows some control over how dense the labels
// will be
int maxTicks = (int) (length / (height * getTickLabelFactor()));
if (maxTicks > 2 && this.tickSelector != null) {
this.tickSelector.select(rangeLength / 2.0);
// step through until we have too many ticks OR we run out of
// tick sizes
int tickCount = (int) (rangeLength
/ this.tickSelector.getCurrentTickSize());
while (tickCount < maxTicks) {
this.tickSelector.previous();
tickCount = (int) (rangeLength
/ this.tickSelector.getCurrentTickSize());
}
this.tickSelector.next();
this.tickSize = this.tickSelector.getCurrentTickSize();
this.tickLabelFormatter
= this.tickSelector.getCurrentTickLabelFormat();
} else {
this.tickSize = Double.NaN;
}
} else if (orientation.equals(LabelOrientation.PARALLEL)) {
// choose a unit that is at least as large as the length of the axis
this.tickSelector.select(rangeLength);
boolean done = false;
while (!done) {
if (this.tickSelector.previous()) {
// estimate the label widths, and do they overlap?
AttributedString s0 = createTickLabelAttributedString(
this.logRange.getMax() + this.logRange.getMin(),
this.tickSelector.getCurrentTickLabelFormat());
TextLayout layout0 = new TextLayout(s0.getIterator(),
g2.getFontRenderContext());
double w0 = layout0.getAdvance();
AttributedString s1 = createTickLabelAttributedString(
this.logRange.getMax() + this.logRange.getMin(),
this.tickSelector.getCurrentTickLabelFormat());
TextLayout layout1 = new TextLayout(s1.getIterator(),
g2.getFontRenderContext());
double w1 = layout1.getAdvance();
double w = Math.max(w0, w1);
int n = (int) (length / (w * this.getTickLabelFactor()));
if (n < rangeLength
/ tickSelector.getCurrentTickSize()) {
tickSelector.next();
done = true;
}
} else {
done = true;
}
}
this.tickSize = this.tickSelector.getCurrentTickSize();
this.tickLabelFormatter
= this.tickSelector.getCurrentTickLabelFormat();
}
return this.tickSize;
}
/**
* Generates tick data for the axis, assuming the specified tick unit
* (a log increment in this case). If the tick unit is Double.NaN then
* ticks will be added for the bounds of the axis only.
*
* @param tickUnit the tick unit.
*
* @return A list of tick data items.
*/
@Override
public List generateTickData(double tickUnit) {
List result = new ArrayList<>();
if (Double.isNaN(tickUnit)) {
result.add(new TickData(0, getRange().getMin()));
result.add(new TickData(1, getRange().getMax()));
} else {
double logx = tickUnit
* Math.ceil(this.logRange.getMin() / tickUnit);
while (logx <= this.logRange.getMax()) {
result.add(new TickData(this.logRange.percent(logx),
calculateValue(logx)));
logx += tickUnit;
}
}
return result;
}
@Override
public int hashCode() {
int hash = 5;
hash = 59 * hash + (int) (Double.doubleToLongBits(this.base)
^ (Double.doubleToLongBits(this.base) >>> 32));
hash = 59 * hash + (int) (Double.doubleToLongBits(this.smallestValue)
^ (Double.doubleToLongBits(this.smallestValue) >>> 32));
hash = 59 * hash + ObjectUtils.hashCode(this.baseSymbol);
hash = 59 * hash + ObjectUtils.hashCode(this.baseFormatter);
hash = 59 * hash + ObjectUtils.hashCode(this.tickSelector);
hash = 59 * hash + (int) (Double.doubleToLongBits(this.tickSize)
^ (Double.doubleToLongBits(this.tickSize) >>> 32));
hash = 59 * hash + ObjectUtils.hashCode(this.tickLabelFormatter);
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final LogAxis3D other = (LogAxis3D) obj;
if (Double.doubleToLongBits(this.base)
!= Double.doubleToLongBits(other.base)) {
return false;
}
if (Double.doubleToLongBits(this.smallestValue)
!= Double.doubleToLongBits(other.smallestValue)) {
return false;
}
if (!ObjectUtils.equals(this.baseSymbol, other.baseSymbol)) {
return false;
}
if (!ObjectUtils.equals(this.baseFormatter, other.baseFormatter)) {
return false;
}
if (!ObjectUtils.equals(this.tickSelector, other.tickSelector)) {
return false;
}
if (Double.doubleToLongBits(this.tickSize)
!= Double.doubleToLongBits(other.tickSize)) {
return false;
}
if (!ObjectUtils.equals(this.tickLabelFormatter,
other.tickLabelFormatter)) {
return false;
}
return super.equals(obj);
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/axis/NumberAxis3D.java 0000664 0000000 0000000 00000061674 14173312715 0026077 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.axis;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.font.LineMetrics;
import java.text.DecimalFormat;
import java.text.Format;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import org.jfree.chart3d.Chart3DHints;
import org.jfree.chart3d.data.Range;
import org.jfree.chart3d.graphics2d.TextAnchor;
import org.jfree.chart3d.graphics3d.RenderedElement;
import org.jfree.chart3d.graphics3d.RenderingInfo;
import org.jfree.chart3d.graphics3d.internal.Utils2D;
import org.jfree.chart3d.interaction.InteractiveElementType;
import org.jfree.chart3d.internal.TextUtils;
import org.jfree.chart3d.internal.Args;
import org.jfree.chart3d.internal.ObjectUtils;
import org.jfree.chart3d.plot.CategoryPlot3D;
import org.jfree.chart3d.plot.XYZPlot;
/**
* A numerical axis for use with 3D plots (implements {@link ValueAxis3D}).
* In a {@link CategoryPlot3D} the value axis (the vertical one) is numerical,
* and in an {@link XYZPlot} all the axes (x, y and z) are numerical - for
* all these cases an instance of this class can be used.
*
* NOTE: This class is serializable, but the serialization format is subject
* to change in future releases and should not be relied upon for persisting
* instances of this class.
*/
public class NumberAxis3D extends AbstractValueAxis3D implements ValueAxis3D,
Serializable {
/**
* Default formatter for axis number values. Can be overwritten.
*/
private static final Format DEFAULT_TICK_LABEL_FORMATTER = new DecimalFormat("0.00");
/**
* A flag indicating whether or not the auto-range calculation should
* include zero.
*/
private boolean autoRangeIncludesZero;
/**
* A flag that controls how zero is handled when it falls within the
* margins. If {@code true}, the margin is truncated at zero, if
* {@code false} the margin is not changed.
*/
private boolean autoRangeStickyZero;
/**
* The tick selector (if not {@code null}, then auto-tick selection is
* used).
*/
private TickSelector tickSelector;
/**
* The tick size. If the tickSelector is not {@code null} then it is
* used to auto-select an appropriate tick size and format.
*/
private double tickSize;
/** The tick formatter (never {@code null}). */
private Format tickLabelFormatter;
/**
* Creates a new axis with the specified label and default attributes.
*
* @param label the axis label ({@code null} permitted).
*/
public NumberAxis3D(String label) {
this(label, new Range(0.0, 1.0));
}
/**
* Creates a new axis with the specified label and range.
*
* @param label the axis label ({@code null} permitted).
* @param range the range ({@code null} not permitted).
*/
public NumberAxis3D(String label, Range range) {
super(label, range);
this.autoRangeIncludesZero = false;
this.autoRangeStickyZero = true;
this.tickSelector = new NumberTickSelector();
this.tickSize = range.getLength() / 10.0;
this.tickLabelFormatter = DEFAULT_TICK_LABEL_FORMATTER;
}
/**
* Returns the flag that determines whether or not the auto range
* mechanism should force zero to be included in the range. The default
* value is {@code false}.
*
* @return A boolean.
*/
public boolean getAutoRangeIncludesZero() {
return this.autoRangeIncludesZero;
}
/**
* Sets the flag that controls whether or not the auto range mechanism
* should force zero to be included in the axis range, and sends an
* {@link Axis3DChangeEvent} to all registered listeners.
*
* @param include the new flag value.
*/
public void setAutoRangeIncludeZero(boolean include) {
this.autoRangeIncludesZero = include;
fireChangeEvent(true);
}
/**
* Returns the flag that controls the behaviour of the auto range
* mechanism when zero falls into the axis margins. The default value
* is {@code true}.
*
* @return A boolean.
*
* @see #setAutoRangeStickyZero(boolean)
*/
public boolean getAutoRangeStickyZero() {
return this.autoRangeStickyZero;
}
/**
* Sets the flag that controls the behaviour of the auto range mechanism
* when zero falls into the axis margins. If {@code true}, when
* zero is in the axis margin the axis range is truncated at zero. If
* {@code false}, there is no special treatment.
*
* @param sticky the new flag value.
*/
public void setAutoRangeStickyZero(boolean sticky) {
this.autoRangeStickyZero = sticky;
fireChangeEvent(true);
}
/**
* Returns the tick selector, an object that is responsible for choosing
* standard tick units for the axis. The default value is a default
* instance of {@link NumberTickSelector}.
*
* @return The tick selector.
*
* @see #setTickSelector(TickSelector)
*/
public TickSelector getTickSelector() {
return this.tickSelector;
}
/**
* Sets the tick selector and sends an {@link Axis3DChangeEvent} to all
* registered listeners.
*
* @param selector the selector ({@code null} permitted).
*
* @see #getTickSelector()
*/
public void setTickSelector(TickSelector selector) {
this.tickSelector = selector;
fireChangeEvent(false);
}
/**
* Returns the tick size (to be used when the tick selector is
* {@code null}).
*
* @return The tick size.
*/
public double getTickSize() {
return this.tickSize;
}
/**
* Sets the tick size and sends an {@link Axis3DChangeEvent} to all
* registered listeners.
*
* @param tickSize the new tick size.
*/
public void setTickSize(double tickSize) {
this.tickSize = tickSize;
fireChangeEvent(false);
}
/**
* Returns the tick label formatter. The default value is
* {@code DecimalFormat("0.00")}.
*
* @return The tick label formatter (never {@code null}).
*/
public Format getTickLabelFormatter() {
return this.tickLabelFormatter;
}
/**
* Sets the formatter for the tick labels and sends an
* {@link Axis3DChangeEvent} to all registered listeners.
*
* @param formatter the formatter ({@code null} not permitted).
*/
public void setTickLabelFormatter(Format formatter) {
Args.nullNotPermitted(formatter, "formatter");
this.tickLabelFormatter = formatter;
fireChangeEvent(false);
}
/**
* Adjusts the range by adding the lower and upper margins and taking into
* account also the {@code autoRangeStickyZero} flag.
*
* @param range the range ({@code null} not permitted).
*
* @return The adjusted range.
*/
@Override
protected Range adjustedDataRange(Range range) {
Args.nullNotPermitted(range, "range");
double lm = range.getLength() * getLowerMargin();
double um = range.getLength() * getUpperMargin();
double lowerBound = range.getMin() - lm;
double upperBound = range.getMax() + um;
if (this.autoRangeIncludesZero) {
lowerBound = Math.min(lowerBound, 0.0);
upperBound = Math.max(upperBound, 0.0);
}
// does zero fall in the margins?
if (this.autoRangeStickyZero) {
if (0.0 <= range.getMin() && 0.0 > lowerBound) {
lowerBound = 0.0;
}
if (0.0 >= range.getMax() && 0.0 < upperBound) {
upperBound = 0.0;
}
}
if ((upperBound - lowerBound) < getMinAutoRangeLength()) {
double adj = (getMinAutoRangeLength() - (upperBound - lowerBound))
/ 2.0;
lowerBound -= adj;
upperBound += adj;
}
return new Range(lowerBound, upperBound);
}
/**
* Draws the axis to the supplied graphics target ({@code g2}, with the
* specified starting and ending points for the line. This method is used
* internally, you should not need to call it directly.
*
* @param g2 the graphics target ({@code null} not permitted).
* @param pt0 the starting point ({@code null} not permitted).
* @param pt1 the ending point ({@code null} not permitted).
* @param opposingPt an opposing point (to determine which side of the
* axis line the labels should appear, {@code null} not permitted).
* @param tickData tick details ({@code null} not permitted).
* @param info an object to be populated with rendering info
* ({@code null} permitted).
* @param hinting perform element hinting?
*/
@Override
public void draw(Graphics2D g2, Point2D pt0, Point2D pt1,
Point2D opposingPt, List tickData, RenderingInfo info,
boolean hinting) {
if (!isVisible()) {
return;
}
if (pt0.equals(pt1)) {
return;
}
// draw a line for the axis
g2.setStroke(getLineStroke());
g2.setPaint(getLineColor());
Line2D axisLine = new Line2D.Float(pt0, pt1);
g2.draw(axisLine);
// draw the tick marks and labels
g2.setFont(getTickLabelFont());
// we track the max width or height of the labels to know how far to
// offset the axis label when we draw it later
double maxTickLabelDim = 0.0;
if (getTickLabelOrientation().equals(LabelOrientation.PARALLEL)) {
LineMetrics lm = g2.getFontMetrics().getLineMetrics("123", g2);
maxTickLabelDim = lm.getHeight();
}
double tickMarkLength = getTickMarkLength();
double tickLabelOffset = getTickLabelOffset();
g2.setPaint(getTickMarkPaint());
g2.setStroke(getTickMarkStroke());
for (TickData t : tickData) {
if (tickMarkLength > 0.0) {
Line2D tickLine = Utils2D.createPerpendicularLine(axisLine,
t.getAnchorPt(), tickMarkLength, opposingPt);
g2.draw(tickLine);
}
String tickLabel = this.tickLabelFormatter.format(t.getDataValue());
if (getTickLabelOrientation().equals(
LabelOrientation.PERPENDICULAR)) {
maxTickLabelDim = Math.max(maxTickLabelDim,
g2.getFontMetrics().stringWidth(tickLabel));
}
}
if (getTickLabelsVisible()) {
g2.setPaint(getTickLabelColor());
if (getTickLabelOrientation().equals(
LabelOrientation.PERPENDICULAR)) {
drawPerpendicularTickLabels(g2, axisLine, opposingPt, tickData,
info, hinting);
} else {
drawParallelTickLabels(g2, axisLine, opposingPt, tickData,
info, hinting);
}
} else {
maxTickLabelDim = 0.0;
}
// draw the axis label (if any)...
if (getLabel() != null) {
Shape labelBounds = drawAxisLabel(getLabel(), g2, axisLine,
opposingPt, maxTickLabelDim + tickMarkLength
+ tickLabelOffset + getLabelOffset(), info, hinting);
}
}
/**
* Draws tick labels parallel to the axis.
*
* @param g2 the graphics target ({@code null} not permitted).
* @param axisLine the axis line ({@code null} not permitted).
* @param opposingPt an opposing point (to determine on which side the
* labels appear, {@code null} not permitted).
* @param tickData the tick data ({@code null} not permitted).
* @param info if not {@code null} this object will be updated with
* {@link RenderedElement} instances for each of the tick labels.
*/
private void drawParallelTickLabels(Graphics2D g2, Line2D axisLine,
Point2D opposingPt, List tickData, RenderingInfo info,
boolean hinting) {
g2.setFont(getTickLabelFont());
double halfAscent = g2.getFontMetrics().getAscent() / 2.0;
for (TickData t : tickData) {
Line2D perpLine = Utils2D.createPerpendicularLine(axisLine,
t.getAnchorPt(), getTickMarkLength()
+ getTickLabelOffset() + halfAscent, opposingPt);
double axisTheta = Utils2D.calculateTheta(axisLine);
TextAnchor textAnchor = TextAnchor.CENTER;
if (axisTheta >= Math.PI / 2.0) {
axisTheta = axisTheta - Math.PI;
} else if (axisTheta <= -Math.PI / 2) {
axisTheta = axisTheta + Math.PI;
}
String tickLabel = this.tickLabelFormatter.format(
t.getDataValue());
if (hinting) {
Map m = new HashMap<>();
m.put("ref", "{\"type\": \"valueTickLabel\", \"axis\": \""
+ axisStr() + "\", \"value\": \""
+ t.getDataValue() + "\"}");
g2.setRenderingHint(Chart3DHints.KEY_BEGIN_ELEMENT, m);
}
Shape bounds = TextUtils.drawRotatedString(tickLabel, g2,
(float) perpLine.getX2(), (float) perpLine.getY2(),
textAnchor, axisTheta, textAnchor);
if (hinting) {
g2.setRenderingHint(Chart3DHints.KEY_END_ELEMENT, true);
}
if (info != null) {
RenderedElement tickLabelElement = new RenderedElement(
InteractiveElementType.VALUE_AXIS_TICK_LABEL, bounds);
tickLabelElement.setProperty("axis", axisStr());
tickLabelElement.setProperty("value", t.getDataValue());
info.addOffsetElement(tickLabelElement);
}
}
}
/**
* Draws tick labels perpendicular to the axis.
*
* @param g2 the graphics target ({@code null} not permitted).
* @param axisLine the axis line ({@code null} not permitted).
* @param opposingPt an opposing point (to determine on which side the
* labels appear, {@code null} not permitted).
* @param tickData the tick data ({@code null} not permitted).
* @param info if not {@code null} this object will be updated with
* {@link RenderedElement} instances for each of the tick labels.
*/
private void drawPerpendicularTickLabels(Graphics2D g2, Line2D axisLine,
Point2D opposingPt, List tickData, RenderingInfo info,
boolean hinting) {
for (TickData t : tickData) {
double theta = Utils2D.calculateTheta(axisLine);
double thetaAdj = theta + Math.PI / 2.0;
if (thetaAdj < -Math.PI / 2.0) {
thetaAdj = thetaAdj + Math.PI;
}
if (thetaAdj > Math.PI / 2.0) {
thetaAdj = thetaAdj - Math.PI;
}
Line2D perpLine = Utils2D.createPerpendicularLine(axisLine,
t.getAnchorPt(), getTickMarkLength() + getTickLabelOffset(),
opposingPt);
double perpTheta = Utils2D.calculateTheta(perpLine);
TextAnchor textAnchor = TextAnchor.CENTER_LEFT;
if (Math.abs(perpTheta) > Math.PI / 2.0) {
textAnchor = TextAnchor.CENTER_RIGHT;
}
String tickLabel = this.tickLabelFormatter.format(
t.getDataValue());
if (hinting) {
Map m = new HashMap<>();
m.put("ref", "{\"type\": \"valueTickLabel\", \"axis\": \""
+ axisStr() + "\", \"value\": \""
+ t.getDataValue() + "\"}");
g2.setRenderingHint(Chart3DHints.KEY_BEGIN_ELEMENT, m);
}
Shape bounds = TextUtils.drawRotatedString(tickLabel, g2,
(float) perpLine.getX2(), (float) perpLine.getY2(),
textAnchor, thetaAdj, textAnchor);
if (hinting) {
g2.setRenderingHint(Chart3DHints.KEY_END_ELEMENT, true);
}
if (info != null) {
RenderedElement tickLabelElement = new RenderedElement(
InteractiveElementType.VALUE_AXIS_TICK_LABEL, bounds);
tickLabelElement.setProperty("axis", axisStr());
tickLabelElement.setProperty("value", t.getDataValue());
info.addOffsetElement(tickLabelElement);
}
}
}
/**
* Converts a data value to world coordinates, taking into account the
* current axis range (assumes the world axis is zero-based and has the
* specified length).
*
* @param value the data value (in axis units).
* @param length the length of the (zero based) world axis.
*
* @return A world coordinate.
*/
@Override
public double translateToWorld(double value, double length) {
double p = getRange().percent(value, isInverted());
return length * p;
}
/**
* Selects a tick size that is appropriate for drawing the axis from
* {@code pt0} to {@code pt1}.
*
* @param g2 the graphics target ({@code null} not permitted).
* @param pt0 the starting point for the axis.
* @param pt1 the ending point for the axis.
* @param opposingPt a point on the opposite side of the line from where
* the labels should be drawn.
*/
@Override
public double selectTick(Graphics2D g2, Point2D pt0, Point2D pt1,
Point2D opposingPt) {
if (this.tickSelector == null) {
return this.tickSize;
}
g2.setFont(getTickLabelFont());
FontMetrics fm = g2.getFontMetrics(getTickLabelFont());
double length = pt0.distance(pt1);
LabelOrientation orientation = getTickLabelOrientation();
if (orientation.equals(LabelOrientation.PERPENDICULAR)) {
// based on the font height, we can determine roughly how many tick
// labels will fit in the length available
double height = fm.getHeight();
// the tickLabelFactor allows some control over how dense the labels
// will be
int maxTicks = (int) (length / (height * getTickLabelFactor()));
if (maxTicks > 2 && this.tickSelector != null) {
double rangeLength = getRange().getLength();
this.tickSelector.select(rangeLength / 2.0);
// step through until we have too many ticks OR we run out of
// tick sizes
int tickCount = (int) (rangeLength
/ this.tickSelector.getCurrentTickSize());
while (tickCount < maxTicks) {
this.tickSelector.previous();
tickCount = (int) (rangeLength
/ this.tickSelector.getCurrentTickSize());
}
this.tickSelector.next();
this.tickSize = this.tickSelector.getCurrentTickSize();
// TFE, 20180911: don't overwrite any formatter explicitly set
if (DEFAULT_TICK_LABEL_FORMATTER.equals(this.tickLabelFormatter)) {
this.tickLabelFormatter
= this.tickSelector.getCurrentTickLabelFormat();
}
} else {
this.tickSize = Double.NaN;
}
} else if (orientation.equals(LabelOrientation.PARALLEL)) {
// choose a unit that is at least as large as the length of the axis
this.tickSelector.select(getRange().getLength());
boolean done = false;
while (!done) {
if (this.tickSelector.previous()) {
// estimate the label widths, and do they overlap?
Format f = this.tickSelector.getCurrentTickLabelFormat();
String s0 = f.format(this.range.getMin());
String s1 = f.format(this.range.getMax());
double w0 = fm.stringWidth(s0);
double w1 = fm.stringWidth(s1);
double w = Math.max(w0, w1);
int n = (int) (length / (w * this.getTickLabelFactor()));
if (n < getRange().getLength()
/ tickSelector.getCurrentTickSize()) {
tickSelector.next();
done = true;
}
} else {
done = true;
}
}
this.tickSize = this.tickSelector.getCurrentTickSize();
// TFE, 20180911: don't overwrite any formatter explicitly set
if (DEFAULT_TICK_LABEL_FORMATTER.equals(this.tickLabelFormatter)) {
this.tickLabelFormatter
= this.tickSelector.getCurrentTickLabelFormat();
}
}
return this.tickSize;
}
/**
* Generates a list of tick data items for the specified tick unit. This
* data will be passed to the 3D engine and will be updated with a 2D
* projection that can later be used to write the axis tick labels in the
* appropriate places.
*
* If {@code tickUnit} is {@code Double.NaN}, then tick data is
* generated for just the bounds of the axis.
*
* @param tickUnit the tick unit.
*
* @return A list of tick data (never {@code null}).
*/
@Override
public List generateTickData(double tickUnit) {
List result = new ArrayList<>();
if (Double.isNaN(tickUnit)) {
result.add(new TickData(0, getRange().getMin()));
result.add(new TickData(1, getRange().getMax()));
} else {
double x = tickUnit * Math.ceil(this.range.getMin() / tickUnit);
while (x <= this.range.getMax()) {
result.add(new TickData(this.range.percent(x, isInverted()),
x));
x += tickUnit;
}
}
return result;
}
/**
* Tests this instance for equality with an arbitrary object.
*
* @param obj the object to test against ({@code null} permitted).
*
* @return A boolean.
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof NumberAxis3D)) {
return false;
}
NumberAxis3D that = (NumberAxis3D) obj;
if (this.autoRangeIncludesZero != that.autoRangeIncludesZero) {
return false;
}
if (this.autoRangeStickyZero != that.autoRangeStickyZero) {
return false;
}
if (this.tickSize != that.tickSize) {
return false;
}
if (!ObjectUtils.equals(this.tickSelector, that.tickSelector)) {
return false;
}
if (!this.tickLabelFormatter.equals(that.tickLabelFormatter)) {
return false;
}
return super.equals(obj);
}
/**
* Returns a hash code for this instance.
*
* @return A hash code.
*/
@Override
public int hashCode() {
int hash = 3;
hash = 59 * hash + (int) (Double.doubleToLongBits(this.tickSize)
^ (Double.doubleToLongBits(this.tickSize) >>> 32));
hash = 59 * hash + ObjectUtils.hashCode(this.tickLabelFormatter);
return hash;
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/axis/NumberTickSelector.java 0000664 0000000 0000000 00000016022 14173312715 0027362 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.axis;
import java.io.Serializable;
import java.text.DecimalFormat;
import java.text.Format;
import org.jfree.chart3d.internal.Args;
/**
* A {@link TickSelector} implementation that selects tick units in multiples
* of 1, 2 and 5.
*
* NOTE: This class is serializable, but the serialization format is subject
* to change in future releases and should not be relied upon for persisting
* instances of this class.
*/
@SuppressWarnings("serial")
public class NumberTickSelector implements TickSelector, Serializable {
private int power = 0;
private int factor = 1;
/**
* A flag to track if the units are percentage values, in which case the
* formatter should display less decimal places.
*/
private boolean percentage;
/**
* Creates a new instance.
*/
public NumberTickSelector() {
this(false);
}
/**
* Creates a new instance, with the option to display the tick values as
* percentages. The axis follows the normal convention that values in the
* range 0.0 to 1.0 a represented as 0% to 100%.
*
* @param percentage format the tick values as percentages.
*/
public NumberTickSelector(boolean percentage) {
this.power = 0;
this.factor = 1;
this.percentage = percentage;
}
/**
* Selects and returns a standard tick size that is greater than or equal to
* the specified reference value and, ideally, as close to it as possible
* (to minimise the number of iterations used by axes to determine the tick
* size to use). After a call to this method, the
* {@link #getCurrentTickSize()} method should return the selected tick
* size (there is a "pointer" to this tick size), the {@link #next()}
* method should move the pointer to the next (larger) standard tick size,
* and the {@link #previous()} method should move the pointer to the
* previous (smaller) standard tick size.
*
* @param reference the reference value (must be positive and finite).
*
* @return The selected tick size.
*/
@Override
public double select(double reference) {
Args.finitePositiveRequired(reference, "reference");
this.power = (int) Math.ceil(Math.log10(reference));
this.factor = 1;
return getCurrentTickSize();
}
/**
* Move the cursor to the next (larger) tick size, if there is one.
* Returns {@code true} in the case that the cursor is moved, and
* {@code false} where there are a finite number of tick sizes and the
* current tick size is the largest available.
*/
@Override
public boolean next() {
if (factor == 1) {
factor = 2;
return true;
}
if (factor == 2) {
factor = 5;
return true;
}
if (factor == 5) {
power++;
factor = 1;
return true;
}
throw new IllegalStateException("We should never get here.");
}
/**
* Move the cursor to the previous (smaller) tick size, if there is one.
* Returns {@code true} in the case that the cursor is moved, and
* {@code false} where there are a finite number of tick sizes and the
* current tick size is the smallest available.
*/
@Override
public boolean previous() {
if (factor == 1) {
factor = 5;
power--;
return true;
}
if (factor == 2) {
factor = 1;
return true;
}
if (factor == 5) {
factor = 2;
return true;
}
throw new IllegalStateException("We should never get here.");
}
@Override
public double getCurrentTickSize() {
return this.factor * Math.pow(10.0, this.power);
}
private final DecimalFormat dfNeg4 = new DecimalFormat("0.0000");
private final DecimalFormat dfNeg3 = new DecimalFormat("0.000");
private final DecimalFormat dfNeg2 = new DecimalFormat("0.00");
private final DecimalFormat dfNeg1 = new DecimalFormat("0.0");
private final DecimalFormat df0 = new DecimalFormat("#,##0");
private final DecimalFormat dfNeg4P = new DecimalFormat("0.00%");
private final DecimalFormat dfNeg3P = new DecimalFormat("0.0%");
private final DecimalFormat dfNeg2P = new DecimalFormat("0%");
private final DecimalFormat dfNeg1P = new DecimalFormat("0%");
private final DecimalFormat df0P = new DecimalFormat("#,##0%");
@Override
public Format getCurrentTickLabelFormat() {
if (power == -4) {
return this.percentage ? dfNeg4P : dfNeg4;
}
if (power == -3) {
return this.percentage ? dfNeg3P : dfNeg3;
}
if (power == -2) {
return this.percentage ? dfNeg2P : dfNeg2;
}
if (power == -1) {
return this.percentage ? dfNeg1P : dfNeg1;
}
if (power >= 0 && power <= 6) {
return this.percentage ? df0P : df0;
}
return this.percentage ? new DecimalFormat("0.0000E0%")
: new DecimalFormat("0.0000E0");
}
/**
* Tests this instance for equality with an arbitrary object.
*
* @param obj the object ({@code null} permitted).
*
* @return A boolean.
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof NumberTickSelector)) {
return false;
}
NumberTickSelector that = (NumberTickSelector) obj;
if (this.percentage != that.percentage) {
return false;
}
return true;
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/axis/StandardCategoryAxis3D.java 0000664 0000000 0000000 00000114501 14173312715 0030071 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.axis;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.font.LineMetrics;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import org.jfree.chart3d.graphics3d.internal.Utils2D;
import org.jfree.chart3d.Chart3DHints;
import org.jfree.chart3d.ChartElementVisitor;
import org.jfree.chart3d.data.Range;
import org.jfree.chart3d.data.category.CategoryDataset3D;
import org.jfree.chart3d.graphics2d.TextAnchor;
import org.jfree.chart3d.graphics3d.RenderedElement;
import org.jfree.chart3d.graphics3d.RenderingInfo;
import org.jfree.chart3d.interaction.InteractiveElementType;
import org.jfree.chart3d.internal.Args;
import org.jfree.chart3d.internal.ObjectUtils;
import org.jfree.chart3d.internal.SerialUtils;
import org.jfree.chart3d.internal.TextUtils;
import org.jfree.chart3d.label.CategoryLabelGenerator;
import org.jfree.chart3d.label.StandardCategoryLabelGenerator;
import org.jfree.chart3d.marker.CategoryMarker;
import org.jfree.chart3d.marker.CategoryMarkerType;
import org.jfree.chart3d.marker.Marker;
import org.jfree.chart3d.marker.MarkerData;
import org.jfree.chart3d.plot.CategoryPlot3D;
import org.jfree.chart3d.renderer.category.AreaRenderer3D;
/**
* An axis that displays categories.
*
* NOTE: This class is serializable, but the serialization format is subject
* to change in future releases and should not be relied upon for persisting
* instances of this class.
*/
@SuppressWarnings("serial")
public class StandardCategoryAxis3D extends AbstractAxis3D
implements CategoryAxis3D, Serializable {
/** The categories. */
private List> categories;
/**
* The axis range (never {@code null}).
*/
private Range range;
private boolean inverted;
/** The percentage margin to leave at the lower end of the axis. */
private double lowerMargin;
/** The percentage margin to leave at the upper end of the axis. */
private double upperMargin;
/**
* Hide half of the first category? This brings the category label
* closer to the beginning of the axis. It is useful if the renderer
* doesn't make full use of the category space for the first item.
*/
private boolean firstCategoryHalfWidth = false;
/**
* Hide half of the last category? This brings the category label
* closer to the end of the axis. It is useful if the renderer
* doesn't make full use of the category space for the last item.
*/
private boolean lastCategoryHalfWidth = false;
/**
* The tick mark length (in Java2D units). When this is 0.0, no tick
* marks will be drawn.
*/
private double tickMarkLength;
/** The tick mark stroke (never {@code null}). */
private transient Stroke tickMarkStroke;
/** The tick mark paint (never {@code null}). */
private transient Paint tickMarkPaint;
/** The tick label generator. */
private CategoryLabelGenerator tickLabelGenerator;
/**
* The tick label offset (in Java2D units). This is the gap between the
* tick marks and their associated labels.
*/
private double tickLabelOffset;
/** The orientation for the tick labels. */
private LabelOrientation tickLabelOrientation;
/**
* The maximum number of offset levels to use for tick labels on the axis.
*/
private int maxTickLabelLevels = 3;
/**
* The tick label factor (used as a multiplier for the tick label width
* when checking for overlapping labels).
*/
private double tickLabelFactor = 1.2;
/**
* The markers for the axis (this may be empty, but not {@code null}).
*/
private Map markers;
/** A flag to indicate that this axis has been configured as a row axis. */
private boolean isRowAxis;
/**
* A flag to indicate that this axis has been configured as a column
* axis.
*/
private boolean isColumnAxis;
/**
* Default constructor.
*/
public StandardCategoryAxis3D() {
this(null);
}
/**
* Creates a new axis with the specified label.
*
* @param label the axis label ({@code null} permitted).
*/
public StandardCategoryAxis3D(String label) {
super(label);
this.categories = new ArrayList<>();
this.range = new Range(0.0, 1.0);
this.lowerMargin = 0.05;
this.upperMargin = 0.05;
this.firstCategoryHalfWidth = false;
this.lastCategoryHalfWidth = false;
this.tickMarkLength = 3.0;
this.tickMarkPaint = Color.GRAY;
this.tickMarkStroke = new BasicStroke(0.5f);
this.tickLabelGenerator = new StandardCategoryLabelGenerator();
this.tickLabelOffset = 5.0;
this.tickLabelOrientation = LabelOrientation.PARALLEL;
this.tickLabelFactor = 1.4;
this.maxTickLabelLevels = 3;
this.markers = new LinkedHashMap<>();
this.isRowAxis = false;
this.isColumnAxis = false;
}
/**
* Returns {@code true} if this axis has been configured as a
* row axis for the plot that it belongs to, and {@code false}
* otherwise.
*
* @return A boolean.
*
* @since 1.3
*/
@Override
public boolean isRowAxis() {
return isRowAxis;
}
/**
* Returns {@code true} if this axis has been configured as a
* column axis for the plot that it belongs to, and {@code false}
* otherwise.
*
* @return A boolean.
*
* @since 1.3
*/
@Override
public boolean isColumnAxis() {
return isColumnAxis;
}
/**
* Returns the range for the axis. By convention, the category axes have
* a range from 0.0 to 1.0.
*
* @return The range.
*/
@Override
public Range getRange() {
return this.range;
}
/**
* Sets the range for the axis and sends an {@link Axis3DChangeEvent} to
* all registered listeners.
*
* @param lowerBound the lower bound.
* @param upperBound the upper bound.
*/
@Override
public void setRange(double lowerBound, double upperBound) {
setRange(new Range(lowerBound, upperBound));
}
/**
* Sets the range for the axis and sends an {@link Axis3DChangeEvent} to
* all registered listeners. Note that changing the range for the
* category axis will have no visible effect.
*
* @param range the range ({@code null} not permitted).
*/
@Override
public void setRange(Range range) {
Args.nullNotPermitted(range, "range");
this.range = range;
fireChangeEvent(true);
}
/**
* Returns the margin to leave at the lower end of the axis, as a
* percentage of the axis length. The default is {@code 0.05} (five
* percent).
*
* @return The lower margin.
*/
public double getLowerMargin() {
return this.lowerMargin;
}
/**
* Sets the margin to leave at the lower end of the axis and sends an
* {@link Axis3DChangeEvent} to all registered listeners.
*
* @param margin the margin.
*/
public void setLowerMargin(double margin) {
this.lowerMargin = margin;
fireChangeEvent(true);
}
/**
* Returns the margin to leave at the upper end of the axis, as a
* percentage of the axis length. The default is {@code 0.05} (five
* percent).
*
* @return The lower margin.
*/
public double getUpperMargin() {
return this.upperMargin;
}
/**
* Sets the margin to leave at the upper end of the axis and sends an
* {@link Axis3DChangeEvent} to all registered listeners.
*
* @param margin the margin.
*/
public void setUpperMargin(double margin) {
this.upperMargin = margin;
fireChangeEvent(true);
}
/**
* Returns {@code true} if the first category on the axis should
* occupy half the normal width, and {@code false} otherwise.
*
* @return A boolean.
*
* @see #setFirstCategoryHalfWidth(boolean)
*/
public boolean isFirstCategoryHalfWidth() {
return this.firstCategoryHalfWidth;
}
/**
* Sets the flag that controls whether the first category on the axis
* occupies a full or half width, and sends an {@link Axis3DChangeEvent}
* to all registered listeners. There are some renderers where the
* charts look better when half-widths are used (for example,
* {@link AreaRenderer3D}).
*
* @param half half width?
*
* @see #setLastCategoryHalfWidth(boolean)
*/
public void setFirstCategoryHalfWidth(boolean half) {
this.firstCategoryHalfWidth = half;
fireChangeEvent(true);
}
/**
* Returns {@code true} if the last category on the axis should
* occupy half the normal width, and {@code false} otherwise.
*
* @return A boolean.
*
* @see #setLastCategoryHalfWidth(boolean)
*/
public boolean isLastCategoryHalfWidth() {
return this.lastCategoryHalfWidth;
}
/**
* Sets the flag that controls whether the last category on the axis
* occupies a full or half width, and sends an {@link Axis3DChangeEvent}
* to all registered listeners. There are some renderers where the
* charts look better when half-widths are used (for example,
* {@link AreaRenderer3D}).
*
* @param half half width?
*
* @see #setFirstCategoryHalfWidth(boolean)
*/
public void setLastCategoryHalfWidth(boolean half) {
this.lastCategoryHalfWidth = half;
fireChangeEvent(true);
}
/**
* Returns the tick mark length (in Java2D units). The default value
* is {@code 3.0}.
*
* @return The tick mark length.
*/
public double getTickMarkLength() {
return this.tickMarkLength;
}
/**
* Sets the tick mark length (in Java2D units) and sends an
* {@link Axis3DChangeEvent} to all registered listeners. You can set
* the length to {@code 0.0} if you don't want any tick marks on the
* axis.
*
* @param length the length (in Java2D units).
*/
public void setTickMarkLength(double length) {
this.tickMarkLength = length;
fireChangeEvent(false);
}
/**
* Returns the paint used to draw the tick marks, if they are visible.
* The default value is {@code Color.GRAY}.
*
* @return The paint used to draw the tick marks (never {@code null}).
*/
public Paint getTickMarkPaint() {
return this.tickMarkPaint;
}
/**
* Sets the paint used to draw the tick marks and sends an
* {@link Axis3DChangeEvent} to all registered listeners.
*
* @param paint the paint ({@code null} not permitted).
*/
public void setTickMarkPaint(Paint paint) {
Args.nullNotPermitted(paint, "paint");
this.tickMarkPaint = paint;
fireChangeEvent(false);
}
/**
* Returns the stroke used to draw the tick marks, if they are visible.
* The default value is {@code new BasicStroke(0.5f)}.
*
* @return The stroke used to draw the tick marks (never {@code null}).
*/
public Stroke getTickMarkStroke() {
return this.tickMarkStroke;
}
/**
* Sets the stroke used to draw the tick marks and sends an
* {@link Axis3DChangeEvent} to all registered listeners.
*
* @param stroke the stroke ({@code null} not permitted).
*/
public void setTickMarkStroke(Stroke stroke) {
Args.nullNotPermitted(stroke, "stroke");
this.tickMarkStroke = stroke;
fireChangeEvent(false);
}
/**
* Returns the tick label generator for the axis. This is an object that
* is responsible for creating the category labels on the axis. You can
* plug in your own instance to take full control over the generation
* of category labels.
*
* @return The tick label generator for the axis (never {@code null}).
*
* @since 1.2
*/
public CategoryLabelGenerator getTickLabelGenerator() {
return this.tickLabelGenerator;
}
/**
* Sets the tick label generator for the axis and sends a change event to
* all registered listeners.
*
* @param generator the generator ({@code null} not permitted).
*
* @since 1.2
*/
public void setTickLabelGenerator(CategoryLabelGenerator generator) {
Args.nullNotPermitted(generator, "generator");
this.tickLabelGenerator = generator;
fireChangeEvent(false);
}
/**
* Returns the offset between the tick marks and the tick labels. The
* default value is {@code 5.0}.
*
* @return The offset between the tick marks and the tick labels (in Java2D
* units).
*/
public double getTickLabelOffset() {
return this.tickLabelOffset;
}
/**
* Sets the offset between the tick marks and the tick labels and sends
* a {@link Axis3DChangeEvent} to all registered listeners.
*
* @param offset the offset.
*/
public void setTickLabelOffset(double offset) {
this.tickLabelOffset = offset;
fireChangeEvent(false);
}
/**
* Returns the orientation for the tick labels. The default value is
* {@link LabelOrientation#PARALLEL}.
*
* @return The orientation for the tick labels (never {@code null}).
*
* @since 1.2
*/
public LabelOrientation getTickLabelOrientation() {
return this.tickLabelOrientation;
}
/**
* Sets the orientation for the tick labels and sends a change event to
* all registered listeners.
*
* @param orientation the orientation ({@code null} not permitted).
*
* @since 1.2
*/
public void setTickLabelOrientation(LabelOrientation orientation) {
Args.nullNotPermitted(orientation, "orientation");
this.tickLabelOrientation = orientation;
fireChangeEvent(false);
}
/**
* Returns the maximum number of offset levels for the category labels on
* the axis. The default value is 3.
*
* @return The maximum number of offset levels.
*
* @since 1.2
*/
public int getMaxTickLabelLevels() {
return this.maxTickLabelLevels;
}
/**
* Sets the maximum number of offset levels for the category labels on the
* axis and sends a change event to all registered listeners.
*
* @param levels the maximum number of levels.
*
* @since 1.2
*/
public void setMaxTickLabelLevels(int levels) {
this.maxTickLabelLevels = levels;
fireChangeEvent(false);
}
/**
* Returns the tick label factor. The default value is {@code 1.4}.
*
* @return The tick label factor.
*
* @since 1.2
*/
public double getTickLabelFactor() {
return this.tickLabelFactor;
}
/**
* Sets the tick label factor and sends a change event to all registered
* listeners.
*
* @param factor the new factor (should be at least 1.0).
*
* @since 1.2
*/
public void setTickLabelFactor(double factor) {
this.tickLabelFactor = factor;
fireChangeEvent(false);
}
/**
* Returns the marker with the specified key, if there is one.
*
* @param key the key ({@code null} not permitted).
*
* @return The marker (possibly {@code null}).
*
* @since 1.2
*/
@Override
public CategoryMarker getMarker(String key) {
return this.markers.get(key);
}
/**
* Sets the marker for the specified key and sends a change event to
* all registered listeners. If there is an existing marker it is replaced
* (and the axis will no longer listen for change events on the previous
* marker).
*
* @param key the key that identifies the marker ({@code null} not
* permitted).
* @param marker the marker ({@code null} permitted).
*
* @since 1.2
*/
public void setMarker(String key, CategoryMarker marker) {
CategoryMarker existing = this.markers.get(key);
if (existing != null) {
existing.removeChangeListener(this);
}
this.markers.put(key, marker);
if (marker != null) {
marker.addChangeListener(this);
}
fireChangeEvent(false);
}
/**
* Returns a new map containing the markers that are assigned to this axis.
*
* @return A map.
*
* @since 1.2
*/
public Map getMarkers() {
return new LinkedHashMap<>(this.markers);
}
/**
* Returns the width of a single category in the units of the axis
* range.
*
* @return The width of a single category.
*/
@Override
public double getCategoryWidth() {
double length = this.range.getLength();
double start = this.range.getMin() + (this.lowerMargin * length);
double end = this.range.getMax() - (this.upperMargin * length);
double available = (end - start);
return available / this.categories.size();
}
/**
* Configures the axis to be used as a row axis for the specified
* plot. This method is for internal use, you should not call it directly.
*
* @param plot the plot ({@code null} not permitted).
*/
@Override @SuppressWarnings("unchecked")
public void configureAsRowAxis(CategoryPlot3D plot) {
Args.nullNotPermitted(plot, "plot");
this.categories = plot.getDataset().getRowKeys();
this.isColumnAxis = false;
this.isRowAxis = true;
}
/**
* Configures the axis to be used as a column axis for the specified
* plot. This method is for internal use, you won't normally need to call
* it directly.
*
* @param plot the plot ({@code null} not permitted).
*/
@Override @SuppressWarnings("unchecked")
public void configureAsColumnAxis(CategoryPlot3D plot) {
Args.nullNotPermitted(plot, "plot");
this.categories = plot.getDataset().getColumnKeys();
this.isColumnAxis = true;
this.isRowAxis = false;
}
/**
* Returns the value for the specified category, or {@code Double.NaN}
* if the category is not registered on the axis.
*
* @param category the category ({@code null} not permitted).
*
* @return The value.
*/
@Override
public double getCategoryValue(Comparable> category) {
int index = this.categories.indexOf(category);
if (index < 0) {
return Double.NaN;
}
double length = this.range.getLength();
double start = this.range.getMin() + (this.lowerMargin * length);
double end = this.range.getMax() - (this.upperMargin * length);
double available = (end - start);
double categoryCount = this.categories.size();
if (categoryCount == 1) {
return (start + end) / 2.0;
}
if (this.firstCategoryHalfWidth) {
categoryCount -= 0.5;
}
if (this.lastCategoryHalfWidth) {
categoryCount -= 0.5;
}
double categoryWidth = 0.0;
if (categoryCount > 0.0) {
categoryWidth = available / categoryCount;
}
double adj = this.firstCategoryHalfWidth ? 0.0 : 0.5;
return start + (adj + index) * categoryWidth;
}
/**
* Translates a value on the axis to the equivalent coordinate in the
* 3D world used to construct a model of the chart.
*
* @param value the value along the axis.
* @param length the length of one side of the 3D box containing the model.
*
* @return A coordinate in 3D space.
*/
@Override
public double translateToWorld(double value, double length) {
double p = getRange().percent(value, isInverted());
return length * p;
}
/**
* Draws the axis between the two points {@code pt0} and {@code pt1} in
* Java2D space.
*
* @param g2 the graphics target ({@code null} not permitted).
* @param pt0 the starting point for the axis ({@code null} not
* permitted).
* @param pt1 the ending point for the axis ({@code null} not
* permitted).
* @param opposingPt a point on the opposite side of the line from the
* labels ({@code null} not permitted).
* @param tickData the tick data, contains positioning anchors calculated
* by the 3D engine ({@code null} not permitted).
* @param info an object to be populated with rendering info
* ({@code null} permitted).
* @param hinting perform element hinting?
*/
@Override
public void draw(Graphics2D g2, Point2D pt0, Point2D pt1,
Point2D opposingPt, List tickData, RenderingInfo info,
boolean hinting) {
if (!isVisible()) {
return;
}
if (pt0.equals(pt1)) { // if the axis starts and ends on the same point
return; // there is nothing we can draw
}
// draw the axis line (if you want no line, setting the line color
// to fully transparent will achieve this)
g2.setStroke(getLineStroke());
g2.setPaint(getLineColor());
Line2D axisLine = new Line2D.Float(pt0, pt1);
g2.draw(axisLine);
// draw the tick marks - during this pass we will also find the maximum
// tick label width
g2.setPaint(this.tickMarkPaint);
g2.setStroke(this.tickMarkStroke);
g2.setFont(getTickLabelFont());
double maxTickLabelWidth = 0.0;
for (TickData t : tickData) {
if (this.tickMarkLength > 0.0) {
Line2D tickLine = Utils2D.createPerpendicularLine(axisLine,
t.getAnchorPt(), this.tickMarkLength, opposingPt);
g2.draw(tickLine);
}
String tickLabel = t.getKeyLabel();
maxTickLabelWidth = Math.max(maxTickLabelWidth,
g2.getFontMetrics().stringWidth(tickLabel));
}
double maxTickLabelDim = maxTickLabelWidth;
if (getTickLabelsVisible()) {
g2.setPaint(getTickLabelColor());
if (this.tickLabelOrientation.equals(
LabelOrientation.PERPENDICULAR)) {
drawPerpendicularTickLabels(g2, axisLine, opposingPt, tickData,
info, hinting);
} else if (this.tickLabelOrientation.equals(
LabelOrientation.PARALLEL)) {
maxTickLabelDim = drawParallelTickLabels(g2, axisLine,
opposingPt, tickData, maxTickLabelWidth, info, hinting);
}
} else {
maxTickLabelDim = 0.0;
}
// draw the axis label if there is one
if (getLabel() != null) {
Shape labelBounds = drawAxisLabel(getLabel(), g2, axisLine,
opposingPt, maxTickLabelDim + this.tickMarkLength
+ this.tickLabelOffset + getLabelOffset(), info, hinting);
}
}
/**
* Returns "row" if the axis has been configured as a row axis, "column" if
* the axis has been configured as a column axis, and the empty string ("")
* if the axis has not yet been configured.
*
* @return A string (never {@code null}).
*
* @since 1.3
*/
@Override
protected String axisStr() {
String result = "";
if (this.isRowAxis) {
result = "row";
} else if (this.isColumnAxis) {
result = "column";
}
return result;
}
private double drawParallelTickLabels(Graphics2D g2, Line2D axisLine,
Point2D opposingPt, List tickData,
double maxTickLabelWidth, RenderingInfo info, boolean hinting) {
int levels = 1;
LineMetrics lm = g2.getFontMetrics().getLineMetrics("123", g2);
double height = lm.getHeight();
if (tickData.size() > 1) {
// work out how many offset levels we need to display the
// categories without overlapping
Point2D p0 = tickData.get(0).getAnchorPt();
Point2D pN = tickData.get(tickData.size() - 1).getAnchorPt();
double availableWidth = pN.distance(p0)
* tickData.size() / (tickData.size() - 1);
int labelsPerLevel = (int) Math.floor(availableWidth /
(maxTickLabelWidth * tickLabelFactor));
int levelsRequired = this.maxTickLabelLevels;
if (labelsPerLevel > 0) {
levelsRequired = this.categories.size() / labelsPerLevel + 1;
}
levels = Math.min(levelsRequired, this.maxTickLabelLevels);
}
int index = 0;
for (TickData t : tickData) {
int level = index % levels;
double adj = height * (level + 0.5);
Line2D perpLine = Utils2D.createPerpendicularLine(axisLine,
t.getAnchorPt(), this.tickMarkLength
+ this.tickLabelOffset + adj, opposingPt);
double axisTheta = Utils2D.calculateTheta(axisLine);
TextAnchor textAnchor = TextAnchor.CENTER;
if (axisTheta >= Math.PI / 2.0) {
axisTheta = axisTheta - Math.PI;
} else if (axisTheta <= -Math.PI / 2) {
axisTheta = axisTheta + Math.PI;
}
String tickLabel = t.getKeyLabel();
if (hinting) {
Map m = new HashMap<>();
m.put("ref", "{\"type\": \"categoryTickLabel\", \"axis\": \""
+ axisStr() + "\", \"key\": \""
+ t.getKey() + "\"}");
g2.setRenderingHint(Chart3DHints.KEY_BEGIN_ELEMENT, m);
}
Shape bounds = TextUtils.drawRotatedString(tickLabel, g2,
(float) perpLine.getX2(), (float) perpLine.getY2(),
textAnchor, axisTheta, textAnchor);
if (hinting) {
g2.setRenderingHint(Chart3DHints.KEY_END_ELEMENT, true);
}
if (info != null) {
RenderedElement tickLabelElement = new RenderedElement(
InteractiveElementType.CATEGORY_AXIS_TICK_LABEL, bounds);
tickLabelElement.setProperty("label", tickLabel);
tickLabelElement.setProperty("axis", axisStr());
info.addOffsetElement(tickLabelElement);
}
index++;
}
return height * levels;
}
/**
* Draws the category labels perpendicular to the axis.
*
* @param g2 the graphics target.
* @param axisLine the axis line.
* @param opposingPt an opposing point (used to indicate which side the
* labels will appear on).
* @param tickData the tick data.
* @param info if not {@code null} this will be populated with
* {@link RenderedElement} instances for the tick labels.
* @param hinting
*/
@SuppressWarnings("unchecked")
private void drawPerpendicularTickLabels(Graphics2D g2, Line2D axisLine,
Point2D opposingPt, List tickData, RenderingInfo info,
boolean hinting) {
for (TickData t : tickData) {
Line2D perpLine = Utils2D.createPerpendicularLine(axisLine,
t.getAnchorPt(), this.tickMarkLength
+ this.tickLabelOffset, opposingPt);
double perpTheta = Utils2D.calculateTheta(perpLine);
TextAnchor textAnchor = TextAnchor.CENTER_LEFT;
if (perpTheta >= Math.PI / 2.0) {
perpTheta = perpTheta - Math.PI;
textAnchor = TextAnchor.CENTER_RIGHT;
} else if (perpTheta <= -Math.PI / 2) {
perpTheta = perpTheta + Math.PI;
textAnchor = TextAnchor.CENTER_RIGHT;
}
String tickLabel = t.getKeyLabel();
if (hinting) {
Map m = new HashMap();
m.put("ref", "{\"type\": \"categoryAxisLabel\", \"axis\": \""
+ axisStr() + "\", \"key\": \""
+ t.getKey() + "\"}");
g2.setRenderingHint(Chart3DHints.KEY_BEGIN_ELEMENT, m);
}
Shape bounds = TextUtils.drawRotatedString(tickLabel, g2,
(float) perpLine.getX2(), (float) perpLine.getY2(),
textAnchor, perpTheta, textAnchor);
if (hinting) {
g2.setRenderingHint(Chart3DHints.KEY_END_ELEMENT, true);
}
if (info != null) {
RenderedElement tickLabelElement = new RenderedElement(
InteractiveElementType.CATEGORY_AXIS_TICK_LABEL, bounds);
tickLabelElement.setProperty("label", tickLabel);
tickLabelElement.setProperty("axis", axisStr());
info.addOffsetElement(tickLabelElement);
}
}
}
/**
* Generates the tick data for the axis (assumes the axis is being used
* as the row axis). The dataset is passed as an argument to provide the
* opportunity to incorporate dataset-specific info into tick labels (for
* example, a row label might show the total for that row in the dataset)
* ---whether or not this is used depends on the axis implementation.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The tick data.
*
* @since 1.2
*/
@Override @SuppressWarnings("unchecked")
public List generateTickDataForRows(CategoryDataset3D dataset) {
Args.nullNotPermitted(dataset, "dataset");
List result = new ArrayList<>(this.categories.size());
for (Comparable> key : this.categories) {
double pos = this.range.percent(getCategoryValue(key));
String label = this.tickLabelGenerator.generateRowLabel(dataset,
key);
result.add(new TickData(pos, key, label));
}
return result;
}
/**
* Generates the tick data for the axis (assumes the axis is being used
* as the row axis). The dataset is passed as an argument to provide the
* opportunity to incorporate dataset-specific info into tick labels (for
* example, a row label might show the total for that row in the dataset)
* ---whether or not this is used depends on the axis implementation.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The tick data.
*
* @since 1.2
*/
@Override @SuppressWarnings("unchecked")
public List generateTickDataForColumns(
CategoryDataset3D dataset) {
Args.nullNotPermitted(dataset, "dataset");
List result = new ArrayList<>(this.categories.size());
for (Comparable> key : this.categories) {
double pos = this.range.percent(getCategoryValue(key));
String label = this.tickLabelGenerator.generateColumnLabel(dataset,
key);
result.add(new TickData(pos, key, label));
}
return result;
}
/**
* Generates and returns a list of marker data items for the axis.
*
* @return A list of marker data items (never {@code null}).
*/
@Override
public List generateMarkerData() {
List result = new ArrayList<>();
for (Map.Entry entry
: this.markers.entrySet()) {
CategoryMarker cm = entry.getValue();
if (cm == null) {
continue;
}
MarkerData markerData;
if (cm.getType().equals(CategoryMarkerType.LINE)) {
double pos = getCategoryValue(cm.getCategory());
markerData = new MarkerData(entry.getKey(), pos);
markerData.setLabelAnchor(cm.getLabel() != null
? cm.getLabelAnchor() : null);
} else if (cm.getType().equals(CategoryMarkerType.BAND)) {
double pos = getCategoryValue(cm.getCategory());
double width = getCategoryWidth();
markerData = new MarkerData(entry.getKey(), pos - width / 2,
false, pos + width / 2, false);
markerData.setLabelAnchor(cm.getLabel() != null
? cm.getLabelAnchor() : null);
} else {
throw new RuntimeException("Unrecognised marker: "
+ cm.getType());
}
result.add(markerData);
}
return result;
}
/**
* Receives a {@link ChartElementVisitor}. This method is part of a general
* mechanism for traversing the chart structure and performing operations
* on each element in the chart. You will not normally call this method
* directly.
*
* @param visitor the visitor ({@code null} not permitted).
*
* @since 1.2
*/
@Override
public void receive(ChartElementVisitor visitor) {
for (Marker marker : this.markers.values()) {
marker.receive(visitor);
}
visitor.visit(this);
}
/**
* Tests this instance for equality with an arbitrary object.
*
* @param obj the object to test against ({@code null} not permitted).
*
* @return A boolean.
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof StandardCategoryAxis3D)) {
return false;
}
StandardCategoryAxis3D that = (StandardCategoryAxis3D) obj;
if (this.lowerMargin != that.lowerMargin) {
return false;
}
if (this.upperMargin != that.upperMargin) {
return false;
}
if (this.firstCategoryHalfWidth != that.firstCategoryHalfWidth) {
return false;
}
if (this.lastCategoryHalfWidth != that.lastCategoryHalfWidth) {
return false;
}
if (this.tickMarkLength != that.tickMarkLength) {
return false;
}
if (!ObjectUtils.equalsPaint(this.tickMarkPaint, that.tickMarkPaint)) {
return false;
}
if (!this.tickMarkStroke.equals(that.tickMarkStroke)) {
return false;
}
if (!this.tickLabelGenerator.equals(that.tickLabelGenerator)) {
return false;
}
if (this.tickLabelOffset != that.tickLabelOffset) {
return false;
}
if (!this.tickLabelOrientation.equals(that.tickLabelOrientation)) {
return false;
}
if (this.tickLabelFactor != that.tickLabelFactor) {
return false;
}
if (this.maxTickLabelLevels != that.maxTickLabelLevels) {
return false;
}
if (!this.markers.equals(that.markers)) {
return false;
}
return super.equals(obj);
}
/**
* Provides serialization support.
*
* @param stream the output stream.
*
* @throws IOException if there is an I/O error.
*/
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
SerialUtils.writePaint(this.tickMarkPaint, stream);
SerialUtils.writeStroke(this.tickMarkStroke, stream);
}
/**
* Provides serialization support.
*
* @param stream the input stream.
*
* @throws IOException if there is an I/O error.
* @throws ClassNotFoundException if there is a classpath problem.
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
this.tickMarkPaint = SerialUtils.readPaint(stream);
this.tickMarkStroke = SerialUtils.readStroke(stream);
}
/**
* Returns {@code true} if the axis inverts the order of the data items,
* and {@code false} otherwise.
*
* @return A boolean.
*
* @since 1.5
*/
@Override
public boolean isInverted() {
return this.inverted;
}
/**
* Sets the flag that controls whether or not the axis inverts the order
* of the data items and sends an {@link Axis3DChangeEvent} to all
* registered listeners.
*
* @param inverted the new flag value.
*
* @since 1.5
*/
@Override
public void setInverted(boolean inverted) {
this.inverted = inverted;
fireChangeEvent(true);
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/axis/TickData.java 0000664 0000000 0000000 00000013061 14173312715 0025302 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.axis;
import java.awt.geom.Point2D;
import org.jfree.chart3d.internal.Args;
/**
* Data related to the tick marks and labels on a chart.
*/
public class TickData {
/** The position along the axis as a percentage. */
private final double pos;
/** The key for the tick (used for CategoryAxis3D, null otherwise). */
private final Comparable> key;
/**
* The label used for the category key (used for CategoryAxis3D, null
* otherwise).
*/
private final String keyLabel;
/** The data value (used for ValueAxis3D). */
private final double dataValue;
/** The vertex in the ChartBox that is the anchor point for this tick. */
private int vertexIndex;
/** The anchor point. */
private Point2D anchorPt;
/**
* Creates a new instance. This constructor is used for category axes.
*
* @param pos the position along the axis as a percentage of the range.
* @param key the key.
* @param keyLabel the key label.
*/
public TickData(double pos, Comparable> key, String keyLabel) {
this.pos = pos;
this.key = key;
this.keyLabel = keyLabel;
this.dataValue = Double.NaN;
this.vertexIndex = -1;
this.anchorPt = null;
}
/**
* Creates a new instance. This constructor is used for numerical axes.
*
* @param pos the position along the axis as a percentage of the range.
* @param dataValue the data value.
*/
public TickData(double pos, double dataValue) {
this.pos = pos;
this.dataValue = dataValue;
this.key = null;
this.keyLabel = null;
this.vertexIndex = -1;
this.anchorPt = null;
}
/**
* Creates a new instance by copying an existing instance but altering
* the vertex index.
*
* @param source a source to copy ({@code null} not permitted).
* @param vertexIndex the vertex index.
*/
public TickData(TickData source, int vertexIndex) {
Args.nullNotPermitted(source, "source");
this.pos = source.pos;
this.dataValue = source.dataValue;
this.key = source.key;
this.keyLabel = source.keyLabel;
this.anchorPt = source.anchorPt;
this.vertexIndex = vertexIndex;
}
/**
* Returns the position of the tick, as a percentage along the axis (for
* example, 0.5 is halfway along the axis).
*
* @return The position.
*/
public double getPos() {
return this.pos;
}
/**
* Returns the key when the tick data is for a {@link CategoryAxis3D},
* and {@code null} otherwise.
*
* @return The key (possibly {@code null}).
*/
public Comparable> getKey() {
return this.key;
}
/**
* Returns the key label.
*
* @return The key label (possibly {@code null}).
*
* @since 1.2
*/
public String getKeyLabel() {
return this.keyLabel;
}
/**
* Returns the data value when the tick data is for a {@link NumberAxis3D},
* and {@code Double.NaN} otherwise.
*
* @return The data value.
*/
public double getDataValue() {
return this.dataValue;
}
/**
* Returns the vertex index that is the anchor point for the tick mark.
*
* @return The vertex index.
*/
public int getVertexIndex() {
return this.vertexIndex;
}
/**
* Sets the vertex index. The vertex is a point in 3D space that is
* the anchor point for the tick mark, after this is projected to
* 2D space we have a reference point for the tick mark.
*
* @param index the index.
*/
public void setVertexIndex(int index) {
this.vertexIndex = index;
}
/**
* Returns the anchor point.
*
* @return The anchor point.
*/
public Point2D getAnchorPt() {
return this.anchorPt;
}
/**
* Sets the anchor point. This is the projected point in 2D space of the
* vertex we track in 3D space.
*
* @param anchorPt the anchor point.
*/
public void setAnchorPt(Point2D anchorPt) {
this.anchorPt = anchorPt;
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/axis/TickSelector.java 0000664 0000000 0000000 00000010106 14173312715 0026206 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.axis;
import java.text.Format;
/**
*
Provides standard tick sizes and formatting for numerical axes.
* Conceptually, the selector maintains a list of standard tick sizes (ordered
* by size), and a pointer to the current (selected) tick size. Clients of
* the selector will initialise the pointer by calling the
* {@link #select(double)} method with a reference value (a guess, based on
* context, of the largest usable tick size - for example, one half of the
* axis length) then, as required, move the pointer to a smaller or larger
* tick size (using the {@link #next()} and {@link #previous()} methods) to
* find the most appropriate standard size to use.
*
*
The {@link NumberTickSelector} class provides a standard implementation,
* but you can create your own if necessary.
*/
public interface TickSelector {
/**
* Selects and returns a standard tick size that is greater than or equal to
* the specified reference value and, ideally, as close to it as possible
* (to minimise the number of iterations used by axes to determine the tick
* size to use). After a call to this method, the
* {@link #getCurrentTickSize()} method should return the selected tick
* size (there is a "cursor" that points to this tick size), the
* {@link #next()} method should move the pointer to the next (larger)
* standard tick size, and the {@link #previous()} method should move the
* pointer to the previous (smaller) standard tick size.
*
* @param reference the reference value (must be positive and finite).
*
* @return The selected tick size.
*/
double select(double reference);
/**
* Move the cursor to the next (larger) tick size, if there is one.
* Returns {@code true} in the case that the cursor is moved, and
* {@code false} where there are a finite number of tick sizes and the
* current tick size is the largest available.
*
* @return A boolean.
*/
boolean next();
/**
* Move the cursor to the previous (smaller) tick size, if there is one.
* Returns {@code true} in the case that the cursor is moved, and
* {@code false} where there are a finite number of tick sizes and the
* current tick size is the smallest available.
*
* @return A boolean.
*/
boolean previous();
/**
* Returns the tick size that the cursor is currently referencing.
*
* @return The tick size.
*/
double getCurrentTickSize();
/**
* Returns the tick formatter associated with the tick size that the
* cursor is currently referencing.
*
* @return The formatter.
*/
Format getCurrentTickLabelFormat();
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/axis/ValueAxis3D.java 0000664 0000000 0000000 00000010625 14173312715 0025711 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.axis;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.util.List;
import org.jfree.chart3d.marker.MarkerData;
import org.jfree.chart3d.marker.ValueMarker;
import org.jfree.chart3d.plot.CategoryPlot3D;
import org.jfree.chart3d.plot.XYZPlot;
/**
* An axis that displays a range of continuous values. These can be used
* for the value axis in a {@link CategoryPlot3D}, and for the X, Y or Z
* axes in an {@link XYZPlot}.
*/
public interface ValueAxis3D extends Axis3D {
/**
* Returns the type of use that the axis has been configured for.
*
* @return The type (or {@code null} if the axis has not yet been
* configured).
*
* @since 1.3
*/
ValueAxis3DType getConfiguredType();
/**
* Configure the axis as a value axis for the specified plot.
*
* @param plot the plot ({@code null} not permitted).
*/
void configureAsValueAxis(CategoryPlot3D plot);
/**
* Configure the axis as an x-axis for the specified plot.
*
* @param plot the plot ({@code null} not permitted).
*/
void configureAsXAxis(XYZPlot plot);
/**
* Configure the axis as a y-axis for the specified plot.
*
* @param plot the plot ({@code null} not permitted).
*/
void configureAsYAxis(XYZPlot plot);
/**
* Configure the axis as an z-axis for the specified plot.
*
* @param plot the plot ({@code null} not permitted).
*/
void configureAsZAxis(XYZPlot plot);
/**
* Selects an appropriate tick size and format for the axis based on
* the axis being rendered from {@code pt0} to {@code pt1}.
*
* @param g2 the graphics target.
* @param pt0 the starting point.
* @param pt1 the ending point.
* @param opposingPt a point on the opposite side of the axis from the
* labels.
*
* @return The tick size.
*/
double selectTick(Graphics2D g2, Point2D pt0, Point2D pt1,
Point2D opposingPt);
/**
* Generates a list of tick data items for the specified tick unit. This
* data will be passed to the 3D engine and will be updated with a 2D
* projection that can later be used to write the axis tick labels in the
* appropriate places.
*
* If {@code tickUnit} is {@code Double.NaN}, then tick data is
* generated for just the bounds of the axis.
*
* @param tickUnit the tick unit.
*
* @return A list of tick data (never {@code null}).
*/
List generateTickData(double tickUnit);
/**
* Returns a list of marker data instances for the markers that fall
* within the current axis range.
*
* @return A list of marker data.
*/
List generateMarkerData();
/**
* Returns the marker with the specified key, if there is one.
*
* @param key the key ({@code null} not permitted).
*
* @return The marker (possibly {@code null}).
*
* @since 1.2
*/
ValueMarker getMarker(String key);
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/axis/ValueAxis3DType.java 0000664 0000000 0000000 00000003537 14173312715 0026557 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.axis;
import org.jfree.chart3d.plot.CategoryPlot3D;
import org.jfree.chart3d.plot.XYZPlot;
/**
* An enumeration of the value axis types. A {@link ValueAxis3D} can be
* used in several different plot dimensions.
*
* @since 1.3
*/
public enum ValueAxis3DType {
/** A value axis in a {@link CategoryPlot3D}. */
VALUE,
/** An x-axis in an {@link XYZPlot}. */
X,
/** A y-axis in an {@link XYZPlot}. */
Y,
/** A z-axis in an {@link XYZPlot}. */
Z
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/axis/package-info.java 0000664 0000000 0000000 00000000356 14173312715 0026145 0 ustar 00root root 0000000 0000000 /**
* Axes ({@link org.jfree.chart3d.axis.CategoryAxis3D},
* {@link org.jfree.chart3d.axis.NumberAxis3D} and
* {@link org.jfree.chart3d.axis.LogAxis3D}) plus supporting classes and
* interfaces.
*/
package org.jfree.chart3d.axis;
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/data/ 0000775 0000000 0000000 00000000000 14173312715 0022717 5 ustar 00root root 0000000 0000000 orson-charts-2.1.0/src/main/java/org/jfree/chart3d/data/AbstractDataset3D.java 0000664 0000000 0000000 00000013401 14173312715 0027021 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.data;
import java.util.Arrays;
import java.util.EventListener;
import java.util.List;
import javax.swing.event.EventListenerList;
/**
* A base class that can be used to create new dataset classes.
*/
public class AbstractDataset3D implements Dataset3D {
/** Storage for registered change listeners. */
private final transient EventListenerList listenerList;
/**
* A flag that controls whether or not the dataset will notify listeners
* of changes (defaults to {@code true}, but sometimes it is useful
* to disable this).
*/
private boolean notify;
/**
* Default constructor - allocates storage for listeners that can
* be registered with the dataset.
*/
protected AbstractDataset3D() {
this.listenerList = new EventListenerList();
this.notify = true;
}
/**
* Returns a flag that controls whether or not change events are sent to
* registered listeners.
*
* @return A boolean.
*
* @see #setNotify(boolean)
*/
public boolean isNotify() {
return this.notify;
}
/**
* Sets a flag that controls whether or not listeners receive
* {@link Dataset3DChangeEvent} notifications.
*
* @param notify a boolean.
*
* @see #isNotify()
*/
public void setNotify(boolean notify) {
this.notify = notify;
// if the flag is being set to true, there may be queued up changes...
if (notify) {
fireChangeEvent();
}
}
/**
* Registers an object to receive notification of changes to the dataset.
*
* @param listener the object to register.
*
* @see #removeChangeListener(Dataset3DChangeListener)
*/
@Override
public void addChangeListener(Dataset3DChangeListener listener) {
this.listenerList.add(Dataset3DChangeListener.class, listener);
}
/**
* Deregisters an object so that it no longer receives notification of
* changes to the dataset.
*
* @param listener the object to deregister.
*
* @see #addChangeListener(Dataset3DChangeListener)
*/
@Override
public void removeChangeListener(Dataset3DChangeListener listener) {
this.listenerList.remove(Dataset3DChangeListener.class, listener);
}
/**
* Returns {@code true} if the specified object is registered with
* the dataset as a listener. Most applications won't need to call this
* method, it exists mainly for use by unit testing code.
*
* @param listener the listener.
*
* @return A boolean.
*
* @see #addChangeListener(Dataset3DChangeListener)
* @see #removeChangeListener(Dataset3DChangeListener)
*/
@Override
public boolean hasListener(EventListener listener) {
List> list = Arrays.asList(this.listenerList.getListenerList());
return list.contains(listener);
}
/**
* Notifies all registered listeners that the dataset has changed.
*
* @see #addChangeListener(Dataset3DChangeListener)
*/
protected void fireDatasetChanged() {
notifyListeners(new Dataset3DChangeEvent(this, this));
}
/**
* Notifies all registered listeners that the dataset has changed, unless
* the {@code notify} flag is set to {@code false} in which
* case this method does nothing.
*
* @param event contains information about the event that triggered the
* notification.
*
* @see #addChangeListener(Dataset3DChangeListener)
* @see #removeChangeListener(Dataset3DChangeListener)
*/
protected void notifyListeners(Dataset3DChangeEvent event) {
// if the 'notify' flag has been switched to false, we don't notify
// the listeners
if (!this.notify) {
return;
}
Object[] listeners = this.listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == Dataset3DChangeListener.class) {
((Dataset3DChangeListener) listeners[i + 1])
.datasetChanged(event);
}
}
}
/**
* Sends a {@link Dataset3DChangeEvent} to all registered listeners, unless
* the {@code notify} flag is set to {@code false} in which
* case this method does nothing.
*/
protected void fireChangeEvent() {
notifyListeners(new Dataset3DChangeEvent(this, this));
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/data/DataUtils.java 0000664 0000000 0000000 00000113044 14173312715 0025457 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.data;
import java.util.List;
import org.jfree.chart3d.data.category.CategoryDataset3D;
import org.jfree.chart3d.data.xyz.XYZDataset;
import org.jfree.chart3d.data.xyz.XYZSeries;
import org.jfree.chart3d.data.xyz.XYZSeriesCollection;
import org.jfree.chart3d.internal.Args;
/**
* Some utility methods for working with the various datasets and data
* structures available in Orson Charts.
*/
public class DataUtils {
private DataUtils() {
// no need to create instances
}
/**
* Returns the total of the values in the list. Any {@code null}
* values are ignored.
*
* @param values the values ({@code null} not permitted).
*
* @return The total of the values in the list.
*/
public static double total(Values values) {
double result = 0.0;
for (int i = 0; i < values.getItemCount(); i++) {
Number n = values.getValue(i);
if (n != null) {
result = result + n.doubleValue();
}
}
return result;
}
/**
* Returns the count of the non-{@code null} entries in the dataset
* for the specified series. An {@code IllegalArgumentException} is
* thrown if the {@code seriesKey} is not present in the data.
*
* @param the series key (must implement Comparable).
* @param data the dataset ({@code null} not permitted).
* @param seriesKey the series key ({@code null} not permitted).
*
* @return The count.
*
* @since 1.2
*/
public static > int count(
KeyedValues3D data, S seriesKey) {
Args.nullNotPermitted(data, "data");
Args.nullNotPermitted(seriesKey, "seriesKey");
int seriesIndex = data.getSeriesIndex(seriesKey);
if (seriesIndex < 0) {
throw new IllegalArgumentException("Series not found: "
+ seriesKey);
}
int count = 0;
int rowCount = data.getRowCount();
int columnCount = data.getColumnCount();
for (int r = 0; r < rowCount; r++) {
for (int c = 0; c < columnCount; c++) {
Number n = (Number) data.getValue(seriesIndex, r, c);
if (n != null) {
count++;
}
}
}
return count;
}
/**
* Returns the count of the non-{@code null} entries in the dataset
* for the specified row (all series).
*
* @param the row key (must implement Comparable).
* @param data the dataset ({@code null} not permitted).
* @param rowKey the row key ({@code null} not permitted).
*
* @return The count.
*
* @since 1.2
*/
public static > int countForRow(
KeyedValues3D, R, ?, ?> data, R rowKey) {
Args.nullNotPermitted(data, "data");
Args.nullNotPermitted(rowKey, "rowKey");
int rowIndex = data.getRowIndex(rowKey);
if (rowIndex < 0) {
throw new IllegalArgumentException("Row not found: " + rowKey);
}
int count = 0;
int seriesCount = data.getSeriesCount();
int columnCount = data.getColumnCount();
for (int s = 0; s < seriesCount; s++) {
for (int c = 0; c < columnCount; c++) {
Number n = (Number) data.getValue(s, rowIndex, c);
if (n != null) {
count++;
}
}
}
return count;
}
/**
* Returns the count of the non-{@code null} entries in the dataset
* for the specified column (all series).
*
* @param the column key (must implement Comparable).
* @param data the dataset ({@code null} not permitted).
* @param columnKey the column key ({@code null} not permitted).
*
* @return The count.
*
* @since 1.2
*/
public static > int countForColumn(
KeyedValues3D, ?, C, ?> data, C columnKey) {
Args.nullNotPermitted(data, "data");
Args.nullNotPermitted(columnKey, "columnKey");
int columnIndex = data.getColumnIndex(columnKey);
if (columnIndex < 0) {
throw new IllegalArgumentException("Column not found: "
+ columnKey);
}
int count = 0;
int seriesCount = data.getSeriesCount();
int rowCount = data.getRowCount();
for (int s = 0; s < seriesCount; s++) {
for (int r = 0; r < rowCount; r++) {
Number n = (Number) data.getValue(s, r, columnIndex);
if (n != null) {
count++;
}
}
}
return count;
}
/**
* Returns the total of the non-{@code null} values in the dataset
* for the specified series. If there is no series with the specified
* key, this method will throw an {@code IllegalArgumentException}.
*
* @param the series key (must implement Comparable).
* @param data the dataset ({@code null} not permitted).
* @param seriesKey the series key ({@code null} not permitted).
*
* @return The total.
*
* @since 1.2
*/
public static > double total(
KeyedValues3D data, S seriesKey) {
Args.nullNotPermitted(data, "data");
Args.nullNotPermitted(seriesKey, "seriesKey");
int seriesIndex = data.getSeriesIndex(seriesKey);
if (seriesIndex < 0) {
throw new IllegalArgumentException("Series not found: "
+ seriesKey);
}
double total = 0.0;
int rowCount = data.getRowCount();
int columnCount = data.getColumnCount();
for (int r = 0; r < rowCount; r++) {
for (int c = 0; c < columnCount; c++) {
Number n = data.getValue(seriesIndex, r, c);
if (n != null) {
total += n.doubleValue();
}
}
}
return total;
}
/**
* Returns the total of the non-{@code null} entries in the dataset
* for the specified row (all series).
*
* @param the row key (must implement Comparable).
* @param data the dataset ({@code null} not permitted).
* @param rowKey the row key ({@code null} not permitted).
*
* @return The total.
*
* @since 1.2
*/
public static > double totalForRow(
KeyedValues3D, R, ?, ? extends Number> data, R rowKey) {
Args.nullNotPermitted(data, "data");
Args.nullNotPermitted(rowKey, "rowKey");
int rowIndex = data.getRowIndex(rowKey);
if (rowIndex < 0) {
throw new IllegalArgumentException("Row not found: " + rowKey);
}
double total = 0.0;
int seriesCount = data.getSeriesCount();
int columnCount = data.getColumnCount();
for (int s = 0; s < seriesCount; s++) {
for (int c = 0; c < columnCount; c++) {
Number n = data.getValue(s, rowIndex, c);
if (n != null) {
total += n.doubleValue();
}
}
}
return total;
}
/**
* Returns the total of the non-{@code null} entries in the dataset
* for the specified column (all series).
*
* @param the column key (must implement Comparable).
* @param data the dataset ({@code null} not permitted).
* @param columnKey the row key ({@code null} not permitted).
*
* @return The total.
*
* @since 1.2
*/
public static > double totalForColumn(
KeyedValues3D, ?, C, ? extends Number> data, C columnKey) {
Args.nullNotPermitted(data, "data");
Args.nullNotPermitted(columnKey, "columnKey");
int columnIndex = data.getColumnIndex(columnKey);
if (columnIndex < 0) {
throw new IllegalArgumentException("Column not found: "
+ columnKey);
}
double total = 0.0;
int seriesCount = data.getSeriesCount();
int rowCount = data.getRowCount();
for (int s = 0; s < seriesCount; s++) {
for (int r = 0; r < rowCount; r++) {
Number n = data.getValue(s, r, columnIndex);
if (n != null) {
total += n.doubleValue();
}
}
}
return total;
}
/**
* Returns the range of values in the specified data structure (a three
* dimensional cube). If there is no data, this method returns
* {@code null}.
*
* @param data the data ({@code null} not permitted).
*
* @return The range of data values (possibly {@code null}).
*/
public static Range findValueRange(Values3D extends Number> data) {
return findValueRange(data, Double.NaN);
}
/**
* Returns the range of values in the specified data cube, or
* {@code null} if there is no data. The range will be expanded, if
* required, to include the {@code base} value (unless it
* is {@code Double.NaN} in which case it is ignored).
*
* @param data the data ({@code null} not permitted).
* @param base a value that must be included in the range (often 0). This
* argument is ignored if it is {@code Double.NaN}.
*
* @return The range (possibly {@code null}).
*/
public static Range findValueRange(Values3D extends Number> data,
double base) {
return findValueRange(data, base, true);
}
/**
/**
* Returns the range of values in the specified data cube, or
* {@code null} if there is no data. The range will be expanded, if
* required, to include the {@code base} value (unless it
* is {@code Double.NaN} in which case it is ignored).
*
* @param data the data ({@code null} not permitted).
* @param base a value that must be included in the range (often 0). This
* argument is ignored if it is {@code Double.NaN}.
* @param finite if {@code true} infinite values will be ignored.
*
* @return The range (possibly {@code null}).
*
* @since 1.4
*/
public static Range findValueRange(Values3D extends Number> data,
double base, boolean finite) {
Args.nullNotPermitted(data, "data");
double min = Double.POSITIVE_INFINITY;
double max = Double.NEGATIVE_INFINITY;
for (int series = 0; series < data.getSeriesCount(); series++) {
for (int row = 0; row < data.getRowCount(); row++) {
for (int col = 0; col < data.getColumnCount(); col++) {
double d = data.getDoubleValue(series, row, col);
if (!Double.isNaN(d)) {
if (!finite || !Double.isInfinite(d)) {
min = Math.min(min, d);
max = Math.max(max, d);
}
}
}
}
}
// include the special value in the range
if (!Double.isNaN(base)) {
min = Math.min(min, base);
max = Math.max(max, base);
}
if (min <= max) {
return new Range(min, max);
} else {
return null;
}
}
/**
* Finds the range of values in the dataset considering that each series
* is stacked on top of the other.
*
* @param data the data ({@code null} not permitted).
*
* @return The range.
*/
public static Range findStackedValueRange(Values3D extends Number> data) {
return findStackedValueRange(data, 0.0);
}
/**
* Finds the range of values in the dataset considering that each series
* is stacked on top of the others, starting at the base value.
*
* @param data the data values ({@code null} not permitted).
* @param base the base value.
*
* @return The range.
*/
public static Range findStackedValueRange(Values3D extends Number> data,
double base) {
Args.nullNotPermitted(data, "data");
double min = base;
double max = base;
int seriesCount = data.getSeriesCount();
for (int row = 0; row < data.getRowCount(); row++) {
for (int col = 0; col < data.getColumnCount(); col++) {
double[] total = stackSubTotal(data, base, seriesCount, row,
col);
min = Math.min(min, total[0]);
max = Math.max(max, total[1]);
}
}
if (min <= max) {
return new Range(min, max);
} else {
return null;
}
}
/**
* Returns the positive and negative subtotals of the values for all the
* series preceding the specified series.
*
* One application for this method is to compute the base values for
* individual bars in a stacked bar chart.
*
* @param data the data ({@code null} not permitted).
* @param base the initial base value (normally {@code 0.0}, but the
* values can be stacked from a different starting point).
* @param series the index of the current series (series with lower indices
* are included in the sub-totals).
* @param row the row index of the required item.
* @param column the column index of the required item.
*
* @return The subtotals, where {@code result[0]} is the subtotal of
* the negative data items, and {@code result[1]} is the subtotal
* of the positive data items.
*/
public static double[] stackSubTotal(Values3D extends Number> data,
double base, int series, int row, int column) {
double neg = base;
double pos = base;
for (int s = 0; s < series; s++) {
double v = data.getDoubleValue(s, row, column);
if (v > 0.0) {
pos = pos + v;
} else if (v < 0.0) {
neg = neg + v;
}
}
return new double[] { neg, pos };
}
/**
* Returns the total of the non-{@code NaN} entries in the dataset
* for the specified series.
*
* @param the series key (must implement Comparable).
* @param data the dataset ({@code null} not permitted).
* @param seriesKey the series key ({@code null} not permitted).
*
* @return The count.
*
* @since 1.2
*/
public static > double total(XYZDataset data,
S seriesKey) {
Args.nullNotPermitted(data, "data");
Args.nullNotPermitted(seriesKey, "seriesKey");
int seriesIndex = data.getSeriesIndex(seriesKey);
if (seriesIndex < 0) {
throw new IllegalArgumentException("Series not found: "
+ seriesKey);
}
double total = 0;
int itemCount = data.getItemCount(seriesIndex);
for (int item = 0; item < itemCount; item++) {
double y = data.getY(seriesIndex, item);
if (!Double.isNaN(y)) {
total += y;
}
}
return total;
}
/**
* Returns the range of x-values in the dataset by iterating over all
* values (and ignoring {@code Double.NaN} and infinite values).
* If there are no values eligible for inclusion in the range, this method
* returns {@code null}.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The range (possibly {@code null}).
*/
public static Range findXRange(XYZDataset dataset) {
return findXRange(dataset, Double.NaN);
}
/**
* Returns the range of x-values in the dataset by iterating over all
* values (and ignoring {@code Double.NaN} values). The range will be
* extended if necessary to include {@code inc} (unless it is
* {@code Double.NaN} in which case it is ignored). Infinite values
* in the dataset will be ignored. If there are no values eligible for
* inclusion in the range, this method returns {@code null}.
*
* @param dataset the dataset ({@code null} not permitted).
* @param inc an additional x-value to include.
*
* @return The range (possibly {@code null}).
*/
public static Range findXRange(XYZDataset dataset, double inc) {
return findXRange(dataset, inc, true);
}
/**
* Returns the range of x-values in the dataset by iterating over all
* values (and ignoring {@code Double.NaN} values). The range will be
* extended if necessary to include {@code inc} (unless it is
* {@code Double.NaN} in which case it is ignored). If the
* {@code finite} flag is set, infinite values in the dataset will be
* ignored. If there are no values eligible for inclusion in the range,
* this method returns {@code null}.
*
* @param dataset the dataset ({@code null} not permitted).
* @param inc an additional x-value to include.
* @param finite a flag indicating whether to exclude infinite values.
*
* @return The range (possibly {@code null}).
*
* @since 1.4
*/
public static Range findXRange(XYZDataset dataset, double inc,
boolean finite) {
Args.nullNotPermitted(dataset, "dataset");
double min = Double.POSITIVE_INFINITY;
double max = Double.NEGATIVE_INFINITY;
for (int s = 0; s < dataset.getSeriesCount(); s++) {
for (int i = 0; i < dataset.getItemCount(s); i++) {
double x = dataset.getX(s, i);
if (!Double.isNaN(x)) {
if (!finite || !Double.isInfinite(x)) {
min = Math.min(x, min);
max = Math.max(x, max);
}
}
}
}
if (!Double.isNaN(inc)) {
min = Math.min(inc, min);
max = Math.max(inc, max);
}
if (min <= max) {
return new Range(min, max);
} else {
return null;
}
}
/**
* Returns the range of y-values in the dataset by iterating over all
* values (and ignoring {@code Double.NaN} and infinite values).
* If there are no values eligible for inclusion in the range, this method
* returns {@code null}.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The range.
*/
public static Range findYRange(XYZDataset dataset) {
return findYRange(dataset, Double.NaN);
}
/**
* Returns the range of y-values in the dataset by iterating over all
* values (and ignoring {@code Double.NaN} values). The range will be
* extended if necessary to include {@code inc} (unless it is
* {@code Double.NaN} in which case it is ignored). Infinite values
* in the dataset will be ignored. If there are no values eligible for
* inclusion in the range, this method returns {@code null}.
*
* @param dataset the dataset ({@code null} not permitted).
* @param inc an additional x-value to include.
*
* @return The range.
*/
public static Range findYRange(XYZDataset dataset, double inc) {
return findYRange(dataset, inc, true);
}
/**
* Returns the range of y-values in the dataset by iterating over all
* values (and ignoring {@code Double.NaN} values). The range will be
* extended if necessary to include {@code inc} (unless it is
* {@code Double.NaN} in which case it is ignored). If the
* {@code finite} flag is set, infinite values in the dataset will be
* ignored. If there are no values eligible for inclusion in the range,
* this method returns {@code null}.
*
* @param dataset the dataset ({@code null} not permitted).
* @param inc an additional y-value to include.
* @param finite a flag indicating whether to exclude infinite values.
*
* @return The range (possibly {@code null}).
*
* @since 1.4
*/
public static Range findYRange(XYZDataset dataset, double inc,
boolean finite) {
Args.nullNotPermitted(dataset, "dataset");
double min = Double.POSITIVE_INFINITY;
double max = Double.NEGATIVE_INFINITY;
for (int s = 0; s < dataset.getSeriesCount(); s++) {
for (int i = 0; i < dataset.getItemCount(s); i++) {
double y = dataset.getY(s, i);
if (!Double.isNaN(y)) {
if (!finite || !Double.isInfinite(y)) {
min = Math.min(y, min);
max = Math.max(y, max);
}
}
}
}
if (!Double.isNaN(inc)) {
min = Math.min(inc, min);
max = Math.max(inc, max);
}
if (min <= max) {
return new Range(min, max);
} else {
return null;
}
}
/**
* Returns the range of z-values in the dataset by iterating over all
* values (and ignoring {@code Double.NaN} and infinite values).
* If there are no values eligible for inclusion in the range, this method
* returns {@code null}.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return The range (possibly {@code null}).
*/
public static Range findZRange(XYZDataset dataset) {
return findZRange(dataset, Double.NaN);
}
/**
* Returns the range of z-values in the dataset by iterating over all
* values (and ignoring {@code Double.NaN} values). The range will be
* extended if necessary to include {@code inc} (unless it is
* {@code Double.NaN} in which case it is ignored). Infinite values
* in the dataset will be ignored. If there are no values eligible for
* inclusion in the range, this method returns {@code null}.
*
* @param dataset the dataset ({@code null} not permitted).
* @param inc an additional x-value to include.
*
* @return The range (possibly {@code null}).
*/
public static Range findZRange(XYZDataset dataset, double inc) {
return findZRange(dataset, inc, true);
}
/**
* Returns the range of z-values in the dataset by iterating over all
* values (and ignoring {@code Double.NaN} values). The range will be
* extended if necessary to include {@code inc} (unless it is
* {@code Double.NaN} in which case it is ignored). If the
* {@code finite} flag is set, infinite values in the dataset will be
* ignored. If there are no values eligible for inclusion in the range,
* this method returns {@code null}.
*
* @param dataset the dataset ({@code null} not permitted).
* @param inc an additional z-value to include.
* @param finite a flag indicating whether to exclude infinite values.
*
* @return The range (possibly {@code null}).
*
* @since 1.4
*/
public static Range findZRange(XYZDataset dataset, double inc,
boolean finite) {
Args.nullNotPermitted(dataset, "dataset");
Args.finiteRequired(inc, "inc");
double min = Double.POSITIVE_INFINITY;
double max = Double.NEGATIVE_INFINITY;
for (int s = 0; s < dataset.getSeriesCount(); s++) {
for (int i = 0; i < dataset.getItemCount(s); i++) {
double z = dataset.getZ(s, i);
if (!Double.isNaN(z)) {
if (!finite || !Double.isInfinite(z)) {
min = Math.min(z, min);
max = Math.max(z, max);
}
}
}
}
if (!Double.isNaN(inc)) {
min = Math.min(inc, min);
max = Math.max(inc, max);
}
if (min <= max) {
return new Range(min, max);
} else {
return null;
}
}
/**
* Creates an {@link XYZDataset} by extracting values from specified
* rows in a {@link KeyedValues3D} instance, across all the available
* columns (items where any of the x, y or z values is {@code null}
* are skipped). The new dataset contains a copy of the data and is
* completely independent of the {@code source} dataset.
*
* Note that {@link CategoryDataset3D} is an extension of
* {@link KeyedValues3D} so you can use this method for any implementation
* of the {@code CategoryDataset3D} interface.
*
* @param the series key (must implement Comparable).
* @param the row key (must implement Comparable).
* @param the column key (must implement Comparable).
* @param source the source data ({@code null} not permitted).
* @param xRowKey the row key for x-values ({@code null} not permitted).
* @param yRowKey the row key for y-values ({@code null} not permitted).
* @param zRowKey the row key for z-values ({@code null} not permitted).
*
* @return A new dataset.
*
* @since 1.3
*/
public static , R extends Comparable,
C extends Comparable> XYZDataset extractXYZDatasetFromRows(
KeyedValues3D source,
R xRowKey, R yRowKey, R zRowKey) {
return extractXYZDatasetFromRows(source, xRowKey, yRowKey, zRowKey,
NullConversion.SKIP, null);
}
/**
* Creates an {@link XYZDataset} by extracting values from specified
* rows in a {@link KeyedValues3D} instance. The new dataset contains
* a copy of the data and is completely independent of the
* {@code source} dataset. Note that {@link CategoryDataset3D} is an
* extension of {@link KeyedValues3D}.
*
* Special handling is provided for items that contain {@code null}
* values. The caller may pass in an {@code exceptions} list (
* normally empty) that will be populated with the keys of the items that
* receive special handling, if any.
*
* @param the series key (must implement Comparable).
* @param the row key (must implement Comparable).
* @param the column key (must implement Comparable).
* @param source the source data ({@code null} not permitted).
* @param xRowKey the row key for x-values ({@code null} not permitted).
* @param yRowKey the row key for y-values ({@code null} not permitted).
* @param zRowKey the row key for z-values ({@code null} not permitted).
* @param nullConversion specifies the treatment for {@code null}
* values in the dataset ({@code null} not permitted).
* @param exceptions a list that, if not null, will be populated with
* keys for the items in the source dataset that contain
* {@code null} values ({@code null} permitted).
*
* @return A new dataset.
*
* @since 1.3
*/
public static , R extends Comparable,
C extends Comparable> XYZDataset extractXYZDatasetFromRows(
KeyedValues3D source,
R xRowKey, R yRowKey, R zRowKey, NullConversion nullConversion,
List exceptions) {
Args.nullNotPermitted(source, "source");
Args.nullNotPermitted(xRowKey, "xRowKey");
Args.nullNotPermitted(yRowKey, "yRowKey");
Args.nullNotPermitted(zRowKey, "zRowKey");
XYZSeriesCollection dataset = new XYZSeriesCollection<>();
for (S seriesKey : source.getSeriesKeys()) {
XYZSeries series = new XYZSeries<>(seriesKey);
for (C colKey : source.getColumnKeys()) {
Number x = source.getValue(seriesKey, xRowKey, colKey);
Number y = source.getValue(seriesKey, yRowKey, colKey);
Number z = source.getValue(seriesKey, zRowKey, colKey);
if (x != null && y != null && z != null) {
series.add(x.doubleValue(), y.doubleValue(),
z.doubleValue());
} else {
if (exceptions != null) {
// add only one exception per data value
R rrKey = zRowKey;
if (x == null) {
rrKey = xRowKey;
} else if (y == null) {
rrKey = yRowKey;
}
exceptions.add(new KeyedValues3DItemKey<>(seriesKey,
rrKey, colKey));
}
if (nullConversion.equals(NullConversion.THROW_EXCEPTION)) {
Comparable rrKey = zRowKey;
if (x == null) {
rrKey = yRowKey;
} else if (y == null) {
rrKey = yRowKey;
}
throw new RuntimeException("There is a null value for "
+ "the item [" + seriesKey +", " + rrKey + ", "
+ colKey + "].");
}
if (nullConversion != NullConversion.SKIP) {
double xx = convert(x, nullConversion);
double yy = convert(y, nullConversion);
double zz = convert(z, nullConversion);
series.add(xx, yy, zz);
}
}
}
dataset.add(series);
}
return dataset;
}
/**
* Creates an {@link XYZDataset} by extracting values from specified
* columns in a {@link KeyedValues3D} instance, across all the available
* rows (items where any of the x, y or z values is {@code null} are
* skipped). The new dataset contains a copy of the data and is completely
* independent of the {@code source} dataset.
*
* Note that {@link CategoryDataset3D} is an extension of
* {@link KeyedValues3D} so you can use this method for any implementation
* of the {@code CategoryDataset3D} interface.
*
* @param the series key (must implement Comparable).
* @param the row key (must implement Comparable).
* @param the column key (must implement Comparable).
* @param source the source data ({@code null} not permitted).
* @param xColKey the column key for x-values ({@code null} not permitted).
* @param yColKey the column key for y-values ({@code null} not permitted).
* @param zColKey the column key for z-values ({@code null} not permitted).
*
* @return A new dataset.
*
* @since 1.3
*/
public static , R extends Comparable,
C extends Comparable> XYZDataset extractXYZDatasetFromColumns(
KeyedValues3D source,
C xColKey, C yColKey, C zColKey) {
return extractXYZDatasetFromColumns(source, xColKey, yColKey, zColKey,
NullConversion.SKIP, null);
}
/**
* Creates an {@link XYZDataset} by extracting values from specified
* columns in a {@link KeyedValues3D} instance. The new dataset contains
* a copy of the data and is completely independent of the
* {@code source} dataset. Note that {@link CategoryDataset3D} is an
* extension of {@link KeyedValues3D}.
*
* Special handling is provided for items that contain {@code null}
* values. The caller may pass in an {@code exceptions} list (
* normally empty) that will be populated with the keys of the items that
* receive special handling, if any.
*
* @param the series key (must implement Comparable).
* @param the row key (must implement Comparable).
* @param the column key (must implement Comparable).
* @param source the source data ({@code null} not permitted).
* @param xColKey the column key for x-values ({@code null} not permitted).
* @param yColKey the column key for y-values ({@code null} not permitted).
* @param zColKey the column key for z-values ({@code null} not permitted).
* @param nullConversion specifies the treatment for {@code null}
* values in the dataset ({@code null} not permitted).
* @param exceptions a list that, if not null, will be populated with
* keys for the items in the source dataset that contain
* {@code null} values ({@code null} permitted).
*
* @return A new dataset.
*
* @since 1.3
*/
public static , R extends Comparable,
C extends Comparable>
XYZDataset extractXYZDatasetFromColumns(
KeyedValues3D source,
C xColKey, C yColKey, C zColKey, NullConversion nullConversion,
List exceptions) {
Args.nullNotPermitted(source, "source");
Args.nullNotPermitted(xColKey, "xColKey");
Args.nullNotPermitted(yColKey, "yColKey");
Args.nullNotPermitted(zColKey, "zColKey");
XYZSeriesCollection dataset = new XYZSeriesCollection<>();
for (S seriesKey : source.getSeriesKeys()) {
XYZSeries series = new XYZSeries<>(seriesKey);
for (R rowKey : source.getRowKeys()) {
Number x = source.getValue(seriesKey, rowKey, xColKey);
Number y = source.getValue(seriesKey, rowKey, yColKey);
Number z = source.getValue(seriesKey, rowKey, zColKey);
if (x != null && y != null && z != null) {
series.add(x.doubleValue(), y.doubleValue(),
z.doubleValue());
} else {
if (exceptions != null) {
// add only one key ref out of the possible 3 per item
C ccKey = zColKey;
if (x == null) {
ccKey = xColKey;
} else if (y == null) {
ccKey = yColKey;
}
exceptions.add(new KeyedValues3DItemKey<>(seriesKey,
rowKey, ccKey));
}
if (nullConversion.equals(NullConversion.THROW_EXCEPTION)) {
Comparable> ccKey = zColKey;
if (x == null) {
ccKey = xColKey;
} else if (y == null) {
ccKey = yColKey;
}
throw new RuntimeException("There is a null value for "
+ "the item [" + seriesKey +", " + rowKey + ", "
+ ccKey + "].");
}
if (nullConversion != NullConversion.SKIP) {
double xx = convert(x, nullConversion);
double yy = convert(y, nullConversion);
double zz = convert(z, nullConversion);
series.add(xx, yy, zz);
}
}
}
dataset.add(series);
}
return dataset;
}
/**
* Returns a double primitive for the specified number, with
* {@code null} values returning {@code Double.NaN} except in the
* case of {@code CONVERT_TO_ZERO} which returns 0.0. Note that this
* method does not throw an exception for {@code THROW_EXCEPTION}, it
* expects code higher up the call chain to handle that (because there is
* not enough information here to throw a useful exception).
*
* @param n the number ({@code null} permitted).
* @param nullConversion the null conversion ({@code null} not
* permitted).
*
* @return A double primitive.
*/
private static double convert(Number n, NullConversion nullConversion) {
if (n != null) {
return n.doubleValue();
} else {
if (nullConversion.equals(NullConversion.CONVERT_TO_ZERO)) {
return 0.0;
}
return Double.NaN;
}
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/data/Dataset3D.java 0000664 0000000 0000000 00000005443 14173312715 0025344 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.data;
import java.util.EventListener;
/**
* The base interface for datasets in Orson Charts. All datasets must support
* the change event notification mechanism. The idea is that when a dataset
* is changed, an event is passed to the plot. The plot can react to this
* event, then pass on the change event to the chart. The chart in turn will
* pass on the event and this can result in the chart being repainted (for
* example, if the chart is displayed in a
* {@link org.jfree.chart3d.Chart3DPanel}).
*/
public interface Dataset3D {
/**
* Registers a change listener to receive notification of changes to the
* dataset.
*
* @param listener the listener ({@code null} not permitted).
*/
void addChangeListener(Dataset3DChangeListener listener);
/**
* De-registers a change listener so that it no longer receives notification
* of changes to the dataset.
*
* @param listener the listener ({@code null} not permitted).
*/
void removeChangeListener(Dataset3DChangeListener listener);
/**
* Returns {@code true} if the specified listener is registered with
* the dataset, and {@code false} otherwise. This method is used
* for unit testing to confirm that listeners are removed correctly
* following dataset switches.
*
* @param listener the listener.
*
* @return A boolean.
*/
boolean hasListener(EventListener listener);
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/data/Dataset3DChangeEvent.java 0000664 0000000 0000000 00000005270 14173312715 0027452 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.data;
import java.util.EventObject;
/**
* A dataset change event. Any object that implements the
* {@link Dataset3DChangeListener} interface can register with a dataset to
* receive notification of change events. By default, the plot classes in
* Orson Charts register with their dataset to monitor dataset changes.
*
* NOTE: This class is serializable, but the serialization format is subject
* to change in future releases and should not be relied upon for persisting
* instances of this class.
*/
@SuppressWarnings("serial")
public class Dataset3DChangeEvent extends EventObject {
private final Object dataset;
/**
* Creates a new dataset change event. The source can be the same as the
* dataset, but this is not required.
*
* @param source the source.
* @param dataset the dataset.
*/
public Dataset3DChangeEvent(Object source, Object dataset) {
super(source);
this.dataset = dataset;
}
/**
* Returns the dataset that this event is associated with. This will
* normally be an instance of {@link PieDataset3D},
* {@link org.jfree.chart3d.data.category.CategoryDataset3D} or
* {@link org.jfree.chart3d.data.xyz.XYZDataset}.
*
* @return The dataset.
*/
public Object getDataset() {
return this.dataset;
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/data/Dataset3DChangeListener.java 0000664 0000000 0000000 00000003542 14173312715 0030156 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.data;
import java.util.EventListener;
/**
* The interface through which dataset change events are passed. The plot
* classes implement this interface so they can receive notification of
* changes to the dataset they are managing.
*/
public interface Dataset3DChangeListener extends EventListener {
/**
* Called to notify the listener that the source dataset has been
* changed.
*
* @param event the event details.
*/
void datasetChanged(Dataset3DChangeEvent event);
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/data/DefaultKeyedValue.java 0000664 0000000 0000000 00000007035 14173312715 0027132 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.data;
import java.io.Serializable;
import org.jfree.chart3d.internal.Args;
import org.jfree.chart3d.internal.ObjectUtils;
/**
* A data item where a key is associated with a value (typically numerical).
*
* NOTE: This class is serializable, but the serialization format is subject
* to change in future releases and should not be relied upon for persisting
* instances of this class.
*/
@SuppressWarnings("serial")
public final class DefaultKeyedValue, T>
implements KeyedValue, Serializable {
/** The key. */
private final K key;
/** The value. */
private T value;
/**
* Creates a new instance.
*
* @param key the key ({@code null} not permitted).
* @param value the value.
*/
public DefaultKeyedValue(K key, T value) {
Args.nullNotPermitted(key, "key");
this.key = key;
this.value = value;
}
/**
* Returns the key.
*
* @return The key (never {@code null}).
*/
@Override
public K getKey() {
return this.key;
}
/**
* Returns the value.
*
* @return The value (possibly {@code null}).
*/
@Override
public T getValue() {
return this.value;
}
/**
* Sets the value.
*
* @param value the value ({@code null} permitted).
*/
public void setValue(T value) {
this.value = value;
}
/**
* Tests this instance for equality with an arbitrary object.
*
* @param obj the object to test ({@code null} permitted).
*
* @return A boolean.
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof DefaultKeyedValue)) {
return false;
}
DefaultKeyedValue, ?> that = (DefaultKeyedValue, ?>) obj;
if (!this.key.equals(that.key)) {
return false;
}
if (!ObjectUtils.equals(this.value, that.value)) {
return false;
}
return true;
}
@Override
public String toString() {
return "(" + this.key.toString() + ", " + value + ")";
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/data/DefaultKeyedValues.java 0000664 0000000 0000000 00000016371 14173312715 0027320 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.jfree.chart3d.internal.Args;
/**
* A list of {@code (key, value)} pairs.
*
* This is the basic structure of the data required for a pie chart.
*
* NOTE: This class is serializable, but the serialization format is subject
* to change in future releases and should not be relied upon for persisting
* instances of this class.
*
* @param the key type (must implement Comparable).
* @param the value type.
*/
@SuppressWarnings("serial")
public final class DefaultKeyedValues, T>
implements KeyedValues, Serializable {
/** Storage for the data items. */
private List> data;
/**
* Creates a new (empty) list of keyed values.
*/
public DefaultKeyedValues() {
this(new ArrayList<>());
}
/**
* Creates a new instance with the specified keys (each associated with
* a {@code null} value). There is usually no need to specify any
* keys in advance, so you will normally use the default constructor. This
* constructor is provided for the convenience of some internal code.
*
* @param keys the keys ({@code null} not permitted).
*/
public DefaultKeyedValues(List keys) {
Args.nullNotPermitted(keys, "keys");
this.data = new ArrayList<>();
for (K key : keys) {
this.data.add(new DefaultKeyedValue<>(key, null));
}
}
/**
* Clears all the data.
*/
public void clear() {
this.data.clear();
}
/**
* Adds a value or, if there is an existing value with the same key, updates
* an existing value.
*
* @param key the key ({@code null} not permitted)
* @param value the value.
*/
public void put(K key, T value) {
Args.nullNotPermitted(key, "key");
DefaultKeyedValue dkv;
int index = getIndex(key);
if (index >= 0) {
dkv = (DefaultKeyedValue) this.data.get(index);
dkv.setValue(value);
} else {
this.data.add(new DefaultKeyedValue<>(key, value));
}
}
/**
* Removes the item with the specified key, if there is one.
*
* @param key the key ({@code null} not permitted).
*/
public void remove(K key) {
Args.nullNotPermitted(key, "key");
int index = getIndex(key);
if (index >= 0) {
remove(index);
}
}
/**
* Removes the item with the specified index.
*
* @param index the index.
*/
public void remove(int index) {
this.data.remove(index);
}
/**
* Returns the key for the item with the specified index.
*
* @param index the item index.
*
* @return The key.
*/
@Override
public K getKey(int index) {
KeyedValue kv = this.data.get(index);
return kv.getKey();
}
/**
* Returns the index of the item with the specified key, or {@code -1}
* if there is no such item.
*
* @param key the key ({@code null} not permitted).
*
* @return The item index, or {@code -1}.
*/
@Override
public int getIndex(K key) {
Args.nullNotPermitted(key, "key");
for (int i = 0; i < this.data.size(); i++) {
KeyedValue kv = this.data.get(i);
if (kv.getKey().equals(key)) {
return i;
}
}
return -1;
}
/**
* Returns a list of all the keys. Note that the list will be a copy, so
* modifying it will not impact this data structure.
*
* @return A list of keys (possibly empty, but never {@code null}).
*/
@Override
public List getKeys() {
List keys = new ArrayList<>();
for (KeyedValue kv : this.data) {
keys.add(kv.getKey());
}
return keys;
}
/**
* Returns the value with the specified key.
*
* @param key the key ({@code null} not permitted).
*
* @return The value (possibly {@code null}).
*/
@Override
public T getValue(K key) {
// arg checking is performed by getIndex()
int index = getIndex(key);
if (index < 0) {
return null;
}
return getValue(index);
}
/**
* Returns the number of items in the list.
*
* @return The number of items in the list.
*/
@Override
public int getItemCount() {
return this.data.size();
}
/**
* Returns the value for the specified item.
*
* @param item the item index.
*
* @return The value (possibly {@code null}).
*/
@Override
public T getValue(int item) {
KeyedValue kv = this.data.get(item);
return kv.getValue();
}
/**
* Returns the value for the specified item, as a double primitive,
* provided that the data value is an instance of {@code Number}.
*
* @param item the item index.
*
* @return The value.
*/
@Override
public double getDoubleValue(int item) {
T n = getValue(item);
if (n != null && n instanceof Number) {
return ((Number) n).doubleValue();
}
return Double.NaN;
}
/**
* Tests this instance for equality with an arbitrary object.
*
* @param obj the object to test against ({@code null} permitted).
*
* @return A boolean.
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof DefaultKeyedValues, ?>)) {
return false;
}
DefaultKeyedValues, ?> that = (DefaultKeyedValues, ?>) obj;
if (!this.data.equals(that.data)) {
return false;
}
return true;
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/data/DefaultKeyedValues2D.java 0000664 0000000 0000000 00000023412 14173312715 0027500 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.jfree.chart3d.internal.Args;
/**
* A two dimensional grid of (typically numerical) data that is accessible by
* row and column keys.
*
* NOTE: This class is serializable, but the serialization format is subject
* to change in future releases and should not be relied upon for persisting
* instances of this class.
*
* @param the row key type
* @param the column key type
* @param the value type.
*
*/
@SuppressWarnings("serial")
public final class DefaultKeyedValues2D, C extends Comparable, T>
implements KeyedValues2D, Serializable {
/** The row keys. */
List rowKeys;
/** The column keys. */
List columnKeys;
/** The data values. */
List> data; // one entry per row key
/**
* Creates a new (empty) instance.
*/
public DefaultKeyedValues2D() {
this(new ArrayList<>(), new ArrayList<>());
}
/**
* Creates a new instance with the specified keys and all data values
* initialized to {@code null}.
*
* @param rowKeys the xKeys ({@code null} not permitted).
* @param columnKeys the yKeys ({@code null} not permitted).
*/
public DefaultKeyedValues2D(List rowKeys, List columnKeys) {
Args.nullNotPermitted(rowKeys, "rowKeys");
Args.nullNotPermitted(columnKeys, "columnKeys");
this.rowKeys = new ArrayList<>(rowKeys);
this.columnKeys = new ArrayList<>(columnKeys);
this.data = new ArrayList<>();
for (int i = 0; i < rowKeys.size(); i++) {
this.data.add(new DefaultKeyedValues<>(columnKeys));
}
}
/**
* Returns the row key corresponding to the specified index.
*
* @param rowIndex the row index.
*
* @return The key.
*/
@Override
public R getRowKey(int rowIndex) {
return this.rowKeys.get(rowIndex);
}
/**
* Returns the column key corresponding to the specified index.
*
* @param columnIndex the column index.
*
* @return The key.
*/
@Override
public C getColumnKey(int columnIndex) {
return this.columnKeys.get(columnIndex);
}
/**
* Returns the index corresponding to the specified row key.
*
* @param rowKey the row key ({@code null} not permitted).
*
* @return The index.
*/
@Override
public int getRowIndex(R rowKey) {
Args.nullNotPermitted(rowKey, "rowKey");
return this.rowKeys.indexOf(rowKey);
}
/**
* Returns the index corresponding to the specified column key.
*
* @param columnKey the column key ({@code null} not permitted).
*
* @return The index.
*/
@Override
public int getColumnIndex(C columnKey) {
Args.nullNotPermitted(columnKey, "columnKey");
return this.columnKeys.indexOf(columnKey);
}
/**
* Returns a copy of the list of row keys.
*
* @return A copy of the list of row keys (never {@code null}).
*/
@Override
public List getRowKeys() {
return new ArrayList<>(this.rowKeys);
}
/**
* Returns a copy of the list of column keys.
*
* @return A copy of the list of column keys (never {@code null}).
*/
@Override
public List getColumnKeys() {
return new ArrayList<>(this.columnKeys);
}
/**
* Returns the number of row keys in the table.
*
* @return The number of row keys in the table.
*/
@Override
public int getRowCount() {
return this.rowKeys.size();
}
/**
* Returns the number of column keys in the data structure.
*
* @return The number of column keys.
*/
@Override
public int getColumnCount() {
return this.columnKeys.size();
}
/**
* Returns a value from one cell in the table.
*
* @param rowKey the row-key ({@code null} not permitted).
* @param columnKey the column-key ({@code null} not permitted).
*
* @return The value (possibly {@code null}).
*/
@Override
public T getValue(R rowKey, C columnKey) {
// arg checking is handled in getXIndex() and getYIndex()
int rowIndex = getRowIndex(rowKey);
int columnIndex = getColumnIndex(columnKey);
return getValue(rowIndex, columnIndex);
}
/**
* Returns the value from one cell in the table.
*
* @param rowIndex the row index.
* @param columnIndex the column index.
*
* @return The value (possibly {@code null}).
*/
@Override
public T getValue(int rowIndex, int columnIndex) {
return this.data.get(rowIndex).getValue(columnIndex);
}
/**
* Returns the data item at the specified position as a double primitive.
* Where the {@link #getValue(int, int)} method returns {@code null},
* this method returns {@code Double.NaN}.
*
* @param rowIndex the row index.
* @param columnIndex the column index.
*
* @return The data value.
*/
@Override
public double getDoubleValue(int rowIndex, int columnIndex) {
T n = getValue(rowIndex, columnIndex);
if (n != null && n instanceof Number) {
return ((Number) n).doubleValue();
}
return Double.NaN;
}
/**
* Sets a value for one cell in the table.
*
* @param n the value ({@code null} permitted).
* @param rowKey the row key ({@code null} not permitted).
* @param columnKey the column key ({@code null} not permitted).
*/
public void setValue(T n, R rowKey, C columnKey) {
Args.nullNotPermitted(rowKey, "rowKey");
Args.nullNotPermitted(columnKey, "columnKey");
if (this.data.isEmpty()) { // 1. no data - just add one new entry
this.rowKeys.add(rowKey);
this.columnKeys.add(columnKey);
DefaultKeyedValues dkvs = new DefaultKeyedValues<>();
dkvs.put(columnKey, n);
this.data.add(dkvs);
} else {
int rowIndex = getRowIndex(rowKey);
int columnIndex = getColumnIndex(columnKey);
if (rowIndex >= 0) {
DefaultKeyedValues dkvs = this.data.get(rowIndex);
if (columnIndex >= 0) {
// 2. Both keys exist - just update the value
dkvs.put(columnKey, n);
} else {
// 3. rowKey exists, but columnKey does not (add the
// columnKey to each series)
this.columnKeys.add(columnKey);
for (DefaultKeyedValues kv : this.data) {
kv.put(columnKey, null);
}
dkvs.put(columnKey, n);
}
} else {
if (columnIndex >= 0) {
// 4. rowKey does not exist, but columnKey does
this.rowKeys.add(rowKey);
DefaultKeyedValues d = new DefaultKeyedValues<>(
this.columnKeys);
d.put(columnKey, n);
this.data.add(d);
} else {
// 5. neither key exists, need to create the new series,
// plus the new entry in every series
this.rowKeys.add(rowKey);
this.columnKeys.add(columnKey);
for (DefaultKeyedValues kv : this.data) {
kv.put(columnKey, null);
}
DefaultKeyedValues d = new DefaultKeyedValues<>(
this.columnKeys);
d.put(columnKey, n);
this.data.add(d);
}
}
}
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof DefaultKeyedValues2D)) {
return false;
}
DefaultKeyedValues2D that = (DefaultKeyedValues2D) obj;
if (!this.rowKeys.equals(that.rowKeys)) {
return false;
}
if (!this.columnKeys.equals(that.columnKeys)) {
return false;
}
if (!this.data.equals(that.data)) {
return false;
}
return true;
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/data/DefaultKeyedValues3D.java 0000664 0000000 0000000 00000025254 14173312715 0027507 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.jfree.chart3d.internal.Args;
/**
* A three dimensional table of numerical values, implementing the
* {@link KeyedValues3D} interface.
*
* NOTE: This class is serializable, but the serialization format is subject
* to change in future releases and should not be relied upon for persisting
* instances of this class.
*
* @param the series key (must implement Comparable).
* @param the row key (must implement Comparable).
* @param the column key (must implement Comparable).
* @param the value type.
*/
@SuppressWarnings("serial")
public final class DefaultKeyedValues3D, R extends Comparable, C extends Comparable, V>
implements KeyedValues3D, Serializable {
/** The series keys. */
private final List seriesKeys;
/** The row keys. */
private final List rowKeys;
/** The column keys. */
private List columnKeys;
/**
* The data, one entry per series. Each series *must* contain the same
* row and column keys.
*/
private List> data; // one entry per series
/**
* Creates a new (empty) table.
*/
public DefaultKeyedValues3D() {
this.seriesKeys = new ArrayList<>();
this.rowKeys = new ArrayList<>();
this.columnKeys = new ArrayList<>();
this.data = new ArrayList<>();
}
/**
* Returns the series key with the specified index.
*
* @param seriesIndex the series index.
*
* @return The series key.
*/
@Override
public S getSeriesKey(int seriesIndex) {
return this.seriesKeys.get(seriesIndex);
}
/**
* Returns the row key with the specified index.
*
* @param rowIndex the row index.
*
* @return The row key.
*/
@Override
public R getRowKey(int rowIndex) {
return this.rowKeys.get(rowIndex);
}
/**
* Returns the column key with the specified index.
*
* @param columnIndex the column index.
*
* @return The column key.
*/
@Override
public C getColumnKey(int columnIndex) {
return this.columnKeys.get(columnIndex);
}
/**
* Returns the index for the specified series key, or {@code -1} if
* the key is not present in this data structure.
*
* @param seriesKey the series key ({@code null} not permitted).
*
* @return The series index or {@code -1}.
*/
@Override
public int getSeriesIndex(S seriesKey) {
Args.nullNotPermitted(seriesKey, "seriesKey");
return this.seriesKeys.indexOf(seriesKey);
}
/**
* Returns the index for the specified row key, or {@code -1} if
* the key is not present in this data structure.
*
* @param rowKey the row key ({@code null} not permitted).
*
* @return The row index or {@code -1}.
*/
@Override
public int getRowIndex(R rowKey) {
Args.nullNotPermitted(rowKey, "rowKey");
return this.rowKeys.indexOf(rowKey);
}
/**
* Returns the index for the specified column key, or {@code -1} if
* the key is not present in this data structure.
*
* @param columnKey the column key ({@code null} not permitted).
*
* @return The column index or {@code -1}.
*/
@Override
public int getColumnIndex(C columnKey) {
Args.nullNotPermitted(columnKey, "columnKey");
return this.columnKeys.indexOf(columnKey);
}
/**
* Returns a list of the series keys for the data. Modifying this
* list will have no impact on the underlying data.
*
* @return A list of the series keys (possibly empty, but never
* {@code null}).
*/
@Override
public List getSeriesKeys() {
return new ArrayList<>(this.seriesKeys);
}
/**
* Returns a list of the row keys for the data. Modifying this
* list will have no impact on the underlying data.
*
* @return A list of the row keys (possibly empty, but never
* {@code null}).
*/
@Override
public List getRowKeys() {
return new ArrayList<>(this.rowKeys);
}
/**
* Returns a list of the column keys for the data. Modifying this
* list will have no impact on the underlying data.
*
* @return A list of the column keys (possibly empty, but never
* {@code null}).
*/
@Override
public List getColumnKeys() {
return new ArrayList<>(this.columnKeys);
}
@Override
public int getSeriesCount() {
return this.seriesKeys.size();
}
@Override
public int getRowCount() {
return this.rowKeys.size();
}
@Override
public int getColumnCount() {
return this.columnKeys.size();
}
@Override
public V getValue(int seriesIndex, int rowIndex, int columnIndex) {
return this.data.get(seriesIndex).getValue(rowIndex, columnIndex);
}
/**
* Returns the value for the specified data item. This method will
* throw an {@code IllegalArgumentException} if the dataset does not
* contain the specified keys.
*
* @param seriesKey the series key ({@code null} not permitted).
* @param rowKey the row key ({@code null} not permitted).
* @param columnKey the column key ({@code null} not permitted).
*
* @return The value (possibly {@code null}).
*/
@Override
public V getValue(S seriesKey, R rowKey, C columnKey) {
int seriesIndex = getSeriesIndex(seriesKey);
if (seriesIndex < 0) {
throw new IllegalArgumentException("Series '" + seriesKey.toString()
+ "' is not found.");
}
int rowIndex = getRowIndex(rowKey);
if (rowIndex < 0) {
throw new IllegalArgumentException("Row key '" + rowKey.toString()
+ "' is not found.");
}
int columnIndex = getColumnIndex(columnKey);
if (columnIndex < 0) {
throw new IllegalArgumentException("Column key '"
+ columnKey.toString() + "' is not found.");
}
return getValue(seriesIndex, rowIndex, columnIndex);
}
@Override
public double getDoubleValue(int seriesIndex, int rowIndex,
int columnIndex) {
V n = getValue(seriesIndex, rowIndex, columnIndex);
if (n != null && n instanceof Number) {
return ((Number) n).doubleValue();
}
return Double.NaN;
}
/**
* Sets the value for an item in a series, overwriting any existing value.
*
* @param n the value ({@code null} permitted).
* @param seriesKey the series key ({@code null} not permitted).
* @param rowKey the row key ({@code null} not permitted).
* @param columnKey the column key ({@code null} not permitted).
*/
public void setValue(V n, S seriesKey, R rowKey, C columnKey) {
Args.nullNotPermitted(seriesKey, "seriesKey");
Args.nullNotPermitted(rowKey, "rowKey");
Args.nullNotPermitted(columnKey, "columnKey");
// cases:
// 1 - the dataset is empty, so we just need to add a new layer with the
// given keys;
if (this.data.isEmpty()) {
this.seriesKeys.add(seriesKey);
this.rowKeys.add(rowKey);
this.columnKeys.add(columnKey);
DefaultKeyedValues2D d = new DefaultKeyedValues2D<>();
d.setValue(n, rowKey, columnKey);
this.data.add(d);
}
int seriesIndex = getSeriesIndex(seriesKey);
int rowIndex = getRowIndex(rowKey);
int columnIndex = getColumnIndex(columnKey);
if (rowIndex < 0) {
this.rowKeys.add(rowKey);
}
if (columnIndex < 0) {
this.columnKeys.add(columnKey);
}
if (rowIndex < 0 || columnIndex < 0) {
for (DefaultKeyedValues2D d : this.data) {
d.setValue(null, rowKey, columnKey);
}
}
if (seriesIndex >= 0) {
DefaultKeyedValues2D d = this.data.get(seriesIndex);
d.setValue(n, rowKey, columnKey);
} else {
this.seriesKeys.add(seriesKey);
DefaultKeyedValues2D d = new DefaultKeyedValues2D<>(
this.rowKeys, this.columnKeys);
d.setValue(n, rowKey, columnKey);
this.data.add(d);
}
}
/**
* Tests this instance for equality with an arbitrary object.
*
* @param obj the object ({@code null} permitted).
*
* @return A boolean.
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof DefaultKeyedValues3D)) {
return false;
}
DefaultKeyedValues3D that = (DefaultKeyedValues3D) obj;
if (!this.seriesKeys.equals(that.seriesKeys)) {
return false;
}
if (!this.rowKeys.equals(that.rowKeys)) {
return false;
}
if (!this.columnKeys.equals(that.columnKeys)) {
return false;
}
if (!this.data.equals(that.data)) {
return false;
}
return true;
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/data/ItemKey.java 0000664 0000000 0000000 00000003162 14173312715 0025133 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.data;
/**
* A key that references a single data item in a dataset.
*
* @since 1.3
*/
public interface ItemKey {
/**
* Returns a JSON formatted string representing the key.
*
* @return A JSON formatted string.
*/
String toJSONString();
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/data/JSONUtils.java 0000664 0000000 0000000 00000100410 14173312715 0025350 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.data;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.jfree.chart3d.util.json.JSONValue;
import org.jfree.chart3d.util.json.parser.JSONParser;
import org.jfree.chart3d.util.json.parser.ParseException;
import org.jfree.chart3d.internal.Args;
import org.jfree.chart3d.util.json.parser.ContainerFactory;
import org.jfree.chart3d.data.category.StandardCategoryDataset3D;
import org.jfree.chart3d.data.xyz.XYZDataset;
import org.jfree.chart3d.data.xyz.XYZSeries;
import org.jfree.chart3d.data.xyz.XYZSeriesCollection;
/**
* Utility methods for interchange between datasets ({@link KeyedValues},
* {@link KeyedValues3D} and {@link XYZDataset}) and JSON format strings.
*
* @since 1.3
*/
public class JSONUtils {
/**
* Parses the supplied JSON string into a {@link KeyedValues} instance.
*
* Implementation note: this method returns an instance of
* {@link StandardPieDataset3D}).
*
* @param json the JSON string ({@code null} not permitted).
*
* @return A {@link KeyedValues} instance.
*/
public static KeyedValues readKeyedValues(String json) {
Args.nullNotPermitted(json, "json");
StringReader in = new StringReader(json);
KeyedValues result;
try {
result = readKeyedValues(in);
} catch (IOException ex) {
// not for StringReader
result = null;
}
return result;
}
/**
* Parses characters from the supplied reader and returns the corresponding
* {@link KeyedValues} instance.
*
* Implementation note: this method returns an instance of
* {@link StandardPieDataset3D}).
*
* @param reader the reader ({@code null} not permitted).
*
* @return A {@code KeyedValues} instance.
*
* @throws IOException if there is an I/O problem.
*/
public static KeyedValues readKeyedValues(
Reader reader) throws IOException {
Args.nullNotPermitted(reader, "reader");
try {
JSONParser parser = new JSONParser();
// parse with custom containers (to preserve item order)
List list = (List) parser.parse(reader, createContainerFactory());
StandardPieDataset3D result = new StandardPieDataset3D<>();
for (Object item : list) {
List itemAsList = (List) item;
result.add(itemAsList.get(0).toString(), (Number) itemAsList.get(1));
}
return result;
} catch (ParseException ex) {
throw new RuntimeException(ex);
}
}
/**
* Returns a string containing the data in JSON format. The format is
* an array of arrays, where each sub-array represents one data value.
* The sub-array should contain two items, first the item key as a string
* and second the item value as a number. For example:
* {@code [["Key A", 1.0], ["Key B", 2.0]]}
*
* Note that this method can be used with instances of {@link PieDataset3D}.
*
* @param data the data ({@code null} not permitted).
*
* @return A string in JSON format.
*/
@SuppressWarnings("unchecked")
public static String writeKeyedValues(KeyedValues data) {
Args.nullNotPermitted(data, "data");
StringWriter sw = new StringWriter();
try {
writeKeyedValues(data, sw);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
return sw.toString();
}
/**
* Writes the data in JSON format to the supplied writer.
*
* Note that this method can be used with instances of {@link PieDataset3D}.
*
* @param data the data ({@code null} not permitted).
* @param writer the writer ({@code null} not permitted).
*
* @throws IOException if there is an I/O problem.
*/
@SuppressWarnings("unchecked")
public static void writeKeyedValues(KeyedValues data, Writer writer)
throws IOException {
Args.nullNotPermitted(data, "data");
Args.nullNotPermitted(writer, "writer");
writer.write("[");
boolean first = true;
for (Object key : data.getKeys()) {
if (!first) {
writer.write(", ");
} else {
first = false;
}
writer.write("[");
writer.write(JSONValue.toJSONString(key.toString()));
writer.write(", ");
writer.write(JSONValue.toJSONString(data.getValue((Comparable) key)));
writer.write("]");
}
writer.write("]");
}
/**
* Reads a data table from a JSON format string.
*
* @param json the string ({@code null} not permitted).
*
* @return A data table.
*/
@SuppressWarnings("unchecked")
public static KeyedValues2D
readKeyedValues2D(String json) {
Args.nullNotPermitted(json, "json");
StringReader in = new StringReader(json);
KeyedValues2D result;
try {
result = readKeyedValues2D(in);
} catch (IOException ex) {
// not for StringReader
result = null;
}
return result;
}
/**
* Reads a data table from a JSON format string coming from the specified
* reader.
*
* @param reader the reader ({@code null} not permitted).
*
* @return A data table.
*
* @throws java.io.IOException if there is an I/O problem.
*/
@SuppressWarnings("unchecked")
public static KeyedValues2D
readKeyedValues2D(Reader reader) throws IOException {
JSONParser parser = new JSONParser();
try {
Map map = (Map) parser.parse(reader, createContainerFactory());
DefaultKeyedValues2D result
= new DefaultKeyedValues2D<>();
if (map.isEmpty()) {
return result;
}
// read the keys
Object keysObj = map.get("columnKeys");
List keys = null;
if (keysObj instanceof List) {
keys = (List) keysObj;
} else {
if (keysObj == null) {
throw new RuntimeException("No 'columnKeys' defined.");
} else {
throw new RuntimeException("Please check the 'columnKeys', "
+ "the format does not parse to a list.");
}
}
Object dataObj = map.get("rows");
if (dataObj instanceof List) {
List rowList = (List) dataObj;
// each entry in the map has the row key and an array of
// values (the length should match the list of keys above
for (Object rowObj : rowList) {
processRow(rowObj, keys, result);
}
} else { // the 'data' entry is not parsing to a list
if (dataObj == null) {
throw new RuntimeException("No 'rows' section defined.");
} else {
throw new RuntimeException("Please check the 'rows' "
+ "entry, the format does not parse to a list of "
+ "rows.");
}
}
return result;
} catch (ParseException ex) {
throw new RuntimeException(ex);
}
}
/**
* Processes an entry for one row in a {@link KeyedValues2D}.
*
* @param rowObj the series object.
* @param columnKeys the required column keys.
* @param dataset the dataset.
*/
@SuppressWarnings("unchecked")
static void processRow(Object rowObj, List columnKeys,
DefaultKeyedValues2D dataset) {
if (!(rowObj instanceof List)) {
throw new RuntimeException("Check the 'data' section it contains "
+ "a row that does not parse to a list.");
}
// we expect the row data object to be an array containing the
// rowKey and rowValueArray entries, where rowValueArray
// should have the same number of entries as the columnKeys
List rowList = (List) rowObj;
Object rowKey = rowList.get(0);
Object rowDataObj = rowList.get(1);
if (!(rowDataObj instanceof List)) {
throw new RuntimeException("Please check the row entry for "
+ rowKey + " because it is not parsing to a list (of "
+ "rowKey and rowDataValues items.");
}
List> rowData = (List>) rowDataObj;
if (rowData.size() != columnKeys.size()) {
throw new RuntimeException("The values list for series "
+ rowKey + " does not contain the correct number of "
+ "entries to match the columnKeys.");
}
for (int c = 0; c < rowData.size(); c++) {
Object columnKey = columnKeys.get(c);
dataset.setValue(objToDouble(rowData.get(c)),
rowKey.toString(), columnKey.toString());
}
}
/**
* Writes a data table to a string in JSON format.
*
* @param data the data ({@code null} not permitted).
*
* @return The string.
*/
public static String writeKeyedValues2D(KeyedValues2D data) {
Args.nullNotPermitted(data, "data");
StringWriter sw = new StringWriter();
try {
writeKeyedValues2D(data, sw);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
return sw.toString();
}
/**
* Writes the data in JSON format to the supplied writer.
*
* @param data the data ({@code null} not permitted).
* @param writer the writer ({@code null} not permitted).
*
* @throws IOException if there is an I/O problem.
*/
@SuppressWarnings("unchecked")
public static void writeKeyedValues2D(KeyedValues2D data, Writer writer)
throws IOException {
Args.nullNotPermitted(data, "data");
Args.nullNotPermitted(writer, "writer");
List columnKeys = data.getColumnKeys();
List rowKeys = data.getRowKeys();
writer.write("{");
if (!columnKeys.isEmpty()) {
writer.write("\"columnKeys\": [");
boolean first = true;
for (Comparable columnKey : columnKeys) {
if (!first) {
writer.write(", ");
} else {
first = false;
}
writer.write(JSONValue.toJSONString(columnKey.toString()));
}
writer.write("]");
}
if (!rowKeys.isEmpty()) {
writer.write(", \"rows\": [");
boolean firstRow = true;
for (Comparable rowKey : rowKeys) {
if (!firstRow) {
writer.write(", [");
} else {
writer.write("[");
firstRow = false;
}
// write the row data
writer.write(JSONValue.toJSONString(rowKey.toString()));
writer.write(", [");
boolean first = true;
for (Comparable columnKey : columnKeys) {
if (!first) {
writer.write(", ");
} else {
first = false;
}
writer.write(JSONValue.toJSONString(data.getValue(rowKey,
columnKey)));
}
writer.write("]]");
}
writer.write("]");
}
writer.write("}");
}
/**
* Parses the supplied string and (if possible) creates a
* {@link KeyedValues3D} instance.
*
* @param json the JSON string ({@code null} not permitted).
*
* @return A {@code KeyedValues3D} instance.
*/
public static KeyedValues3D
readKeyedValues3D(String json) {
StringReader in = new StringReader(json);
KeyedValues3D result;
try {
result = readKeyedValues3D(in);
} catch (IOException ex) {
// not for StringReader
result = null;
}
return result;
}
/**
* Parses character data from the reader and (if possible) creates a
* {@link KeyedValues3D} instance. This method will read back the data
* written by {@link JSONUtils#writeKeyedValues3D(
* org.jfree.chart3d.data.KeyedValues3D, java.io.Writer) }.
*
* @param reader the reader ({@code null} not permitted).
*
* @return A {@code KeyedValues3D} instance.
*
* @throws IOException if there is an I/O problem.
*/
@SuppressWarnings("unchecked")
public static KeyedValues3D
readKeyedValues3D(Reader reader) throws IOException {
JSONParser parser = new JSONParser();
try {
Map map = (Map) parser.parse(reader, createContainerFactory());
StandardCategoryDataset3D result = new StandardCategoryDataset3D();
if (map.isEmpty()) {
return result;
}
// read the row keys, we'll use these to validate the row keys
// supplied with the data
Object rowKeysObj = map.get("rowKeys");
List rowKeys;
if (rowKeysObj instanceof List) {
rowKeys = (List) rowKeysObj;
} else {
if (rowKeysObj == null) {
throw new RuntimeException("No 'rowKeys' defined.");
} else {
throw new RuntimeException("Please check the 'rowKeys', "
+ "the format does not parse to a list.");
}
}
// read the column keys, the data is provided later in rows that
// should have the same number of entries as the columnKeys list
Object columnKeysObj = map.get("columnKeys");
List columnKeys;
if (columnKeysObj instanceof List) {
columnKeys = (List) columnKeysObj;
} else {
if (columnKeysObj == null) {
throw new RuntimeException("No 'columnKeys' defined.");
} else {
throw new RuntimeException("Please check the 'columnKeys', "
+ "the format does not parse to a list.");
}
}
// the data object should be a list of data series
Object dataObj = map.get("data");
if (dataObj instanceof List) {
List seriesList = (List) dataObj;
// each entry in the map has the series name as the key, and
// the value is a map of row data (rowKey, list of values)
for (Object seriesObj : seriesList) {
processSeries(seriesObj, rowKeys, columnKeys, result);
}
} else { // the 'data' entry is not parsing to a list
if (dataObj == null) {
throw new RuntimeException("No 'data' section defined.");
} else {
throw new RuntimeException("Please check the 'data' "
+ "entry, the format does not parse to a list of "
+ "series.");
}
}
return result;
} catch (ParseException ex) {
throw new RuntimeException(ex);
}
}
/**
* Processes an entry for one series.
*
* @param seriesObj the series object.
* @param rowKeys the expected row keys.
* @param columnKeys the required column keys.
*/
static , C extends Comparable>
void processSeries(Object seriesObj, List rowKeys,
List columnKeys,
StandardCategoryDataset3D dataset) {
if (!(seriesObj instanceof Map)) {
throw new RuntimeException("Check the 'data' section it contains "
+ "a series that does not parse to a map.");
}
// we expect the series data object to be a map of
// rowKey ==> rowValueArray entries, where rowValueArray
// should have the same number of entries as the columnKeys
Map seriesMap = (Map) seriesObj;
Object seriesKey = seriesMap.get("seriesKey");
Object seriesRowsObj = seriesMap.get("rows");
if (!(seriesRowsObj instanceof Map)) {
throw new RuntimeException("Please check the series entry for "
+ seriesKey + " because it is not parsing to a map (of "
+ "rowKey -> rowDataValues items.");
}
Map, ?> seriesData = (Map, ?>) seriesRowsObj;
for (Object rowKey : seriesData.keySet()) {
if (!rowKeys.contains(rowKey)) {
throw new RuntimeException("The row key " + rowKey + " is not "
+ "listed in the rowKeys entry.");
}
Object rowValuesObj = seriesData.get(rowKey);
if (!(rowValuesObj instanceof List>)) {
throw new RuntimeException("Please check the entry for series "
+ seriesKey + " and row " + rowKey + " because it "
+ "does not parse to a list of values.");
}
List> rowValues = (List>) rowValuesObj;
if (rowValues.size() != columnKeys.size()) {
throw new RuntimeException("The values list for series "
+ seriesKey + " and row " + rowKey + " does not "
+ "contain the correct number of entries to match "
+ "the columnKeys.");
}
for (int c = 0; c < rowValues.size(); c++) {
Object columnKey = columnKeys.get(c);
dataset.addValue(objToDouble(rowValues.get(c)),
seriesKey.toString(), rowKey.toString(),
columnKey.toString());
}
}
}
/**
* Returns a string containing the data in JSON format.
*
* @param dataset the data ({@code null} not permitted).
*
* @return A string in JSON format.
*/
public static String writeKeyedValues3D(KeyedValues3D dataset) {
Args.nullNotPermitted(dataset, "dataset");
StringWriter sw = new StringWriter();
try {
writeKeyedValues3D(dataset, sw);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
return sw.toString();
}
/**
* Writes the dataset in JSON format to the supplied writer.
*
* @param dataset the dataset ({@code null} not permitted).
* @param writer the writer ({@code null} not permitted).
*
* @throws IOException if there is an I/O problem.
*/
@SuppressWarnings("unchecked")
public static void writeKeyedValues3D(KeyedValues3D dataset, Writer writer)
throws IOException {
Args.nullNotPermitted(dataset, "dataset");
Args.nullNotPermitted(writer, "writer");
writer.write("{");
if (!dataset.getColumnKeys().isEmpty()) {
writer.write("\"columnKeys\": [");
boolean first = true;
for (Object key : dataset.getColumnKeys()) {
if (!first) {
writer.write(", ");
} else {
first = false;
}
writer.write(JSONValue.toJSONString(key.toString()));
}
writer.write("], ");
}
// write the row keys
if (!dataset.getRowKeys().isEmpty()) {
writer.write("\"rowKeys\": [");
boolean first = true;
for (Object key : dataset.getRowKeys()) {
if (!first) {
writer.write(", ");
} else {
first = false;
}
writer.write(JSONValue.toJSONString(key.toString()));
}
writer.write("], ");
}
// write the data which is zero, one or many data series
// a data series has a 'key' and a 'rows' attribute
// the 'rows' attribute is a Map from 'rowKey' -> array of data values
if (dataset.getSeriesCount() != 0) {
writer.write("\"series\": [");
boolean first = true;
for (Object seriesKey : dataset.getSeriesKeys()) {
if (!first) {
writer.write(", ");
} else {
first = false;
}
writer.write("{\"seriesKey\": ");
writer.write(JSONValue.toJSONString(seriesKey.toString()));
writer.write(", \"rows\": [");
boolean firstRow = true;
for (Object rowKey : dataset.getRowKeys()) {
if (countForRowInSeries(dataset, (Comparable) seriesKey,
(Comparable) rowKey) > 0) {
if (!firstRow) {
writer.write(", [");
} else {
writer.write("[");
firstRow = false;
}
// write the row values
writer.write(JSONValue.toJSONString(rowKey.toString())
+ ", [");
for (int c = 0; c < dataset.getColumnCount(); c++) {
Object columnKey = dataset.getColumnKey(c);
if (c != 0) {
writer.write(", ");
}
writer.write(JSONValue.toJSONString(
dataset.getValue((Comparable) seriesKey,
(Comparable) rowKey,
(Comparable) columnKey)));
}
writer.write("]]");
}
}
writer.write("]}");
}
writer.write("]");
}
writer.write("}");
}
/**
* Returns the number of non-{@code null} entries for the specified
* series and row.
*
* @param data the dataset ({@code null} not permitted).
* @param seriesKey the series key ({@code null} not permitted).
* @param rowKey the row key ({@code null} not permitted).
*
* @return The count.
*/
@SuppressWarnings("unchecked")
private static int countForRowInSeries(KeyedValues3D data,
Comparable seriesKey, Comparable rowKey) {
Args.nullNotPermitted(data, "data");
Args.nullNotPermitted(seriesKey, "seriesKey");
Args.nullNotPermitted(rowKey, "rowKey");
int seriesIndex = data.getSeriesIndex(seriesKey);
if (seriesIndex < 0) {
throw new IllegalArgumentException("Series not found: "
+ seriesKey);
}
int rowIndex = data.getRowIndex(rowKey);
if (rowIndex < 0) {
throw new IllegalArgumentException("Row not found: " + rowKey);
}
int count = 0;
for (int c = 0; c < data.getColumnCount(); c++) {
Object n = data.getValue(seriesIndex, rowIndex, c);
if (n != null) {
count++;
}
}
return count;
}
/**
* Parses the string and (if possible) creates an {XYZDataset} instance
* that represents the data. This method will read back the data that
* is written by
* {@link #writeXYZDataset(org.jfree.chart3d.data.xyz.XYZDataset)}.
*
* @param json a JSON formatted string ({@code null} not permitted).
*
* @return A dataset.
*
* @see #writeXYZDataset(org.jfree.chart3d.data.xyz.XYZDataset)
*/
public static XYZDataset readXYZDataset(String json) {
Args.nullNotPermitted(json, "json");
StringReader in = new StringReader(json);
XYZDataset result;
try {
result = readXYZDataset(in);
} catch (IOException ex) {
// not for StringReader
result = null;
}
return result;
}
/**
* Parses character data from the reader and (if possible) creates an
* {XYZDataset} instance that represents the data.
*
* @param reader a reader ({@code null} not permitted).
*
* @return A dataset.
*
* @throws IOException if there is an I/O problem.
*/
@SuppressWarnings("unchecked")
public static XYZDataset readXYZDataset(Reader reader) throws IOException {
JSONParser parser = new JSONParser();
XYZSeriesCollection result = new XYZSeriesCollection<>();
try {
List> list = (List>) parser.parse(reader,
createContainerFactory());
// each entry in the array should be a series array (where the
// first item is the series name and the next value is an array
// (of arrays of length 3) containing the data items
for (Object seriesArray : list) {
if (seriesArray instanceof List) {
List> seriesList = (List>) seriesArray;
XYZSeries series = createSeries(seriesList);
result.add(series);
} else {
throw new RuntimeException(
"Input for a series did not parse to a list.");
}
}
} catch (ParseException ex) {
throw new RuntimeException(ex);
}
return result;
}
/**
* Returns a string containing the dataset in JSON format.
*
* @param dataset the dataset ({@code null} not permitted).
*
* @return A string in JSON format.
*/
public static String writeXYZDataset(XYZDataset dataset) {
StringWriter sw = new StringWriter();
try {
writeXYZDataset(dataset, sw);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
return sw.toString();
}
/**
* Writes the dataset in JSON format to the supplied writer.
*
* @param dataset the data ({@code null} not permitted).
* @param writer the writer ({@code null} not permitted).
*
* @throws IOException if there is an I/O problem.
*/
@SuppressWarnings("unchecked")
public static void writeXYZDataset(XYZDataset dataset, Writer writer)
throws IOException {
writer.write("[");
boolean first = true;
for (Object seriesKey : dataset.getSeriesKeys()) {
if (!first) {
writer.write(", [");
} else {
writer.write("[");
first = false;
}
writer.write(JSONValue.toJSONString(seriesKey.toString()));
writer.write(", [");
int seriesIndex = dataset.getSeriesIndex((Comparable) seriesKey);
int itemCount = dataset.getItemCount(seriesIndex);
for (int i = 0; i < itemCount; i++) {
if (i != 0) {
writer.write(", ");
}
writer.write("[");
writer.write(JSONValue.toJSONString(
dataset.getX(seriesIndex, i)));
writer.write(", ");
writer.write(JSONValue.toJSONString(
dataset.getY(seriesIndex, i)));
writer.write(", ");
writer.write(JSONValue.toJSONString(
dataset.getZ(seriesIndex, i)));
writer.write("]");
}
writer.write("]]");
}
writer.write("]");
}
/**
* Converts an arbitrary object to a double.
*
* @param obj an object ({@code null} permitted).
*
* @return A double primitive (possibly Double.NaN).
*/
private static double objToDouble(Object obj) {
if (obj == null) {
return Double.NaN;
}
if (obj instanceof Number) {
return ((Number) obj).doubleValue();
}
double result = Double.NaN;
try {
result = Double.parseDouble(obj.toString());
} catch (Exception e) {
}
return result;
}
/**
* Creates an {@link XYZSeries} from the supplied list. The list is
* coming from the JSON parser and should contain the series name as the
* first item, and a list of data items as the second item. The list of
* data items should be a list of lists (
*
* @param sArray the series array.
*
* @return A data series.
*/
@SuppressWarnings("unchecked")
private static XYZSeries createSeries(List> sArray) {
Comparable> key = (Comparable>) sArray.get(0);
List> dataItems = (List>) sArray.get(1);
XYZSeries series = new XYZSeries(key);
for (Object item : dataItems) {
if (item instanceof List>) {
List> xyz = (List>) item;
if (xyz.size() != 3) {
throw new RuntimeException(
"A data item should contain three numbers, "
+ "but we have " + xyz);
}
double x = objToDouble(xyz.get(0));
double y = objToDouble(xyz.get(1));
double z = objToDouble(xyz.get(2));
series.add(x, y, z);
} else {
throw new RuntimeException(
"Expecting a data item (x, y, z) for series " + key
+ " but found " + item + ".");
}
}
return series;
}
/**
* Returns a custom container factory for the JSON parser. We create this
* so that the collections respect the order of elements.
*
* @return The container factory.
*/
private static ContainerFactory createContainerFactory() {
return new ContainerFactory() {
@Override
public Map createObjectContainer() {
return new LinkedHashMap();
}
@Override
public List creatArrayContainer() {
return new ArrayList();
}
};
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/data/KeyedValue.java 0000664 0000000 0000000 00000003606 14173312715 0025625 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.data;
/**
* A {@code (key, value)} pair that is used as a building block for some
* data structures for the charts.
*
* @param The key type.
* @param The value type.
*/
public interface KeyedValue, T> {
/**
* Returns the key (by design, this key is required to be
* non-{@code null}).
*
* @return The key (never {@code null}).
*/
K getKey();
/**
* Returns the value.
*
* @return The value (possibly {@code null}).
*/
T getValue();
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/data/KeyedValues.java 0000664 0000000 0000000 00000005053 14173312715 0026006 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.data;
import java.util.List;
/**
* A list of values that are associated with unique keys.
*
* @param the key type (must implement Comparable).
* @param the value type.
*/
public interface KeyedValues, T> extends Values {
/**
* Returns the key for the specified item in the list.
*
* @param index the item index.
*
* @return The key.
*/
K getKey(int index);
/**
* Returns the index for the specified key, or {@code -1} if the key
* is not present in the list.
*
* @param key the key ({@code null} not permitted).
*
* @return The item index, or {@code -1}.
*/
int getIndex(K key);
/**
* Returns a list of all the keys. Note that the list will be a copy, so
* modifying it will not impact this data structure.
*
* @return A list of keys (possibly empty, but never {@code null}).
*/
List getKeys();
/**
* Returns the value associated with the specified key, or
* {@code null}.
*
* @param key the key ({@code null} not permitted).
*
* @return The value (possibly {@code null}).
*/
T getValue(K key);
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/data/KeyedValues2D.java 0000664 0000000 0000000 00000007767 14173312715 0026212 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.data;
import java.util.List;
/**
* A two dimensional grid of data values where each value is uniquely
* identified by two keys (the {@code rowKey} and the
* {@code columnKey}). Any instance of {@code Comparable} can be
* used as a key ({@code String} objects are instances of
* {@code Comparable}, making them convenient key objects).
*
* @param The row key type.
* @param The column key type.
* @param The value type.
*/
public interface KeyedValues2D,
C extends Comparable, T> extends Values2D {
/**
* Returns the row key with the specified index.
*
* @param rowIndex the row index.
*
* @return The key.
*/
R getRowKey(int rowIndex);
/**
* Returns the column key with the specified index.
*
* @param columnIndex the index.
*
* @return The key.
*/
C getColumnKey(int columnIndex);
/**
* Returns the index of the specified key, or {@code -1} if there
* is no such key.
*
* @param rowKey the row key ({@code null} not permitted).
*
* @return The index, or {@code -1}.
*/
int getRowIndex(R rowKey);
/**
* Returns the index of the specified key, or {@code -1} if there
* is no such key.
*
* @param columnKey the column key ({@code null} not permitted).
*
* @return The index, or {@code -1}.
*/
int getColumnIndex(C columnKey);
/**
* Returns a list of the row keys (the order is significant, since data
* values can be accessed by index as well as by key).
*
* NOTE: this method must be implemented so that modifications to the
* returned list do not impact the underlying data structure.
*
* @return A list of row keys.
*/
List getRowKeys();
/**
* Returns a list of the column keys (the order is significant, since data
* values can be accessed by index as well as by key).
*
* NOTE: this method must be implemented so that modifications to the
* returned list do not impact the underlying data structure.
*
* @return A list of column keys.
*/
List getColumnKeys();
/**
* Returns the value (possibly {@code null}) associated with the
* specified keys. If either or both of the keys is not defined in this
* data structure, a runtime exception will be thrown.
*
* @param rowKey the row key ({@code null} not permitted).
* @param columnKey the column key ({@code null} not permitted).
*
* @return The value (possibly {@code null}).
*/
T getValue(R rowKey, C columnKey);
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/data/KeyedValues3D.java 0000664 0000000 0000000 00000010720 14173312715 0026172 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.data;
import java.util.List;
/**
* A three dimensional cube of data values where each value is uniquely
* identified by three keys (the {@code seriesKey}, {@code rowKey}
* and {@code columnKey}).
*
* @param The series key type.
* @param The row key type.
* @param The column key type.
* @param The value type.
*/
public interface KeyedValues3D,
R extends Comparable, C extends Comparable, T>
extends Values3D {
/**
* Returns a list of the series keys for the dataset. Modifying this
* list will have no impact on the underlying dataset.
*
* @return A list of the series keys (possibly empty, but never
* {@code null}).
*/
List getSeriesKeys();
/**
* Returns a list of the row keys for the dataset. Modifying this
* list will have no impact on the underlying dataset.
*
* @return A list of the row keys (possibly empty, but never
* {@code null}).
*/
List getRowKeys();
/**
* Returns a list of the column keys for the dataset. Modifying this
* list will have no impact on the underlying dataset.
*
* @return A list of the column keys (possibly empty, but never
* {@code null}).
*/
List getColumnKeys();
/**
* Returns the series key with the specified index.
*
* @param seriesIndex the series index.
*
* @return The key.
*/
S getSeriesKey(int seriesIndex);
/**
* Returns the row key with the specified index.
*
* @param rowIndex the row index.
*
* @return The key.
*/
R getRowKey(int rowIndex);
/**
* Returns the column key with the specified index.
*
* @param columnIndex the column index.
*
* @return The key.
*/
C getColumnKey(int columnIndex);
/**
* Returns the index of the specified series key, or {@code -1} if
* there is no matching key.
*
* @param serieskey the series key ({@code null} not permitted).
*
* @return The key index, or {@code -1}.
*/
int getSeriesIndex(S serieskey);
/**
* Returns the index of the specified row key, or {@code -1} if there
* is no matching key.
*
* @param rowkey the row key ({@code null} not permitted).
*
* @return The row index or {@code -1}.
*/
int getRowIndex(R rowkey);
/**
* Returns the index of the specified column key, or {@code -1} if
* there is no matching key.
*
* @param columnkey the column key ({@code null} not permitted).
*
* @return The column index or {@code -1}.
*/
int getColumnIndex(C columnkey);
/**
* Returns the value for a given series, row and column.
*
* @param seriesKey the series key ({@code null} not permitted).
* @param rowKey the row key ({@code null} not permitted).
* @param columnKey the column key ({@code null} not permitted).
*
* @return The value (possibly {@code null}).
*/
T getValue(S seriesKey, R rowKey, C columnKey);
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/data/KeyedValues3DItemKey.java 0000664 0000000 0000000 00000012007 14173312715 0027462 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.data;
import java.io.Serializable;
import org.jfree.chart3d.internal.Args;
import org.jfree.chart3d.internal.ObjectUtils;
/**
* An object that references one data item in a {@link KeyedValues3D} data
* structure. Instances of this class are immutable (subject to the caller
* using series, row and column keys that are immutable).
*
* @since 1.3
*/
public class KeyedValues3DItemKey,
R extends Comparable, C extends Comparable>
implements ItemKey, Comparable>,
Serializable {
/** The series key. */
S seriesKey;
/** The row key. */
R rowKey;
/** The column key. */
C columnKey;
/**
* Creates a new instance.
*
* @param seriesKey the series key ({@code null} not permitted).
* @param rowKey the row key ({@code null} not permitted).
* @param columnKey the column key ({@code null} not permitted).
*/
public KeyedValues3DItemKey(S seriesKey, R rowKey, C columnKey) {
Args.nullNotPermitted(seriesKey, "seriesKey");
Args.nullNotPermitted(rowKey, "rowKey");
Args.nullNotPermitted(columnKey, "columnKey");
this.seriesKey = seriesKey;
this.rowKey = rowKey;
this.columnKey = columnKey;
}
/**
* Returns the series key.
*
* @return The series key (never {@code null}).
*/
public S getSeriesKey() {
return this.seriesKey;
}
/**
* Returns the row key.
*
* @return The row key (never {@code null}).
*/
public R getRowKey() {
return this.rowKey;
}
/**
* Returns the column key.
*
* @return The column key (never {@code null}).
*/
public C getColumnKey() {
return this.columnKey;
}
@Override
public int compareTo(KeyedValues3DItemKey key) {
int result = this.seriesKey.compareTo(key.getSeriesKey());
if (result == 0) {
result = this.rowKey.compareTo(key.rowKey);
if (result == 0) {
result = this.columnKey.compareTo(key.columnKey);
}
}
return result;
}
/**
* Tests this key for equality with an arbitrary object.
*
* @param obj the object ({@code null} permitted).
*
* @return A boolean.
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof KeyedValues3DItemKey)) {
return false;
}
KeyedValues3DItemKey that = (KeyedValues3DItemKey) obj;
if (!this.seriesKey.equals(that.seriesKey)) {
return false;
}
if (!this.rowKey.equals(that.rowKey)) {
return false;
}
if (!this.columnKey.equals(that.columnKey)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 3;
hash = 17 * hash + ObjectUtils.hashCode(this.seriesKey);
hash = 17 * hash + ObjectUtils.hashCode(this.rowKey);
hash = 17 * hash + ObjectUtils.hashCode(this.columnKey);
return hash;
}
@Override
public String toJSONString() {
return "{\"seriesKey\": \"" + this.seriesKey.toString() + "\", "
+ "\"rowKey\": \"" + this.rowKey.toString() + "\", "
+ "\"columnKey\": \"" + this.columnKey.toString() + "\"}";
}
@Override
public String toString() {
return "Values3DItemKey[series=" + seriesKey.toString()
+ ",row=" + rowKey.toString()
+ ",column=" + columnKey.toString() + "]";
}
}
orson-charts-2.1.0/src/main/java/org/jfree/chart3d/data/KeyedValues3DItemKeys.java 0000664 0000000 0000000 00000012673 14173312715 0027656 0 ustar 00root root 0000000 0000000 /* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package org.jfree.chart3d.data;
import java.util.ArrayList;
import java.util.Collection;
import org.jfree.chart3d.internal.Args;
/**
* Utility methods related to the {@link KeyedValues3DItemKey} class.
*
* @since 1.3
*/
public class KeyedValues3DItemKeys {
private KeyedValues3DItemKeys() {
// no need to instantiate this
}
/**
* Returns a collection containing all the item keys for the specified
* series.
*
* @param data the data ({@code null} not permitted).
* @param seriesKey the series key ({@code null} not permitted).
*
* @param the series key type
* @param the row key type
* @param the column key type
* @param the value type
*
* @return A collection of item keys (never {@code null}).
*/
public static