diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..ffb2c3e --- /dev/null +++ b/.classpath @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..7ca0e43 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + JDraftingProject_1_12 + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..0fee6a9 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,15 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e72bfdd --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/bin/LICENSE.txt b/bin/LICENSE.txt new file mode 100644 index 0000000..1a233af --- /dev/null +++ b/bin/LICENSE.txt @@ -0,0 +1,16 @@ +//////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016,2020,2021 Miguel Alejandro Moreno Barrientos +// +// JDrafting 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. +// +// JDrafting 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 JDrafting. If not, see . +//////////////////////////////////////////////////////////////////////// diff --git a/bin/jdrafting/Exercise.class b/bin/jdrafting/Exercise.class new file mode 100644 index 0000000..a82a060 Binary files /dev/null and b/bin/jdrafting/Exercise.class differ diff --git a/bin/jdrafting/geom/JDMath.class b/bin/jdrafting/geom/JDMath.class new file mode 100644 index 0000000..8368beb Binary files /dev/null and b/bin/jdrafting/geom/JDMath.class differ diff --git a/bin/jdrafting/geom/JDPoint.class b/bin/jdrafting/geom/JDPoint.class new file mode 100644 index 0000000..38a74ea Binary files /dev/null and b/bin/jdrafting/geom/JDPoint.class differ diff --git a/bin/jdrafting/geom/JDStrokes$1.class b/bin/jdrafting/geom/JDStrokes$1.class new file mode 100644 index 0000000..8918105 Binary files /dev/null and b/bin/jdrafting/geom/JDStrokes$1.class differ diff --git a/bin/jdrafting/geom/JDStrokes$2.class b/bin/jdrafting/geom/JDStrokes$2.class new file mode 100644 index 0000000..43090a2 Binary files /dev/null and b/bin/jdrafting/geom/JDStrokes$2.class differ diff --git a/bin/jdrafting/geom/JDStrokes$3.class b/bin/jdrafting/geom/JDStrokes$3.class new file mode 100644 index 0000000..f8e6d4b Binary files /dev/null and b/bin/jdrafting/geom/JDStrokes$3.class differ diff --git a/bin/jdrafting/geom/JDStrokes$4.class b/bin/jdrafting/geom/JDStrokes$4.class new file mode 100644 index 0000000..305d17f Binary files /dev/null and b/bin/jdrafting/geom/JDStrokes$4.class differ diff --git a/bin/jdrafting/geom/JDStrokes$5.class b/bin/jdrafting/geom/JDStrokes$5.class new file mode 100644 index 0000000..c0a233e Binary files /dev/null and b/bin/jdrafting/geom/JDStrokes$5.class differ diff --git a/bin/jdrafting/geom/JDStrokes.class b/bin/jdrafting/geom/JDStrokes.class new file mode 100644 index 0000000..57418f2 Binary files /dev/null and b/bin/jdrafting/geom/JDStrokes.class differ diff --git a/bin/jdrafting/geom/JDraftingShape.class b/bin/jdrafting/geom/JDraftingShape.class new file mode 100644 index 0000000..366e032 Binary files /dev/null and b/bin/jdrafting/geom/JDraftingShape.class differ diff --git a/bin/jdrafting/gui/Application$1.class b/bin/jdrafting/gui/Application$1.class new file mode 100644 index 0000000..8c71040 Binary files /dev/null and b/bin/jdrafting/gui/Application$1.class differ diff --git a/bin/jdrafting/gui/Application$10.class b/bin/jdrafting/gui/Application$10.class new file mode 100644 index 0000000..7be7966 Binary files /dev/null and b/bin/jdrafting/gui/Application$10.class differ diff --git a/bin/jdrafting/gui/Application$11.class b/bin/jdrafting/gui/Application$11.class new file mode 100644 index 0000000..837ed9e Binary files /dev/null and b/bin/jdrafting/gui/Application$11.class differ diff --git a/bin/jdrafting/gui/Application$12.class b/bin/jdrafting/gui/Application$12.class new file mode 100644 index 0000000..f70e1c1 Binary files /dev/null and b/bin/jdrafting/gui/Application$12.class differ diff --git a/bin/jdrafting/gui/Application$13.class b/bin/jdrafting/gui/Application$13.class new file mode 100644 index 0000000..bd45a67 Binary files /dev/null and b/bin/jdrafting/gui/Application$13.class differ diff --git a/bin/jdrafting/gui/Application$14.class b/bin/jdrafting/gui/Application$14.class new file mode 100644 index 0000000..e52f707 Binary files /dev/null and b/bin/jdrafting/gui/Application$14.class differ diff --git a/bin/jdrafting/gui/Application$15.class b/bin/jdrafting/gui/Application$15.class new file mode 100644 index 0000000..683d5c4 Binary files /dev/null and b/bin/jdrafting/gui/Application$15.class differ diff --git a/bin/jdrafting/gui/Application$16.class b/bin/jdrafting/gui/Application$16.class new file mode 100644 index 0000000..07f1b40 Binary files /dev/null and b/bin/jdrafting/gui/Application$16.class differ diff --git a/bin/jdrafting/gui/Application$17.class b/bin/jdrafting/gui/Application$17.class new file mode 100644 index 0000000..5353f46 Binary files /dev/null and b/bin/jdrafting/gui/Application$17.class differ diff --git a/bin/jdrafting/gui/Application$18.class b/bin/jdrafting/gui/Application$18.class new file mode 100644 index 0000000..3f6fece Binary files /dev/null and b/bin/jdrafting/gui/Application$18.class differ diff --git a/bin/jdrafting/gui/Application$19.class b/bin/jdrafting/gui/Application$19.class new file mode 100644 index 0000000..a973b1d Binary files /dev/null and b/bin/jdrafting/gui/Application$19.class differ diff --git a/bin/jdrafting/gui/Application$2.class b/bin/jdrafting/gui/Application$2.class new file mode 100644 index 0000000..b09d42f Binary files /dev/null and b/bin/jdrafting/gui/Application$2.class differ diff --git a/bin/jdrafting/gui/Application$20.class b/bin/jdrafting/gui/Application$20.class new file mode 100644 index 0000000..f824698 Binary files /dev/null and b/bin/jdrafting/gui/Application$20.class differ diff --git a/bin/jdrafting/gui/Application$3.class b/bin/jdrafting/gui/Application$3.class new file mode 100644 index 0000000..94ecbe1 Binary files /dev/null and b/bin/jdrafting/gui/Application$3.class differ diff --git a/bin/jdrafting/gui/Application$4.class b/bin/jdrafting/gui/Application$4.class new file mode 100644 index 0000000..0b27873 Binary files /dev/null and b/bin/jdrafting/gui/Application$4.class differ diff --git a/bin/jdrafting/gui/Application$5.class b/bin/jdrafting/gui/Application$5.class new file mode 100644 index 0000000..f5409f4 Binary files /dev/null and b/bin/jdrafting/gui/Application$5.class differ diff --git a/bin/jdrafting/gui/Application$6.class b/bin/jdrafting/gui/Application$6.class new file mode 100644 index 0000000..1d36f39 Binary files /dev/null and b/bin/jdrafting/gui/Application$6.class differ diff --git a/bin/jdrafting/gui/Application$7.class b/bin/jdrafting/gui/Application$7.class new file mode 100644 index 0000000..ba5b198 Binary files /dev/null and b/bin/jdrafting/gui/Application$7.class differ diff --git a/bin/jdrafting/gui/Application$8.class b/bin/jdrafting/gui/Application$8.class new file mode 100644 index 0000000..ca8087b Binary files /dev/null and b/bin/jdrafting/gui/Application$8.class differ diff --git a/bin/jdrafting/gui/Application$9.class b/bin/jdrafting/gui/Application$9.class new file mode 100644 index 0000000..dee5b11 Binary files /dev/null and b/bin/jdrafting/gui/Application$9.class differ diff --git a/bin/jdrafting/gui/Application$EditAddShapeToExercise.class b/bin/jdrafting/gui/Application$EditAddShapeToExercise.class new file mode 100644 index 0000000..141bab4 Binary files /dev/null and b/bin/jdrafting/gui/Application$EditAddShapeToExercise.class differ diff --git a/bin/jdrafting/gui/Application$EditRemoveShapeFromExercise.class b/bin/jdrafting/gui/Application$EditRemoveShapeFromExercise.class new file mode 100644 index 0000000..721d475 Binary files /dev/null and b/bin/jdrafting/gui/Application$EditRemoveShapeFromExercise.class differ diff --git a/bin/jdrafting/gui/Application$ExtendedUndoManager.class b/bin/jdrafting/gui/Application$ExtendedUndoManager.class new file mode 100644 index 0000000..665b36d Binary files /dev/null and b/bin/jdrafting/gui/Application$ExtendedUndoManager.class differ diff --git a/bin/jdrafting/gui/Application$UndoRedoListener.class b/bin/jdrafting/gui/Application$UndoRedoListener.class new file mode 100644 index 0000000..4b1130a Binary files /dev/null and b/bin/jdrafting/gui/Application$UndoRedoListener.class differ diff --git a/bin/jdrafting/gui/Application.class b/bin/jdrafting/gui/Application.class new file mode 100644 index 0000000..fddfa18 Binary files /dev/null and b/bin/jdrafting/gui/Application.class differ diff --git a/bin/jdrafting/gui/CanvasPanel$1.class b/bin/jdrafting/gui/CanvasPanel$1.class new file mode 100644 index 0000000..6224d9e Binary files /dev/null and b/bin/jdrafting/gui/CanvasPanel$1.class differ diff --git a/bin/jdrafting/gui/CanvasPanel$MoveCanvasThread.class b/bin/jdrafting/gui/CanvasPanel$MoveCanvasThread.class new file mode 100644 index 0000000..9894780 Binary files /dev/null and b/bin/jdrafting/gui/CanvasPanel$MoveCanvasThread.class differ diff --git a/bin/jdrafting/gui/CanvasPanel.class b/bin/jdrafting/gui/CanvasPanel.class new file mode 100644 index 0000000..d285ec8 Binary files /dev/null and b/bin/jdrafting/gui/CanvasPanel.class differ diff --git a/bin/jdrafting/gui/EditShapeDialog$1.class b/bin/jdrafting/gui/EditShapeDialog$1.class new file mode 100644 index 0000000..93fed2d Binary files /dev/null and b/bin/jdrafting/gui/EditShapeDialog$1.class differ diff --git a/bin/jdrafting/gui/EditShapeDialog$2.class b/bin/jdrafting/gui/EditShapeDialog$2.class new file mode 100644 index 0000000..98d44b6 Binary files /dev/null and b/bin/jdrafting/gui/EditShapeDialog$2.class differ diff --git a/bin/jdrafting/gui/EditShapeDialog$3.class b/bin/jdrafting/gui/EditShapeDialog$3.class new file mode 100644 index 0000000..597d43f Binary files /dev/null and b/bin/jdrafting/gui/EditShapeDialog$3.class differ diff --git a/bin/jdrafting/gui/EditShapeDialog.class b/bin/jdrafting/gui/EditShapeDialog.class new file mode 100644 index 0000000..71330c8 Binary files /dev/null and b/bin/jdrafting/gui/EditShapeDialog.class differ diff --git a/bin/jdrafting/gui/FileInfoDialog$1.class b/bin/jdrafting/gui/FileInfoDialog$1.class new file mode 100644 index 0000000..778a756 Binary files /dev/null and b/bin/jdrafting/gui/FileInfoDialog$1.class differ diff --git a/bin/jdrafting/gui/FileInfoDialog$2.class b/bin/jdrafting/gui/FileInfoDialog$2.class new file mode 100644 index 0000000..e4d16f8 Binary files /dev/null and b/bin/jdrafting/gui/FileInfoDialog$2.class differ diff --git a/bin/jdrafting/gui/FileInfoDialog.class b/bin/jdrafting/gui/FileInfoDialog.class new file mode 100644 index 0000000..358a4b7 Binary files /dev/null and b/bin/jdrafting/gui/FileInfoDialog.class differ diff --git a/bin/jdrafting/gui/JDCompoundEdit.class b/bin/jdrafting/gui/JDCompoundEdit.class new file mode 100644 index 0000000..c9267c5 Binary files /dev/null and b/bin/jdrafting/gui/JDCompoundEdit.class differ diff --git a/bin/jdrafting/gui/JDUtils.class b/bin/jdrafting/gui/JDUtils.class new file mode 100644 index 0000000..881a7b1 Binary files /dev/null and b/bin/jdrafting/gui/JDUtils.class differ diff --git a/bin/jdrafting/gui/JDraftingArgs.class b/bin/jdrafting/gui/JDraftingArgs.class new file mode 100644 index 0000000..05abb2e Binary files /dev/null and b/bin/jdrafting/gui/JDraftingArgs.class differ diff --git a/bin/jdrafting/gui/Launcher.class b/bin/jdrafting/gui/Launcher.class new file mode 100644 index 0000000..6b55ecd Binary files /dev/null and b/bin/jdrafting/gui/Launcher.class differ diff --git a/bin/jdrafting/gui/LineStyleComboBox$1.class b/bin/jdrafting/gui/LineStyleComboBox$1.class new file mode 100644 index 0000000..8a3babc Binary files /dev/null and b/bin/jdrafting/gui/LineStyleComboBox$1.class differ diff --git a/bin/jdrafting/gui/LineStyleComboBox$LineRenderer.class b/bin/jdrafting/gui/LineStyleComboBox$LineRenderer.class new file mode 100644 index 0000000..a1d816d Binary files /dev/null and b/bin/jdrafting/gui/LineStyleComboBox$LineRenderer.class differ diff --git a/bin/jdrafting/gui/LineStyleComboBox.class b/bin/jdrafting/gui/LineStyleComboBox.class new file mode 100644 index 0000000..cf34083 Binary files /dev/null and b/bin/jdrafting/gui/LineStyleComboBox.class differ diff --git a/bin/jdrafting/gui/ShapeList$ShapeListMouseAdapter.class b/bin/jdrafting/gui/ShapeList$ShapeListMouseAdapter.class new file mode 100644 index 0000000..7866fe8 Binary files /dev/null and b/bin/jdrafting/gui/ShapeList$ShapeListMouseAdapter.class differ diff --git a/bin/jdrafting/gui/ShapeList$ShapeListRenderer$1.class b/bin/jdrafting/gui/ShapeList$ShapeListRenderer$1.class new file mode 100644 index 0000000..05d93f6 Binary files /dev/null and b/bin/jdrafting/gui/ShapeList$ShapeListRenderer$1.class differ diff --git a/bin/jdrafting/gui/ShapeList$ShapeListRenderer.class b/bin/jdrafting/gui/ShapeList$ShapeListRenderer.class new file mode 100644 index 0000000..c1f4a4f Binary files /dev/null and b/bin/jdrafting/gui/ShapeList$ShapeListRenderer.class differ diff --git a/bin/jdrafting/gui/ShapeList$ShapeListSelectionListener.class b/bin/jdrafting/gui/ShapeList$ShapeListSelectionListener.class new file mode 100644 index 0000000..1467fb5 Binary files /dev/null and b/bin/jdrafting/gui/ShapeList$ShapeListSelectionListener.class differ diff --git a/bin/jdrafting/gui/ShapeList.class b/bin/jdrafting/gui/ShapeList.class new file mode 100644 index 0000000..c77901d Binary files /dev/null and b/bin/jdrafting/gui/ShapeList.class differ diff --git a/bin/jdrafting/gui/Toast.class b/bin/jdrafting/gui/Toast.class new file mode 100644 index 0000000..161ffed Binary files /dev/null and b/bin/jdrafting/gui/Toast.class differ diff --git a/bin/jdrafting/gui/ToastCanvasStep$1.class b/bin/jdrafting/gui/ToastCanvasStep$1.class new file mode 100644 index 0000000..f3a8b36 Binary files /dev/null and b/bin/jdrafting/gui/ToastCanvasStep$1.class differ diff --git a/bin/jdrafting/gui/ToastCanvasStep.class b/bin/jdrafting/gui/ToastCanvasStep.class new file mode 100644 index 0000000..e5ba9e7 Binary files /dev/null and b/bin/jdrafting/gui/ToastCanvasStep.class differ diff --git a/bin/jdrafting/gui/UndoRedoHistoryList$1.class b/bin/jdrafting/gui/UndoRedoHistoryList$1.class new file mode 100644 index 0000000..18dae17 Binary files /dev/null and b/bin/jdrafting/gui/UndoRedoHistoryList$1.class differ diff --git a/bin/jdrafting/gui/UndoRedoHistoryList$2.class b/bin/jdrafting/gui/UndoRedoHistoryList$2.class new file mode 100644 index 0000000..703eb98 Binary files /dev/null and b/bin/jdrafting/gui/UndoRedoHistoryList$2.class differ diff --git a/bin/jdrafting/gui/UndoRedoHistoryList$3.class b/bin/jdrafting/gui/UndoRedoHistoryList$3.class new file mode 100644 index 0000000..342cefd Binary files /dev/null and b/bin/jdrafting/gui/UndoRedoHistoryList$3.class differ diff --git a/bin/jdrafting/gui/UndoRedoHistoryList.class b/bin/jdrafting/gui/UndoRedoHistoryList.class new file mode 100644 index 0000000..fced2ad Binary files /dev/null and b/bin/jdrafting/gui/UndoRedoHistoryList.class differ diff --git a/bin/jdrafting/gui/Viewport.class b/bin/jdrafting/gui/Viewport.class new file mode 100644 index 0000000..e9cb24d Binary files /dev/null and b/bin/jdrafting/gui/Viewport.class differ diff --git a/bin/jdrafting/gui/controller/actions/AboutAction.class b/bin/jdrafting/gui/controller/actions/AboutAction.class new file mode 100644 index 0000000..170c5a0 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/AboutAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/AngleAction.class b/bin/jdrafting/gui/controller/actions/AngleAction.class new file mode 100644 index 0000000..8f713a9 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/AngleAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/ArcAction.class b/bin/jdrafting/gui/controller/actions/ArcAction.class new file mode 100644 index 0000000..5309f94 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/ArcAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/AreaIntersectionAction.class b/bin/jdrafting/gui/controller/actions/AreaIntersectionAction.class new file mode 100644 index 0000000..98eef89 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/AreaIntersectionAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/AreaSubstractAction.class b/bin/jdrafting/gui/controller/actions/AreaSubstractAction.class new file mode 100644 index 0000000..90e6b33 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/AreaSubstractAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/AreaSymmetricDifferenceAction.class b/bin/jdrafting/gui/controller/actions/AreaSymmetricDifferenceAction.class new file mode 100644 index 0000000..71bb287 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/AreaSymmetricDifferenceAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/AreaUnionAction.class b/bin/jdrafting/gui/controller/actions/AreaUnionAction.class new file mode 100644 index 0000000..1d90076 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/AreaUnionAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/ArrowAction.class b/bin/jdrafting/gui/controller/actions/ArrowAction.class new file mode 100644 index 0000000..4756f6f Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/ArrowAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/AxialSymmetryAction.class b/bin/jdrafting/gui/controller/actions/AxialSymmetryAction.class new file mode 100644 index 0000000..9fb8846 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/AxialSymmetryAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/BackwardAction.class b/bin/jdrafting/gui/controller/actions/BackwardAction.class new file mode 100644 index 0000000..b5c93f9 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/BackwardAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/BisectrixAction.class b/bin/jdrafting/gui/controller/actions/BisectrixAction.class new file mode 100644 index 0000000..e4cc97a Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/BisectrixAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/BoundsAction.class b/bin/jdrafting/gui/controller/actions/BoundsAction.class new file mode 100644 index 0000000..e9683a5 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/BoundsAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/CanvasColorAction.class b/bin/jdrafting/gui/controller/actions/CanvasColorAction.class new file mode 100644 index 0000000..9f0cf82 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/CanvasColorAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/CapableArcAction.class b/bin/jdrafting/gui/controller/actions/CapableArcAction.class new file mode 100644 index 0000000..c19d815 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/CapableArcAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/CentralSymmetryAction.class b/bin/jdrafting/gui/controller/actions/CentralSymmetryAction.class new file mode 100644 index 0000000..1712ce9 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/CentralSymmetryAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/CircumferenceAction.class b/bin/jdrafting/gui/controller/actions/CircumferenceAction.class new file mode 100644 index 0000000..79182ae Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/CircumferenceAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/CopySelectedAction.class b/bin/jdrafting/gui/controller/actions/CopySelectedAction.class new file mode 100644 index 0000000..7a697ef Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/CopySelectedAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/DeleteSelectedAction$1.class b/bin/jdrafting/gui/controller/actions/DeleteSelectedAction$1.class new file mode 100644 index 0000000..e787a06 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/DeleteSelectedAction$1.class differ diff --git a/bin/jdrafting/gui/controller/actions/DeleteSelectedAction.class b/bin/jdrafting/gui/controller/actions/DeleteSelectedAction.class new file mode 100644 index 0000000..30d436f Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/DeleteSelectedAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/DivisionPointsAction.class b/bin/jdrafting/gui/controller/actions/DivisionPointsAction.class new file mode 100644 index 0000000..4c682e6 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/DivisionPointsAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/EllipseAction.class b/bin/jdrafting/gui/controller/actions/EllipseAction.class new file mode 100644 index 0000000..b862cdd Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/EllipseAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/EndAction.class b/bin/jdrafting/gui/controller/actions/EndAction.class new file mode 100644 index 0000000..353c30b Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/EndAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/ExerciseMetadataAction.class b/bin/jdrafting/gui/controller/actions/ExerciseMetadataAction.class new file mode 100644 index 0000000..472a7aa Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/ExerciseMetadataAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/ExitAction.class b/bin/jdrafting/gui/controller/actions/ExitAction.class new file mode 100644 index 0000000..9a24e8e Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/ExitAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/ExtremesAction.class b/bin/jdrafting/gui/controller/actions/ExtremesAction.class new file mode 100644 index 0000000..206cf06 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/ExtremesAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/EyedropperAction.class b/bin/jdrafting/gui/controller/actions/EyedropperAction.class new file mode 100644 index 0000000..c89aa20 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/EyedropperAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/ForwardAction.class b/bin/jdrafting/gui/controller/actions/ForwardAction.class new file mode 100644 index 0000000..34028f5 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/ForwardAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/FragmentAction.class b/bin/jdrafting/gui/controller/actions/FragmentAction.class new file mode 100644 index 0000000..18f7069 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/FragmentAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/FreeHandAction.class b/bin/jdrafting/gui/controller/actions/FreeHandAction.class new file mode 100644 index 0000000..86fb2f3 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/FreeHandAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/FusionAction.class b/bin/jdrafting/gui/controller/actions/FusionAction.class new file mode 100644 index 0000000..5db005f Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/FusionAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/HomothetyAction.class b/bin/jdrafting/gui/controller/actions/HomothetyAction.class new file mode 100644 index 0000000..886dbbc Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/HomothetyAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/HyperbolaAction.class b/bin/jdrafting/gui/controller/actions/HyperbolaAction.class new file mode 100644 index 0000000..3a3ba6d Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/HyperbolaAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/IntersectionsAction$1.class b/bin/jdrafting/gui/controller/actions/IntersectionsAction$1.class new file mode 100644 index 0000000..8362786 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/IntersectionsAction$1.class differ diff --git a/bin/jdrafting/gui/controller/actions/IntersectionsAction$POJOJoin.class b/bin/jdrafting/gui/controller/actions/IntersectionsAction$POJOJoin.class new file mode 100644 index 0000000..93a4918 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/IntersectionsAction$POJOJoin.class differ diff --git a/bin/jdrafting/gui/controller/actions/IntersectionsAction.class b/bin/jdrafting/gui/controller/actions/IntersectionsAction.class new file mode 100644 index 0000000..722a87f Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/IntersectionsAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/InvertSelectionAction.class b/bin/jdrafting/gui/controller/actions/InvertSelectionAction.class new file mode 100644 index 0000000..67ec65a Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/InvertSelectionAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/LookFeelAction.class b/bin/jdrafting/gui/controller/actions/LookFeelAction.class new file mode 100644 index 0000000..6322bb1 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/LookFeelAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/MathFunctionAction$JMEDialog$1.class b/bin/jdrafting/gui/controller/actions/MathFunctionAction$JMEDialog$1.class new file mode 100644 index 0000000..0d61051 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/MathFunctionAction$JMEDialog$1.class differ diff --git a/bin/jdrafting/gui/controller/actions/MathFunctionAction$JMEDialog$2.class b/bin/jdrafting/gui/controller/actions/MathFunctionAction$JMEDialog$2.class new file mode 100644 index 0000000..c222b5a Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/MathFunctionAction$JMEDialog$2.class differ diff --git a/bin/jdrafting/gui/controller/actions/MathFunctionAction$JMEDialog.class b/bin/jdrafting/gui/controller/actions/MathFunctionAction$JMEDialog.class new file mode 100644 index 0000000..ed01731 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/MathFunctionAction$JMEDialog.class differ diff --git a/bin/jdrafting/gui/controller/actions/MathFunctionAction.class b/bin/jdrafting/gui/controller/actions/MathFunctionAction.class new file mode 100644 index 0000000..88c87b6 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/MathFunctionAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/MediatrixAction.class b/bin/jdrafting/gui/controller/actions/MediatrixAction.class new file mode 100644 index 0000000..523d6c3 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/MediatrixAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/MidpointAction.class b/bin/jdrafting/gui/controller/actions/MidpointAction.class new file mode 100644 index 0000000..b8396b5 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/MidpointAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/ModifySegmentAction.class b/bin/jdrafting/gui/controller/actions/ModifySegmentAction.class new file mode 100644 index 0000000..1dabe56 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/ModifySegmentAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/MoveZBufferAction$EditMoveZBuffer.class b/bin/jdrafting/gui/controller/actions/MoveZBufferAction$EditMoveZBuffer.class new file mode 100644 index 0000000..a43aec6 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/MoveZBufferAction$EditMoveZBuffer.class differ diff --git a/bin/jdrafting/gui/controller/actions/MoveZBufferAction.class b/bin/jdrafting/gui/controller/actions/MoveZBufferAction.class new file mode 100644 index 0000000..2eed469 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/MoveZBufferAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/NewAction.class b/bin/jdrafting/gui/controller/actions/NewAction.class new file mode 100644 index 0000000..eb47334 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/NewAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/OpenAction$PreviewPanel.class b/bin/jdrafting/gui/controller/actions/OpenAction$PreviewPanel.class new file mode 100644 index 0000000..bc2565b Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/OpenAction$PreviewPanel.class differ diff --git a/bin/jdrafting/gui/controller/actions/OpenAction.class b/bin/jdrafting/gui/controller/actions/OpenAction.class new file mode 100644 index 0000000..da5f87a Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/OpenAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/PaintAction.class b/bin/jdrafting/gui/controller/actions/PaintAction.class new file mode 100644 index 0000000..103065a Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/PaintAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/ParabolaAction.class b/bin/jdrafting/gui/controller/actions/ParabolaAction.class new file mode 100644 index 0000000..9e221ef Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/ParabolaAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/ParallelAction.class b/bin/jdrafting/gui/controller/actions/ParallelAction.class new file mode 100644 index 0000000..1ee0400 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/ParallelAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/PasteAction$1.class b/bin/jdrafting/gui/controller/actions/PasteAction$1.class new file mode 100644 index 0000000..7587365 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/PasteAction$1.class differ diff --git a/bin/jdrafting/gui/controller/actions/PasteAction.class b/bin/jdrafting/gui/controller/actions/PasteAction.class new file mode 100644 index 0000000..f8ff6ac Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/PasteAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/PasteStyleAction.class b/bin/jdrafting/gui/controller/actions/PasteStyleAction.class new file mode 100644 index 0000000..9bef3ae Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/PasteStyleAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/PerpendicularAction.class b/bin/jdrafting/gui/controller/actions/PerpendicularAction.class new file mode 100644 index 0000000..2c09293 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/PerpendicularAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/PointAction.class b/bin/jdrafting/gui/controller/actions/PointAction.class new file mode 100644 index 0000000..53500ae Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/PointAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/PointColorAction$1.class b/bin/jdrafting/gui/controller/actions/PointColorAction$1.class new file mode 100644 index 0000000..9dd4f2e Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/PointColorAction$1.class differ diff --git a/bin/jdrafting/gui/controller/actions/PointColorAction$2.class b/bin/jdrafting/gui/controller/actions/PointColorAction$2.class new file mode 100644 index 0000000..745c2b9 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/PointColorAction$2.class differ diff --git a/bin/jdrafting/gui/controller/actions/PointColorAction.class b/bin/jdrafting/gui/controller/actions/PointColorAction.class new file mode 100644 index 0000000..fc22277 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/PointColorAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/PolyLineAction.class b/bin/jdrafting/gui/controller/actions/PolyLineAction.class new file mode 100644 index 0000000..e589e22 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/PolyLineAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/PolygonAction.class b/bin/jdrafting/gui/controller/actions/PolygonAction.class new file mode 100644 index 0000000..3fe4968 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/PolygonAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/PrintAction.class b/bin/jdrafting/gui/controller/actions/PrintAction.class new file mode 100644 index 0000000..4ed686a Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/PrintAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/ProtractorAction.class b/bin/jdrafting/gui/controller/actions/ProtractorAction.class new file mode 100644 index 0000000..730e512 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/ProtractorAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/RectangleAction.class b/bin/jdrafting/gui/controller/actions/RectangleAction.class new file mode 100644 index 0000000..76183c0 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/RectangleAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/RedoAction.class b/bin/jdrafting/gui/controller/actions/RedoAction.class new file mode 100644 index 0000000..98be0f4 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/RedoAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/RegularPolygonAction.class b/bin/jdrafting/gui/controller/actions/RegularPolygonAction.class new file mode 100644 index 0000000..949894e Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/RegularPolygonAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/RewindAction.class b/bin/jdrafting/gui/controller/actions/RewindAction.class new file mode 100644 index 0000000..fa4a69f Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/RewindAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/RotationAction.class b/bin/jdrafting/gui/controller/actions/RotationAction.class new file mode 100644 index 0000000..575e59c Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/RotationAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/RulerAction.class b/bin/jdrafting/gui/controller/actions/RulerAction.class new file mode 100644 index 0000000..aeb45d4 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/RulerAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/SaveAction.class b/bin/jdrafting/gui/controller/actions/SaveAction.class new file mode 100644 index 0000000..d9d3589 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/SaveAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/SaveImageAction$ImageChooser$1.class b/bin/jdrafting/gui/controller/actions/SaveImageAction$ImageChooser$1.class new file mode 100644 index 0000000..3dceee0 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/SaveImageAction$ImageChooser$1.class differ diff --git a/bin/jdrafting/gui/controller/actions/SaveImageAction$ImageChooser$2.class b/bin/jdrafting/gui/controller/actions/SaveImageAction$ImageChooser$2.class new file mode 100644 index 0000000..cf0072e Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/SaveImageAction$ImageChooser$2.class differ diff --git a/bin/jdrafting/gui/controller/actions/SaveImageAction$ImageChooser.class b/bin/jdrafting/gui/controller/actions/SaveImageAction$ImageChooser.class new file mode 100644 index 0000000..738c31c Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/SaveImageAction$ImageChooser.class differ diff --git a/bin/jdrafting/gui/controller/actions/SaveImageAction.class b/bin/jdrafting/gui/controller/actions/SaveImageAction.class new file mode 100644 index 0000000..912efc7 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/SaveImageAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/SegmentAction.class b/bin/jdrafting/gui/controller/actions/SegmentAction.class new file mode 100644 index 0000000..4fdf234 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/SegmentAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/SelectAllAction.class b/bin/jdrafting/gui/controller/actions/SelectAllAction.class new file mode 100644 index 0000000..1ea0916 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/SelectAllAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/SelectionAction.class b/bin/jdrafting/gui/controller/actions/SelectionAction.class new file mode 100644 index 0000000..e4058f1 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/SelectionAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/ShapeColorAction$1.class b/bin/jdrafting/gui/controller/actions/ShapeColorAction$1.class new file mode 100644 index 0000000..3f4c2d3 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/ShapeColorAction$1.class differ diff --git a/bin/jdrafting/gui/controller/actions/ShapeColorAction$2.class b/bin/jdrafting/gui/controller/actions/ShapeColorAction$2.class new file mode 100644 index 0000000..4bb0338 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/ShapeColorAction$2.class differ diff --git a/bin/jdrafting/gui/controller/actions/ShapeColorAction.class b/bin/jdrafting/gui/controller/actions/ShapeColorAction.class new file mode 100644 index 0000000..4094de0 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/ShapeColorAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/ShapeFillAction$1.class b/bin/jdrafting/gui/controller/actions/ShapeFillAction$1.class new file mode 100644 index 0000000..08eeed9 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/ShapeFillAction$1.class differ diff --git a/bin/jdrafting/gui/controller/actions/ShapeFillAction$2.class b/bin/jdrafting/gui/controller/actions/ShapeFillAction$2.class new file mode 100644 index 0000000..765a80d Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/ShapeFillAction$2.class differ diff --git a/bin/jdrafting/gui/controller/actions/ShapeFillAction$3.class b/bin/jdrafting/gui/controller/actions/ShapeFillAction$3.class new file mode 100644 index 0000000..d9943e4 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/ShapeFillAction$3.class differ diff --git a/bin/jdrafting/gui/controller/actions/ShapeFillAction.class b/bin/jdrafting/gui/controller/actions/ShapeFillAction.class new file mode 100644 index 0000000..82fc323 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/ShapeFillAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/SplineAction.class b/bin/jdrafting/gui/controller/actions/SplineAction.class new file mode 100644 index 0000000..73726ff Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/SplineAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/TextBoxAction.class b/bin/jdrafting/gui/controller/actions/TextBoxAction.class new file mode 100644 index 0000000..f6907c8 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/TextBoxAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/TextVisibleAction.class b/bin/jdrafting/gui/controller/actions/TextVisibleAction.class new file mode 100644 index 0000000..796632c Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/TextVisibleAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/TranslationAction.class b/bin/jdrafting/gui/controller/actions/TranslationAction.class new file mode 100644 index 0000000..730172c Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/TranslationAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/TriangleAction.class b/bin/jdrafting/gui/controller/actions/TriangleAction.class new file mode 100644 index 0000000..9324610 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/TriangleAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/TrianglePointsAction.class b/bin/jdrafting/gui/controller/actions/TrianglePointsAction.class new file mode 100644 index 0000000..e0e7005 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/TrianglePointsAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/UndoAction.class b/bin/jdrafting/gui/controller/actions/UndoAction.class new file mode 100644 index 0000000..a166dc1 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/UndoAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/UndoRedoHistoryAction$1.class b/bin/jdrafting/gui/controller/actions/UndoRedoHistoryAction$1.class new file mode 100644 index 0000000..6e4e299 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/UndoRedoHistoryAction$1.class differ diff --git a/bin/jdrafting/gui/controller/actions/UndoRedoHistoryAction.class b/bin/jdrafting/gui/controller/actions/UndoRedoHistoryAction.class new file mode 100644 index 0000000..71f703f Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/UndoRedoHistoryAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/VertexAction.class b/bin/jdrafting/gui/controller/actions/VertexAction.class new file mode 100644 index 0000000..42f4925 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/VertexAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/ZoomAllAction.class b/bin/jdrafting/gui/controller/actions/ZoomAllAction.class new file mode 100644 index 0000000..f08da04 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/ZoomAllAction.class differ diff --git a/bin/jdrafting/gui/controller/actions/ZoomInOutAction.class b/bin/jdrafting/gui/controller/actions/ZoomInOutAction.class new file mode 100644 index 0000000..b3885d4 Binary files /dev/null and b/bin/jdrafting/gui/controller/actions/ZoomInOutAction.class differ diff --git a/bin/jdrafting/gui/controller/mouse/AbstractCanvasMouseListener.class b/bin/jdrafting/gui/controller/mouse/AbstractCanvasMouseListener.class new file mode 100644 index 0000000..fe7b3a9 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/AbstractCanvasMouseListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/AngleListener$1.class b/bin/jdrafting/gui/controller/mouse/AngleListener$1.class new file mode 100644 index 0000000..0c9a237 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/AngleListener$1.class differ diff --git a/bin/jdrafting/gui/controller/mouse/AngleListener.class b/bin/jdrafting/gui/controller/mouse/AngleListener.class new file mode 100644 index 0000000..261ec3c Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/AngleListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/ArcListener.class b/bin/jdrafting/gui/controller/mouse/ArcListener.class new file mode 100644 index 0000000..8acf302 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/ArcListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/AreaSubstractListener.class b/bin/jdrafting/gui/controller/mouse/AreaSubstractListener.class new file mode 100644 index 0000000..9742eaf Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/AreaSubstractListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/ArrowListener.class b/bin/jdrafting/gui/controller/mouse/ArrowListener.class new file mode 100644 index 0000000..1138e4e Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/ArrowListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/AxialSymmetryListener$EditSymmetry.class b/bin/jdrafting/gui/controller/mouse/AxialSymmetryListener$EditSymmetry.class new file mode 100644 index 0000000..c2f7aec Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/AxialSymmetryListener$EditSymmetry.class differ diff --git a/bin/jdrafting/gui/controller/mouse/AxialSymmetryListener.class b/bin/jdrafting/gui/controller/mouse/AxialSymmetryListener.class new file mode 100644 index 0000000..07ca959 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/AxialSymmetryListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/BisectrixListener.class b/bin/jdrafting/gui/controller/mouse/BisectrixListener.class new file mode 100644 index 0000000..82a52d5 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/BisectrixListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/BoundsListener.class b/bin/jdrafting/gui/controller/mouse/BoundsListener.class new file mode 100644 index 0000000..6ec34b3 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/BoundsListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/CapableArcListener.class b/bin/jdrafting/gui/controller/mouse/CapableArcListener.class new file mode 100644 index 0000000..d49c650 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/CapableArcListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/CentralSymmetryListener$EditSymmetric.class b/bin/jdrafting/gui/controller/mouse/CentralSymmetryListener$EditSymmetric.class new file mode 100644 index 0000000..1714a69 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/CentralSymmetryListener$EditSymmetric.class differ diff --git a/bin/jdrafting/gui/controller/mouse/CentralSymmetryListener.class b/bin/jdrafting/gui/controller/mouse/CentralSymmetryListener.class new file mode 100644 index 0000000..5851ba2 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/CentralSymmetryListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/CircumferenceListener.class b/bin/jdrafting/gui/controller/mouse/CircumferenceListener.class new file mode 100644 index 0000000..adb4282 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/CircumferenceListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/DivisionPointsListener$1.class b/bin/jdrafting/gui/controller/mouse/DivisionPointsListener$1.class new file mode 100644 index 0000000..e33fe73 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/DivisionPointsListener$1.class differ diff --git a/bin/jdrafting/gui/controller/mouse/DivisionPointsListener$2.class b/bin/jdrafting/gui/controller/mouse/DivisionPointsListener$2.class new file mode 100644 index 0000000..630beea Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/DivisionPointsListener$2.class differ diff --git a/bin/jdrafting/gui/controller/mouse/DivisionPointsListener.class b/bin/jdrafting/gui/controller/mouse/DivisionPointsListener.class new file mode 100644 index 0000000..2b2758c Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/DivisionPointsListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/EllipseListener.class b/bin/jdrafting/gui/controller/mouse/EllipseListener.class new file mode 100644 index 0000000..be80eae Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/EllipseListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/ExtremesListener.class b/bin/jdrafting/gui/controller/mouse/ExtremesListener.class new file mode 100644 index 0000000..f46fbbb Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/ExtremesListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/EyedropperListener.class b/bin/jdrafting/gui/controller/mouse/EyedropperListener.class new file mode 100644 index 0000000..e6344ac Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/EyedropperListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/FragmentListener$1.class b/bin/jdrafting/gui/controller/mouse/FragmentListener$1.class new file mode 100644 index 0000000..fec085b Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/FragmentListener$1.class differ diff --git a/bin/jdrafting/gui/controller/mouse/FragmentListener.class b/bin/jdrafting/gui/controller/mouse/FragmentListener.class new file mode 100644 index 0000000..5210859 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/FragmentListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/FreeHandListener.class b/bin/jdrafting/gui/controller/mouse/FreeHandListener.class new file mode 100644 index 0000000..77fed43 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/FreeHandListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/HandListener$EditMoveShapes.class b/bin/jdrafting/gui/controller/mouse/HandListener$EditMoveShapes.class new file mode 100644 index 0000000..72bc30b Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/HandListener$EditMoveShapes.class differ diff --git a/bin/jdrafting/gui/controller/mouse/HandListener.class b/bin/jdrafting/gui/controller/mouse/HandListener.class new file mode 100644 index 0000000..286ebbf Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/HandListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/HomothetyListener$EditHomothety.class b/bin/jdrafting/gui/controller/mouse/HomothetyListener$EditHomothety.class new file mode 100644 index 0000000..edb10f5 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/HomothetyListener$EditHomothety.class differ diff --git a/bin/jdrafting/gui/controller/mouse/HomothetyListener.class b/bin/jdrafting/gui/controller/mouse/HomothetyListener.class new file mode 100644 index 0000000..a54d645 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/HomothetyListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/HyperbolaListener.class b/bin/jdrafting/gui/controller/mouse/HyperbolaListener.class new file mode 100644 index 0000000..ee519ff Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/HyperbolaListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/MathFunctionListener.class b/bin/jdrafting/gui/controller/mouse/MathFunctionListener.class new file mode 100644 index 0000000..660e060 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/MathFunctionListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/MediatrixListener.class b/bin/jdrafting/gui/controller/mouse/MediatrixListener.class new file mode 100644 index 0000000..2be07b4 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/MediatrixListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/MidpointListener.class b/bin/jdrafting/gui/controller/mouse/MidpointListener.class new file mode 100644 index 0000000..6843841 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/MidpointListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/ModifySegmentListener.class b/bin/jdrafting/gui/controller/mouse/ModifySegmentListener.class new file mode 100644 index 0000000..4a35335 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/ModifySegmentListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/PaintListener$EditPaint.class b/bin/jdrafting/gui/controller/mouse/PaintListener$EditPaint.class new file mode 100644 index 0000000..fce64fc Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/PaintListener$EditPaint.class differ diff --git a/bin/jdrafting/gui/controller/mouse/PaintListener.class b/bin/jdrafting/gui/controller/mouse/PaintListener.class new file mode 100644 index 0000000..cfdb444 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/PaintListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/ParabolaListener.class b/bin/jdrafting/gui/controller/mouse/ParabolaListener.class new file mode 100644 index 0000000..f85f873 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/ParabolaListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/ParallelListener.class b/bin/jdrafting/gui/controller/mouse/ParallelListener.class new file mode 100644 index 0000000..a9e7c8e Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/ParallelListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/PasteStyleListener$EditPasteStyle.class b/bin/jdrafting/gui/controller/mouse/PasteStyleListener$EditPasteStyle.class new file mode 100644 index 0000000..d8affc9 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/PasteStyleListener$EditPasteStyle.class differ diff --git a/bin/jdrafting/gui/controller/mouse/PasteStyleListener.class b/bin/jdrafting/gui/controller/mouse/PasteStyleListener.class new file mode 100644 index 0000000..686b3a4 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/PasteStyleListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/PerpendicularListener.class b/bin/jdrafting/gui/controller/mouse/PerpendicularListener.class new file mode 100644 index 0000000..d0c4a0b Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/PerpendicularListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/PointListener.class b/bin/jdrafting/gui/controller/mouse/PointListener.class new file mode 100644 index 0000000..6e88117 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/PointListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/PolygonListener.class b/bin/jdrafting/gui/controller/mouse/PolygonListener.class new file mode 100644 index 0000000..d77bd00 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/PolygonListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/ProtractorListener.class b/bin/jdrafting/gui/controller/mouse/ProtractorListener.class new file mode 100644 index 0000000..da73bd6 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/ProtractorListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/RectangleListener.class b/bin/jdrafting/gui/controller/mouse/RectangleListener.class new file mode 100644 index 0000000..67ce8ac Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/RectangleListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/RegularPolygonListener.class b/bin/jdrafting/gui/controller/mouse/RegularPolygonListener.class new file mode 100644 index 0000000..247ba53 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/RegularPolygonListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/RotationListener$EditRotation.class b/bin/jdrafting/gui/controller/mouse/RotationListener$EditRotation.class new file mode 100644 index 0000000..fc01440 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/RotationListener$EditRotation.class differ diff --git a/bin/jdrafting/gui/controller/mouse/RotationListener.class b/bin/jdrafting/gui/controller/mouse/RotationListener.class new file mode 100644 index 0000000..216b8dc Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/RotationListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/RulerListener.class b/bin/jdrafting/gui/controller/mouse/RulerListener.class new file mode 100644 index 0000000..6783276 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/RulerListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/SegmentListener.class b/bin/jdrafting/gui/controller/mouse/SegmentListener.class new file mode 100644 index 0000000..80f3d1b Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/SegmentListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/SelectionListener.class b/bin/jdrafting/gui/controller/mouse/SelectionListener.class new file mode 100644 index 0000000..87fc438 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/SelectionListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/SplineListener.class b/bin/jdrafting/gui/controller/mouse/SplineListener.class new file mode 100644 index 0000000..76d6f74 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/SplineListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/TextBoxListener$1.class b/bin/jdrafting/gui/controller/mouse/TextBoxListener$1.class new file mode 100644 index 0000000..319fc82 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/TextBoxListener$1.class differ diff --git a/bin/jdrafting/gui/controller/mouse/TextBoxListener$2.class b/bin/jdrafting/gui/controller/mouse/TextBoxListener$2.class new file mode 100644 index 0000000..b123f8b Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/TextBoxListener$2.class differ diff --git a/bin/jdrafting/gui/controller/mouse/TextBoxListener.class b/bin/jdrafting/gui/controller/mouse/TextBoxListener.class new file mode 100644 index 0000000..2c9fc0a Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/TextBoxListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/TranslationListener$EditTranslation.class b/bin/jdrafting/gui/controller/mouse/TranslationListener$EditTranslation.class new file mode 100644 index 0000000..a3846a2 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/TranslationListener$EditTranslation.class differ diff --git a/bin/jdrafting/gui/controller/mouse/TranslationListener.class b/bin/jdrafting/gui/controller/mouse/TranslationListener.class new file mode 100644 index 0000000..07ceb13 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/TranslationListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/TriangleListener.class b/bin/jdrafting/gui/controller/mouse/TriangleListener.class new file mode 100644 index 0000000..9f91344 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/TriangleListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/TrianglePointsListener.class b/bin/jdrafting/gui/controller/mouse/TrianglePointsListener.class new file mode 100644 index 0000000..a4a1c67 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/TrianglePointsListener.class differ diff --git a/bin/jdrafting/gui/controller/mouse/VertexListener$1.class b/bin/jdrafting/gui/controller/mouse/VertexListener$1.class new file mode 100644 index 0000000..b4e2670 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/VertexListener$1.class differ diff --git a/bin/jdrafting/gui/controller/mouse/VertexListener.class b/bin/jdrafting/gui/controller/mouse/VertexListener.class new file mode 100644 index 0000000..e72bac3 Binary files /dev/null and b/bin/jdrafting/gui/controller/mouse/VertexListener.class differ diff --git a/bin/jdrafting/resources/images/angle.png b/bin/jdrafting/resources/images/angle.png new file mode 100644 index 0000000..7615a1c Binary files /dev/null and b/bin/jdrafting/resources/images/angle.png differ diff --git a/bin/jdrafting/resources/images/arc.png b/bin/jdrafting/resources/images/arc.png new file mode 100644 index 0000000..257034f Binary files /dev/null and b/bin/jdrafting/resources/images/arc.png differ diff --git a/bin/jdrafting/resources/images/area_intersection.png b/bin/jdrafting/resources/images/area_intersection.png new file mode 100644 index 0000000..109589f Binary files /dev/null and b/bin/jdrafting/resources/images/area_intersection.png differ diff --git a/bin/jdrafting/resources/images/area_substract.png b/bin/jdrafting/resources/images/area_substract.png new file mode 100644 index 0000000..db15807 Binary files /dev/null and b/bin/jdrafting/resources/images/area_substract.png differ diff --git a/bin/jdrafting/resources/images/area_symmetric_substract.png b/bin/jdrafting/resources/images/area_symmetric_substract.png new file mode 100644 index 0000000..aa8c787 Binary files /dev/null and b/bin/jdrafting/resources/images/area_symmetric_substract.png differ diff --git a/bin/jdrafting/resources/images/area_union.png b/bin/jdrafting/resources/images/area_union.png new file mode 100644 index 0000000..8df53f3 Binary files /dev/null and b/bin/jdrafting/resources/images/area_union.png differ diff --git a/bin/jdrafting/resources/images/arrow.png b/bin/jdrafting/resources/images/arrow.png new file mode 100644 index 0000000..4f973a0 Binary files /dev/null and b/bin/jdrafting/resources/images/arrow.png differ diff --git a/bin/jdrafting/resources/images/axial_symmetry.png b/bin/jdrafting/resources/images/axial_symmetry.png new file mode 100644 index 0000000..942eabf Binary files /dev/null and b/bin/jdrafting/resources/images/axial_symmetry.png differ diff --git a/bin/jdrafting/resources/images/backcolor.png b/bin/jdrafting/resources/images/backcolor.png new file mode 100644 index 0000000..0a9a461 Binary files /dev/null and b/bin/jdrafting/resources/images/backcolor.png differ diff --git a/bin/jdrafting/resources/images/backward.png b/bin/jdrafting/resources/images/backward.png new file mode 100644 index 0000000..4d83266 Binary files /dev/null and b/bin/jdrafting/resources/images/backward.png differ diff --git a/bin/jdrafting/resources/images/baricenter.png b/bin/jdrafting/resources/images/baricenter.png new file mode 100644 index 0000000..3f797bf Binary files /dev/null and b/bin/jdrafting/resources/images/baricenter.png differ diff --git a/bin/jdrafting/resources/images/bisectrix.png b/bin/jdrafting/resources/images/bisectrix.png new file mode 100644 index 0000000..87f0f53 Binary files /dev/null and b/bin/jdrafting/resources/images/bisectrix.png differ diff --git a/bin/jdrafting/resources/images/bounds.png b/bin/jdrafting/resources/images/bounds.png new file mode 100644 index 0000000..b016a64 Binary files /dev/null and b/bin/jdrafting/resources/images/bounds.png differ diff --git a/bin/jdrafting/resources/images/capable_arc.png b/bin/jdrafting/resources/images/capable_arc.png new file mode 100644 index 0000000..f22f4a7 Binary files /dev/null and b/bin/jdrafting/resources/images/capable_arc.png differ diff --git a/bin/jdrafting/resources/images/central_symmetry.png b/bin/jdrafting/resources/images/central_symmetry.png new file mode 100644 index 0000000..c294a85 Binary files /dev/null and b/bin/jdrafting/resources/images/central_symmetry.png differ diff --git a/bin/jdrafting/resources/images/circumcenter.png b/bin/jdrafting/resources/images/circumcenter.png new file mode 100644 index 0000000..ff6382e Binary files /dev/null and b/bin/jdrafting/resources/images/circumcenter.png differ diff --git a/bin/jdrafting/resources/images/circumference.png b/bin/jdrafting/resources/images/circumference.png new file mode 100644 index 0000000..bd45c85 Binary files /dev/null and b/bin/jdrafting/resources/images/circumference.png differ diff --git a/bin/jdrafting/resources/images/color.png b/bin/jdrafting/resources/images/color.png new file mode 100644 index 0000000..00c056b Binary files /dev/null and b/bin/jdrafting/resources/images/color.png differ diff --git a/bin/jdrafting/resources/images/conics.png b/bin/jdrafting/resources/images/conics.png new file mode 100644 index 0000000..e45096f Binary files /dev/null and b/bin/jdrafting/resources/images/conics.png differ diff --git a/bin/jdrafting/resources/images/copy.png b/bin/jdrafting/resources/images/copy.png new file mode 100644 index 0000000..839bb99 Binary files /dev/null and b/bin/jdrafting/resources/images/copy.png differ diff --git a/bin/jdrafting/resources/images/cursors/angle_cursor.png b/bin/jdrafting/resources/images/cursors/angle_cursor.png new file mode 100644 index 0000000..1b1a488 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/angle_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/arc_cursor.png b/bin/jdrafting/resources/images/cursors/arc_cursor.png new file mode 100644 index 0000000..6452dda Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/arc_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/area_substract_cursor.png b/bin/jdrafting/resources/images/cursors/area_substract_cursor.png new file mode 100644 index 0000000..5c714bf Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/area_substract_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/arrow_cursor.png b/bin/jdrafting/resources/images/cursors/arrow_cursor.png new file mode 100644 index 0000000..71bacda Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/arrow_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/axial_symmetry_cursor.png b/bin/jdrafting/resources/images/cursors/axial_symmetry_cursor.png new file mode 100644 index 0000000..8accd17 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/axial_symmetry_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/baricenter_cursor.png b/bin/jdrafting/resources/images/cursors/baricenter_cursor.png new file mode 100644 index 0000000..56674a3 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/baricenter_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/bisectrix_cursor.png b/bin/jdrafting/resources/images/cursors/bisectrix_cursor.png new file mode 100644 index 0000000..f63c35c Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/bisectrix_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/bounds_cursor.png b/bin/jdrafting/resources/images/cursors/bounds_cursor.png new file mode 100644 index 0000000..de1d49b Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/bounds_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/capable_arc_cursor.png b/bin/jdrafting/resources/images/cursors/capable_arc_cursor.png new file mode 100644 index 0000000..bb83284 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/capable_arc_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/central_symmetry_cursor.png b/bin/jdrafting/resources/images/cursors/central_symmetry_cursor.png new file mode 100644 index 0000000..1c15aab Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/central_symmetry_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/circumcenter_cursor.png b/bin/jdrafting/resources/images/cursors/circumcenter_cursor.png new file mode 100644 index 0000000..861207d Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/circumcenter_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/circumference_cursor.png b/bin/jdrafting/resources/images/cursors/circumference_cursor.png new file mode 100644 index 0000000..d877f7e Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/circumference_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/divisions_cursor.png b/bin/jdrafting/resources/images/cursors/divisions_cursor.png new file mode 100644 index 0000000..c191929 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/divisions_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/dragging_cursor.png b/bin/jdrafting/resources/images/cursors/dragging_cursor.png new file mode 100644 index 0000000..7752229 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/dragging_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/ellipse_cursor.png b/bin/jdrafting/resources/images/cursors/ellipse_cursor.png new file mode 100644 index 0000000..a9c82f2 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/ellipse_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/extremes_cursor.png b/bin/jdrafting/resources/images/cursors/extremes_cursor.png new file mode 100644 index 0000000..694d99d Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/extremes_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/eyedropper_cursor.png b/bin/jdrafting/resources/images/cursors/eyedropper_cursor.png new file mode 100644 index 0000000..b8f9a5f Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/eyedropper_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/free_hand_down_cursor.png b/bin/jdrafting/resources/images/cursors/free_hand_down_cursor.png new file mode 100644 index 0000000..bfd0217 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/free_hand_down_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/free_hand_up_cursor.png b/bin/jdrafting/resources/images/cursors/free_hand_up_cursor.png new file mode 100644 index 0000000..88a9c8e Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/free_hand_up_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/hammer_cursor.png b/bin/jdrafting/resources/images/cursors/hammer_cursor.png new file mode 100644 index 0000000..a9f1285 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/hammer_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/hand_cursor.png b/bin/jdrafting/resources/images/cursors/hand_cursor.png new file mode 100644 index 0000000..c01a690 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/hand_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/homothety_cursor.png b/bin/jdrafting/resources/images/cursors/homothety_cursor.png new file mode 100644 index 0000000..8758662 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/homothety_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/hyperbola_cursor.png b/bin/jdrafting/resources/images/cursors/hyperbola_cursor.png new file mode 100644 index 0000000..d84e69c Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/hyperbola_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/incenter_cursor.png b/bin/jdrafting/resources/images/cursors/incenter_cursor.png new file mode 100644 index 0000000..abd59b0 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/incenter_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/jme_cursor.png b/bin/jdrafting/resources/images/cursors/jme_cursor.png new file mode 100644 index 0000000..156a1b6 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/jme_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/mediatrix_cursor.png b/bin/jdrafting/resources/images/cursors/mediatrix_cursor.png new file mode 100644 index 0000000..6c238c6 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/mediatrix_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/midpoint_cursor.png b/bin/jdrafting/resources/images/cursors/midpoint_cursor.png new file mode 100644 index 0000000..473397e Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/midpoint_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/modify_segment_cursor.png b/bin/jdrafting/resources/images/cursors/modify_segment_cursor.png new file mode 100644 index 0000000..d6a0dae Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/modify_segment_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/ortocenter_cursor.png b/bin/jdrafting/resources/images/cursors/ortocenter_cursor.png new file mode 100644 index 0000000..6d361a3 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/ortocenter_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/paint_cursor.png b/bin/jdrafting/resources/images/cursors/paint_cursor.png new file mode 100644 index 0000000..c4315f9 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/paint_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/parabola_cursor.png b/bin/jdrafting/resources/images/cursors/parabola_cursor.png new file mode 100644 index 0000000..d6210fd Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/parabola_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/parallel_cursor.png b/bin/jdrafting/resources/images/cursors/parallel_cursor.png new file mode 100644 index 0000000..5e1cfef Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/parallel_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/paste_style_cursor.png b/bin/jdrafting/resources/images/cursors/paste_style_cursor.png new file mode 100644 index 0000000..3aa7f01 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/paste_style_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/perpendicular_cursor.png b/bin/jdrafting/resources/images/cursors/perpendicular_cursor.png new file mode 100644 index 0000000..5f68e51 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/perpendicular_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/point_cursor.png b/bin/jdrafting/resources/images/cursors/point_cursor.png new file mode 100644 index 0000000..f24fa86 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/point_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/polygon_cursor.png b/bin/jdrafting/resources/images/cursors/polygon_cursor.png new file mode 100644 index 0000000..66b26c1 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/polygon_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/polyline_cursor.png b/bin/jdrafting/resources/images/cursors/polyline_cursor.png new file mode 100644 index 0000000..6cddb54 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/polyline_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/protractor_cursor.png b/bin/jdrafting/resources/images/cursors/protractor_cursor.png new file mode 100644 index 0000000..112465f Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/protractor_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/rectangle_cursor.png b/bin/jdrafting/resources/images/cursors/rectangle_cursor.png new file mode 100644 index 0000000..efaa399 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/rectangle_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/reg_poly_cursor.png b/bin/jdrafting/resources/images/cursors/reg_poly_cursor.png new file mode 100644 index 0000000..5ce64d8 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/reg_poly_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/rotation_cursor.png b/bin/jdrafting/resources/images/cursors/rotation_cursor.png new file mode 100644 index 0000000..15c7988 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/rotation_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/ruler_cursor.png b/bin/jdrafting/resources/images/cursors/ruler_cursor.png new file mode 100644 index 0000000..5201f89 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/ruler_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/segment_cursor.png b/bin/jdrafting/resources/images/cursors/segment_cursor.png new file mode 100644 index 0000000..d683fa9 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/segment_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/spline_cursor.png b/bin/jdrafting/resources/images/cursors/spline_cursor.png new file mode 100644 index 0000000..8725c5d Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/spline_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/text_cursor.png b/bin/jdrafting/resources/images/cursors/text_cursor.png new file mode 100644 index 0000000..92a9d74 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/text_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/translation_cursor.png b/bin/jdrafting/resources/images/cursors/translation_cursor.png new file mode 100644 index 0000000..241d3c6 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/translation_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/triangle_cursor.png b/bin/jdrafting/resources/images/cursors/triangle_cursor.png new file mode 100644 index 0000000..f5c8e0a Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/triangle_cursor.png differ diff --git a/bin/jdrafting/resources/images/cursors/vertex_cursor.png b/bin/jdrafting/resources/images/cursors/vertex_cursor.png new file mode 100644 index 0000000..298f0e0 Binary files /dev/null and b/bin/jdrafting/resources/images/cursors/vertex_cursor.png differ diff --git a/bin/jdrafting/resources/images/delete_all.png b/bin/jdrafting/resources/images/delete_all.png new file mode 100644 index 0000000..9f63a08 Binary files /dev/null and b/bin/jdrafting/resources/images/delete_all.png differ diff --git a/bin/jdrafting/resources/images/divisions.png b/bin/jdrafting/resources/images/divisions.png new file mode 100644 index 0000000..20a6b0e Binary files /dev/null and b/bin/jdrafting/resources/images/divisions.png differ diff --git a/bin/jdrafting/resources/images/down.png b/bin/jdrafting/resources/images/down.png new file mode 100644 index 0000000..f627a91 Binary files /dev/null and b/bin/jdrafting/resources/images/down.png differ diff --git a/bin/jdrafting/resources/images/ellipse.png b/bin/jdrafting/resources/images/ellipse.png new file mode 100644 index 0000000..fa6b81f Binary files /dev/null and b/bin/jdrafting/resources/images/ellipse.png differ diff --git a/bin/jdrafting/resources/images/end.png b/bin/jdrafting/resources/images/end.png new file mode 100644 index 0000000..a5e38d8 Binary files /dev/null and b/bin/jdrafting/resources/images/end.png differ diff --git a/bin/jdrafting/resources/images/exit.png b/bin/jdrafting/resources/images/exit.png new file mode 100644 index 0000000..1c7d8fb Binary files /dev/null and b/bin/jdrafting/resources/images/exit.png differ diff --git a/bin/jdrafting/resources/images/extremes.png b/bin/jdrafting/resources/images/extremes.png new file mode 100644 index 0000000..c1bc207 Binary files /dev/null and b/bin/jdrafting/resources/images/extremes.png differ diff --git a/bin/jdrafting/resources/images/eyedropper.png b/bin/jdrafting/resources/images/eyedropper.png new file mode 100644 index 0000000..2b1c92e Binary files /dev/null and b/bin/jdrafting/resources/images/eyedropper.png differ diff --git a/bin/jdrafting/resources/images/fileinfo.png b/bin/jdrafting/resources/images/fileinfo.png new file mode 100644 index 0000000..9ef7483 Binary files /dev/null and b/bin/jdrafting/resources/images/fileinfo.png differ diff --git a/bin/jdrafting/resources/images/fill_color.png b/bin/jdrafting/resources/images/fill_color.png new file mode 100644 index 0000000..3ed0781 Binary files /dev/null and b/bin/jdrafting/resources/images/fill_color.png differ diff --git a/bin/jdrafting/resources/images/forward.png b/bin/jdrafting/resources/images/forward.png new file mode 100644 index 0000000..d43c51e Binary files /dev/null and b/bin/jdrafting/resources/images/forward.png differ diff --git a/bin/jdrafting/resources/images/free_hand.png b/bin/jdrafting/resources/images/free_hand.png new file mode 100644 index 0000000..bce68b2 Binary files /dev/null and b/bin/jdrafting/resources/images/free_hand.png differ diff --git a/bin/jdrafting/resources/images/fusion.png b/bin/jdrafting/resources/images/fusion.png new file mode 100644 index 0000000..3088829 Binary files /dev/null and b/bin/jdrafting/resources/images/fusion.png differ diff --git a/bin/jdrafting/resources/images/github.png b/bin/jdrafting/resources/images/github.png new file mode 100644 index 0000000..9b0803d Binary files /dev/null and b/bin/jdrafting/resources/images/github.png differ diff --git a/bin/jdrafting/resources/images/hammer.png b/bin/jdrafting/resources/images/hammer.png new file mode 100644 index 0000000..1f44d93 Binary files /dev/null and b/bin/jdrafting/resources/images/hammer.png differ diff --git a/bin/jdrafting/resources/images/homepage.png b/bin/jdrafting/resources/images/homepage.png new file mode 100644 index 0000000..698af9d Binary files /dev/null and b/bin/jdrafting/resources/images/homepage.png differ diff --git a/bin/jdrafting/resources/images/homothety.png b/bin/jdrafting/resources/images/homothety.png new file mode 100644 index 0000000..ff24000 Binary files /dev/null and b/bin/jdrafting/resources/images/homothety.png differ diff --git a/bin/jdrafting/resources/images/hyperbola.png b/bin/jdrafting/resources/images/hyperbola.png new file mode 100644 index 0000000..12e4914 Binary files /dev/null and b/bin/jdrafting/resources/images/hyperbola.png differ diff --git a/bin/jdrafting/resources/images/incenter.png b/bin/jdrafting/resources/images/incenter.png new file mode 100644 index 0000000..325ca96 Binary files /dev/null and b/bin/jdrafting/resources/images/incenter.png differ diff --git a/bin/jdrafting/resources/images/intersection.png b/bin/jdrafting/resources/images/intersection.png new file mode 100644 index 0000000..f35a016 Binary files /dev/null and b/bin/jdrafting/resources/images/intersection.png differ diff --git a/bin/jdrafting/resources/images/invert.png b/bin/jdrafting/resources/images/invert.png new file mode 100644 index 0000000..deaf165 Binary files /dev/null and b/bin/jdrafting/resources/images/invert.png differ diff --git a/bin/jdrafting/resources/images/jdrafting.png b/bin/jdrafting/resources/images/jdrafting.png new file mode 100644 index 0000000..d3d13e3 Binary files /dev/null and b/bin/jdrafting/resources/images/jdrafting.png differ diff --git a/bin/jdrafting/resources/images/jme.png b/bin/jdrafting/resources/images/jme.png new file mode 100644 index 0000000..18d7c37 Binary files /dev/null and b/bin/jdrafting/resources/images/jme.png differ diff --git a/bin/jdrafting/resources/images/mediatrix.png b/bin/jdrafting/resources/images/mediatrix.png new file mode 100644 index 0000000..6d33241 Binary files /dev/null and b/bin/jdrafting/resources/images/mediatrix.png differ diff --git a/bin/jdrafting/resources/images/midpoint.png b/bin/jdrafting/resources/images/midpoint.png new file mode 100644 index 0000000..3f69f9a Binary files /dev/null and b/bin/jdrafting/resources/images/midpoint.png differ diff --git a/bin/jdrafting/resources/images/modify_segment.png b/bin/jdrafting/resources/images/modify_segment.png new file mode 100644 index 0000000..010a014 Binary files /dev/null and b/bin/jdrafting/resources/images/modify_segment.png differ diff --git a/bin/jdrafting/resources/images/names.png b/bin/jdrafting/resources/images/names.png new file mode 100644 index 0000000..ed17deb Binary files /dev/null and b/bin/jdrafting/resources/images/names.png differ diff --git a/bin/jdrafting/resources/images/new.png b/bin/jdrafting/resources/images/new.png new file mode 100644 index 0000000..212e0d6 Binary files /dev/null and b/bin/jdrafting/resources/images/new.png differ diff --git a/bin/jdrafting/resources/images/open.png b/bin/jdrafting/resources/images/open.png new file mode 100644 index 0000000..9e94fa0 Binary files /dev/null and b/bin/jdrafting/resources/images/open.png differ diff --git a/bin/jdrafting/resources/images/ortocenter.png b/bin/jdrafting/resources/images/ortocenter.png new file mode 100644 index 0000000..6b595cd Binary files /dev/null and b/bin/jdrafting/resources/images/ortocenter.png differ diff --git a/bin/jdrafting/resources/images/paint.png b/bin/jdrafting/resources/images/paint.png new file mode 100644 index 0000000..e78f574 Binary files /dev/null and b/bin/jdrafting/resources/images/paint.png differ diff --git a/bin/jdrafting/resources/images/parabola.png b/bin/jdrafting/resources/images/parabola.png new file mode 100644 index 0000000..18b7ecd Binary files /dev/null and b/bin/jdrafting/resources/images/parabola.png differ diff --git a/bin/jdrafting/resources/images/parallel.png b/bin/jdrafting/resources/images/parallel.png new file mode 100644 index 0000000..a99f0f7 Binary files /dev/null and b/bin/jdrafting/resources/images/parallel.png differ diff --git a/bin/jdrafting/resources/images/paste.png b/bin/jdrafting/resources/images/paste.png new file mode 100644 index 0000000..7e98e70 Binary files /dev/null and b/bin/jdrafting/resources/images/paste.png differ diff --git a/bin/jdrafting/resources/images/paste_style.png b/bin/jdrafting/resources/images/paste_style.png new file mode 100644 index 0000000..8d5dda6 Binary files /dev/null and b/bin/jdrafting/resources/images/paste_style.png differ diff --git a/bin/jdrafting/resources/images/perpendicular.png b/bin/jdrafting/resources/images/perpendicular.png new file mode 100644 index 0000000..0264392 Binary files /dev/null and b/bin/jdrafting/resources/images/perpendicular.png differ diff --git a/bin/jdrafting/resources/images/point.png b/bin/jdrafting/resources/images/point.png new file mode 100644 index 0000000..0d79afe Binary files /dev/null and b/bin/jdrafting/resources/images/point.png differ diff --git a/bin/jdrafting/resources/images/point_color.png b/bin/jdrafting/resources/images/point_color.png new file mode 100644 index 0000000..288308a Binary files /dev/null and b/bin/jdrafting/resources/images/point_color.png differ diff --git a/bin/jdrafting/resources/images/polygon.png b/bin/jdrafting/resources/images/polygon.png new file mode 100644 index 0000000..97a7f01 Binary files /dev/null and b/bin/jdrafting/resources/images/polygon.png differ diff --git a/bin/jdrafting/resources/images/polygon_popup.png b/bin/jdrafting/resources/images/polygon_popup.png new file mode 100644 index 0000000..2b529f2 Binary files /dev/null and b/bin/jdrafting/resources/images/polygon_popup.png differ diff --git a/bin/jdrafting/resources/images/polyline.png b/bin/jdrafting/resources/images/polyline.png new file mode 100644 index 0000000..17c37d2 Binary files /dev/null and b/bin/jdrafting/resources/images/polyline.png differ diff --git a/bin/jdrafting/resources/images/print.png b/bin/jdrafting/resources/images/print.png new file mode 100644 index 0000000..2bd3ad1 Binary files /dev/null and b/bin/jdrafting/resources/images/print.png differ diff --git a/bin/jdrafting/resources/images/protractor.png b/bin/jdrafting/resources/images/protractor.png new file mode 100644 index 0000000..89c8888 Binary files /dev/null and b/bin/jdrafting/resources/images/protractor.png differ diff --git a/bin/jdrafting/resources/images/rectangle.png b/bin/jdrafting/resources/images/rectangle.png new file mode 100644 index 0000000..4067ca9 Binary files /dev/null and b/bin/jdrafting/resources/images/rectangle.png differ diff --git a/bin/jdrafting/resources/images/redo.png b/bin/jdrafting/resources/images/redo.png new file mode 100644 index 0000000..dd6aaa5 Binary files /dev/null and b/bin/jdrafting/resources/images/redo.png differ diff --git a/bin/jdrafting/resources/images/reg_poly.png b/bin/jdrafting/resources/images/reg_poly.png new file mode 100644 index 0000000..b1a7fef Binary files /dev/null and b/bin/jdrafting/resources/images/reg_poly.png differ diff --git a/bin/jdrafting/resources/images/rewind.png b/bin/jdrafting/resources/images/rewind.png new file mode 100644 index 0000000..ef12b13 Binary files /dev/null and b/bin/jdrafting/resources/images/rewind.png differ diff --git a/bin/jdrafting/resources/images/rotation.png b/bin/jdrafting/resources/images/rotation.png new file mode 100644 index 0000000..3b5f5c4 Binary files /dev/null and b/bin/jdrafting/resources/images/rotation.png differ diff --git a/bin/jdrafting/resources/images/ruler.png b/bin/jdrafting/resources/images/ruler.png new file mode 100644 index 0000000..a073906 Binary files /dev/null and b/bin/jdrafting/resources/images/ruler.png differ diff --git a/bin/jdrafting/resources/images/save.png b/bin/jdrafting/resources/images/save.png new file mode 100644 index 0000000..7ae5502 Binary files /dev/null and b/bin/jdrafting/resources/images/save.png differ diff --git a/bin/jdrafting/resources/images/save_as.png b/bin/jdrafting/resources/images/save_as.png new file mode 100644 index 0000000..d04a9f5 Binary files /dev/null and b/bin/jdrafting/resources/images/save_as.png differ diff --git a/bin/jdrafting/resources/images/save_image.png b/bin/jdrafting/resources/images/save_image.png new file mode 100644 index 0000000..98b60f1 Binary files /dev/null and b/bin/jdrafting/resources/images/save_image.png differ diff --git a/bin/jdrafting/resources/images/segment.png b/bin/jdrafting/resources/images/segment.png new file mode 100644 index 0000000..d2fd409 Binary files /dev/null and b/bin/jdrafting/resources/images/segment.png differ diff --git a/bin/jdrafting/resources/images/select_all.png b/bin/jdrafting/resources/images/select_all.png new file mode 100644 index 0000000..ba719b5 Binary files /dev/null and b/bin/jdrafting/resources/images/select_all.png differ diff --git a/bin/jdrafting/resources/images/selection.png b/bin/jdrafting/resources/images/selection.png new file mode 100644 index 0000000..ca49a07 Binary files /dev/null and b/bin/jdrafting/resources/images/selection.png differ diff --git a/bin/jdrafting/resources/images/spline.png b/bin/jdrafting/resources/images/spline.png new file mode 100644 index 0000000..6b72194 Binary files /dev/null and b/bin/jdrafting/resources/images/spline.png differ diff --git a/bin/jdrafting/resources/images/swap_colors.png b/bin/jdrafting/resources/images/swap_colors.png new file mode 100644 index 0000000..35c3e57 Binary files /dev/null and b/bin/jdrafting/resources/images/swap_colors.png differ diff --git a/bin/jdrafting/resources/images/text.png b/bin/jdrafting/resources/images/text.png new file mode 100644 index 0000000..66754a2 Binary files /dev/null and b/bin/jdrafting/resources/images/text.png differ diff --git a/bin/jdrafting/resources/images/translation.png b/bin/jdrafting/resources/images/translation.png new file mode 100644 index 0000000..2758406 Binary files /dev/null and b/bin/jdrafting/resources/images/translation.png differ diff --git a/bin/jdrafting/resources/images/triangle.png b/bin/jdrafting/resources/images/triangle.png new file mode 100644 index 0000000..b6980e7 Binary files /dev/null and b/bin/jdrafting/resources/images/triangle.png differ diff --git a/bin/jdrafting/resources/images/triangle_popup.png b/bin/jdrafting/resources/images/triangle_popup.png new file mode 100644 index 0000000..df94312 Binary files /dev/null and b/bin/jdrafting/resources/images/triangle_popup.png differ diff --git a/bin/jdrafting/resources/images/undo.png b/bin/jdrafting/resources/images/undo.png new file mode 100644 index 0000000..9cc4262 Binary files /dev/null and b/bin/jdrafting/resources/images/undo.png differ diff --git a/bin/jdrafting/resources/images/up.png b/bin/jdrafting/resources/images/up.png new file mode 100644 index 0000000..5be1752 Binary files /dev/null and b/bin/jdrafting/resources/images/up.png differ diff --git a/bin/jdrafting/resources/images/vertex.png b/bin/jdrafting/resources/images/vertex.png new file mode 100644 index 0000000..d8defa5 Binary files /dev/null and b/bin/jdrafting/resources/images/vertex.png differ diff --git a/bin/jdrafting/resources/images/zoom_all.png b/bin/jdrafting/resources/images/zoom_all.png new file mode 100644 index 0000000..c5c2d78 Binary files /dev/null and b/bin/jdrafting/resources/images/zoom_all.png differ diff --git a/bin/jdrafting/resources/images/zoom_in.png b/bin/jdrafting/resources/images/zoom_in.png new file mode 100644 index 0000000..6d0a0f3 Binary files /dev/null and b/bin/jdrafting/resources/images/zoom_in.png differ diff --git a/bin/jdrafting/resources/images/zoom_out.png b/bin/jdrafting/resources/images/zoom_out.png new file mode 100644 index 0000000..68f7976 Binary files /dev/null and b/bin/jdrafting/resources/images/zoom_out.png differ diff --git a/bin/jdrafting/resources/language/language.properties b/bin/jdrafting/resources/language/language.properties new file mode 100644 index 0000000..8409af2 --- /dev/null +++ b/bin/jdrafting/resources/language/language.properties @@ -0,0 +1,480 @@ +# general +app_des=is an open-source drafting application to design small Technical Drawing sketches.
This application is inspired by the 'Classical Construction',
also known as 'Ruler-and-compass Construction' or 'Compass-and-straightedge construction'.
However, another drawing tools can be used.
(More info about classical construction: Wikipedia) +width=Width +height=Height +degrees=deg +not_saved=Not saved +cancel=Cancel +add=Add +remove=Remove + +# labels +thickness=

Line

thickness +point_thickness=

Point

thickness +lbl_fill=Fill +lbl_no_fill=No fill +lbl_line_color=Line color +lbl_point_color=Point color +lbl_fill_color=Fill color + +# actions +new=New exercise +new_des=Start a new exercise +open=Open... +open_des=Open an exercise +save=Save +save_des=Save exercise +save_as=Save as... +save_as_des=Save exercise as +save_image=Save image... +save_image_des=Export exercise as image SVG, PNG +print=Print... +print_des=Print in selected printer +fileinfo=Exercise info +fileinfo_des=Get or modify exercise info +redo=Redo +undo=Undo +move_up=Up shapes +move_up_des=Increase selected shapes index +move_down=Down shapes +move_down_des=Decrease selected shapes index +copy=Copy selected +copy_des=Copy selected shapes +exit=Exit +exit_des=Quit program +about=About... +zoom_all=Zoom all +zoom_all_des=Adjust content to canvas +zoom_in=Zoom in +zoom_in_des=Zoom in +zoom_out=Zoom out +zoom_out_des=Zoom out +delete=Delete selected +delete_des=Delete selected shapes +background_color=Canvas color... +background_color_des=Canvas background color +color=Shape color... +color_des=Shape color +point_color=Point color... +point_color_des=Point color +eyedropper=Eyedropper +eyedropper_des=Capture shape style +paste_style=Paste style +paste_style_des=Paste current style color and stroke +text=See/Hide shape names +text_des=See/Hide shape names +backward=Previous frame +backward_des=Previous frame +forward=Next frame +forward_des=Next frame +end=End frame +end_des=End frame +rewind=Rewind +rewind_des=Rewind +selection=Rectangular selection +selection_des=Select shapes in a rectangular area +invert=Invert selection +invert_des=Invert selection +select_all=Select all +select_all_des=Select all +point=Point +point_des=Create a new point +segment=Segment +segment_des=Create segment +arrow=Arrow +arrow_des=Create single or double arrow +polygon_tools=Polygon tools +reg_poly=Regular polygon +reg_poly_des=Create regular polygon +polygon=Polygon +polygon_des=Create polygon +polyline=Polyline +polyline_des=Create polyline +free_hand=Free hand +free_hand_des=Create an arbitrary shape +rectangle=Rectangle +rectangle_des=Create rectangle +ellipse=Ellipse +ellipse_des=Create ellipse +arc=Arc +arc_des=Create arc +circumference=Circumference +circumference_des=Create circumference +angle=Angle +angle_des=Create a segment by angle +triangle_tools=Triangle tools +triangle=Triangle +triangle_des=Create triangle +incenter=Incenter +ortocenter=Ortocenter +baricenter=Baricenter +circumcenter=Circumcenter +comment=Comment +comment_des=Add comment +spline=Spline +spline_des=Create spline between points +parabola=Parabola +parabola_des=Create parabola from rectangle bounds +hyperbola=Hyperbola +hyperbola_des=Create hyperbola from rectangle bounds +func_des=Create graph from function +func=Function +ruler=Ruler +ruler_des=Capture distance +protractor=Protractor +protractor_des=Capture angle +divisions=Division points +divisions_des=Divide shape in points +modify=Modify segment +modify_des=Modify a segment along its container straight +midpoint=Midpoint +midpoint_des=Set midpoint in the center of the shape bounds +centroid=Centroid +vertex=Vertex +vertex_des=Add vertex points to shape +extremes=Extremes +extremes_des=Add extreme vertex to open shapes +inter=Intersection points +inter_des=Select two or more shapes (none points) to add intersections between them +fragment=Fragment shape sides +fragment_des=Fragment shape sides into individual segments +fusion=Fusion shapes +fusion_des=Select shapes to be merged. Shift for connect extremes +bounds=Rectangle bounds +bounds_des=Create rectangle bounds of a shape +perp=Perpendicular +perp_des=Create perpendicular from segment +para=Parallel +para_des=Create parallel from segment +mediatrix=Mediatrix +mediatrix_des=Create mediatrix of a segment +bisectrix=Bisectrix +bisectrix_des=Create bisectrix +capable_arc=Capable arc +capable_arc_des=Create capable arc of a segment +translation=Translate +translation_des=Translate selected shapes +rotation=Rotate +rotation_des=Rotate selected shapes +homothety=Homothety +homothety_des=Homothety of the selected shapes +central_sym=Central symmetry +central_sym_des=Central symmetry of the selected shapes +axial_sym=Axial symmetry +axial_sym_des=Axial symmetry of the selected shapes +paint=Paint +paint_des=Paint shape +fix_dist=Fix
distance +paste=Paste +paste_des=Paste selected shapes +fill=Fill color +fill_des=Fill color of the shapes +area_intersection=Shape intersection +area_intersection_des=Shape from selected intersected shapes +area_union=Shape union +area_union_des=Shape from union of the selected shapes +area_substract=Shape difference +area_substract_des=Shape from the difference of two shapes +area_sym_diff=Symmetric difference +area_sym_diff_des=Symmetric difference of the selected shapes + +# menus +file=File +edit=Edit +style=Style +shapes=Shapes +exercise=Exercise +transforms=Transforms +conics=Conics +view=View +appearance=Appearance +help=Help + +item_style=Show/hide style bar +item_ruler_prot=Show/hide ruler&protractor bar +item_status=Show/hide status bar +item_action=Show/hide action bar +item_shape=Show/hide shape bar +item_tool=Show/hide tool bar +show_all=Show all +hide_all=Hide all + +# toolbars +tools=Tools +actions=Actions +tit_style=Line/Point style +ruler_prot=Ruler and Protractor + +# dialogs +exit_msg=Ignore changes? +exit_dlg=Exit +inter_msg=Select two or more shapes (none points) +inter_dlg=Intersection points error +ext_dlg=The shape must be an open shape +ext_title=Error while selecting shape +bisectrix_dlg=Parallel or coincident segments +bisectrix_title=Error while calculating bisectrix +div_dlg=Number of divisions +reg_poly_dlg=Number of vertex/sides +save_image_acce1=Image setting +save_image_acce2=Include background +save_image_acce3=Include shape names +overwrite1=File exists, overwrite? +overwrite2=Overwrite +exer_prop=Exercise properties +title=Title +exer_desc=Exercise description +start_frame=Start frame +shape_name=Shape identifier +shape_prop=Shape properties +shape_desc=Shape description +save_close=Save and Close +details=Details +selected_shapes_msg=You must select at least one shape +homo_dlg=Homothety factor +dist_mult_dlg=Multiply distance by factor +jme_dlg=JME expression +jme_examples=examples +jme_doc=JME documentation +jme_min=Min 'x' or 't' value +jme_max=Max 'x' or 't' value +jme_intervals=Number of intervals +save_error1=Error when saving +save_error2=Empty exercise +sel_2_error=Select two o more shapes +inter_error=No joins +empty_intersection_error=Empty intersection +empty_union_error=Empty union +empty_substract_error=Empty difference +empty_sym_diff_error=Empty symmetric difference + +# new shapes default description +new_point=point +new_segment=segment +new_segment_extreme=segment extreme +new_arc=arc +new_circumference=circumference +new_circumference_center=circumference center +new_arrow=arrow +new_triangle=triangle +new_rectangle=rectangle +new_square=square +new_ellipse=ellipse +new_regular_polygon=regular polygon +new_polygon=polygon +new_polyline=polyline +new_v_axis=vertical axis +new_h_axis=horizontal axis +new_parabola=parabola +new_spline=spline +new_free_hand=free hand +new_midpoint=> midpoint of +new_centroid=> centroid of +new_vertex=> vertex of +new_extreme=> extreme of +new_parallel=> parallel to +new_perpendicular=> perpendicular to +new_mediatrix=> mediatrix of +new_bisectrix=> bisectrix of +new_capable_arc=> capable arc of +new_fragment=> fragment of +new_div=> division point of +new_join=> intersection point between +new_bounds=> bounds of +new_fusion=> fusion of +new_focus=focus +new_para_vertex=parabola vertex +new_para_bounds=parabola bounds +new_directrix=directrix +new_hype_branch=hyperbola branch +new_hype_vertex=hyperbola vertex +new_hype_bounds=hyperbola bounds +new_hype_center=hyperbola center +new_main_axis=hyperbola main axis +new_img_axis=hyperbola imaginary axis +new_circumscribed=circumscribed circumference +new_center_reg=regular polygon center +new_intersection=> intersection of +new_union=> union of +new_substract=> substract of +new_sym_diff=> symmetric difference of + +# status msg +txt_hand=Use mouse wheel to zoom. Mouse dragging with second buttom to move shapes. Shift over shape for select/unselect several shapes. Drag to move viewport +txt_eyedropper=Select shape to capture line style. Shift for capture point style. Control to capture color on screen +txt_paste_style1=Select shape to apply line style. Control for don't apply fill. Shift to apply point style +txt_ruler1=Select start point +txt_ruler2=Select end point. Shift for add to current distance. Control for substract +txt_prot1=Select vertex angle +txt_prot2=Select first side point +txt_prot3=Select second side point +txt_sel1=Drag mouse to select a rectangular area. Control to force full shape selection +txt_point=Select point position. Control for non-intersection adjusting. Shift to use line style instead of point style +txt_seg1=Select segment start point +txt_seg2=Select segment end point. Shift for 45º fixed angles. Control to add extremes +txt_arrow1=Select arrow start point +txt_arrow2=Select arrow end point. Shift for 45º fixed angles. Control for double arrow +txt_circ1=Select circumference center. Shift for switch radius/diameter +txt_circ2=Select radius. Shift for switch radius/diameter. Control to add center +txt_arc1=Select arc center +txt_arc2=Select start angle +txt_arc3=Select end angle. Control for conjugate angle. Shift for fixed angle. Alt Gr for integer angles +txt_angle1=Select vertex +txt_angle2=Select end point of the first side +txt_angle3=Select end point of the second side. Shift for suplementary angle. Control for complementary angle. Alt for include both sides +txt_rect1=Select rectangle start point +txt_rect2=Move mouse to select rectangle end point. Shift for square. You can use ruler +txt_ellipse1=Select center or corner. Shift for alternate center/corner mode +txt_ellipse2=Select ellipse size. Control to add axis and focuses. Shift for alternate center/corner mode. You can use ruler +txt_poly=Select vertex. Double click to finish +txt_free1=Drag mouse to draw free shape. Double click to finish +txt_modify1=Select segment to modify +txt_modify2=Select first extreme +txt_modify3=Select second extreme. Shift for current line style instead of old segment style +txt_midpoint=Select shape to add midpoint of the shape bounds. Shift to use line style. Control for centroid +txt_vtx1=Select shape to add vertex. Shift for line style +txt_ext1=Select shape to add extreme vertex (must be an open shape) +txt_div1=Select shape to add divisions points. You can use Fix distance +txt_fragment1=Select shape to fragment sides. Press Shift for use current stroke instead of shape stroke +txt_bounds1=Select shape to add rectangle bounds +txt_perp1=Select segment +txt_perp2=Select perpendicular start +txt_perp3=Select perpendicular end +txt_para1=Select segment +txt_para2=Select parallel start +txt_para3=Select parallel end +txt_mediatrix1=Select segment +txt_mediatrix2=Select mediatrix start +txt_mediatrix3=Select mediatrix end +txt_bisectrix1=Select first segment +txt_bisectrix2=Select second segment +txt_bisectrix3=Select first bisectrix extreme +txt_bisectrix4=Select second bisectrix extreme +txt_cap1=Select segment +txt_cap2=Select one of the two sides +txt_translate1=Select first point of direction vector +txt_translate2=Select second point of direction vector. You can use ruler +txt_rotation1=Select rotation center +txt_rotation2=Move mouse to selectangle. Shift for fixed angle. Control for integer angles +txt_homothety1=Select homothety center. Shift for negative factor. Control for inverted factor +txt_central_sym1=Select symmetry center +txt_axial_sym1=Select first point of axis +txt_axial_sym2=Select second point of axis +txt_triangle_points1=Select three points. Shift for equilateral. Control for right +txt_triangle_points2=Select three points. Shift for line style +txt_parabola1=Select parabola bounds start point +txt_parabola2=Move mouse to select parabola bounds end point. Control to add additional shapes. Shift for square bounds. You can use ruler +txt_hyperbola1=Select hyperbola bounds start point +txt_hyperbola2=Move mouse to select end point. You can use the ruler +txt_hyperbola3=Move mouse to select main axis. Control to add auxiliary shapes +txt_reg_poly1=Select center of the polygon +txt_reg_poly2=Select radius. Control to add auxiliary shapes. You can use ruler +txt_paint1=Select shape to fill. Control for delete fill. Alt for line color. Shift for point color +txt_comment1=Select start point of the text box +txt_area_substract1=Select first shape +txt_area_substract2=Select second shape +txt_jme1=Select start point for coordinates origin +txt_jme2=Select end point for x axis + +# mnemonics +mne_menu_file=f +mne_menu_edit=e +mne_menu_style=s +mne_menu_shapes=h +mne_menu_tools=t +mne_menu_exercise=x +mne_menu_view=v +mne_menu_help=l +mne_menu_transform=t +mne_menu_trian=t +mne_menu_poly=o +mne_menu_conics=i +mne_menu_appea=a + +mne_about=a +mne_angle=n +mne_arc=a +mne_arrow=r +mne_sym_axial=a +mne_backward=p +mne_baricenter=b +mne_bisectrix=b +mne_bounds=g +mne_canvas_col=c +mne_cap_arc=c +mne_sym_central=c +mne_circ=c +mne_circumcenter=c +mne_copy=c +mne_delete=t +mne_divisions=s +mne_ellipse=e +mne_end=e +mne_ex_metadata=x +mne_exit=e +mne_extremes=e +mne_eyedropper=e +mne_forward=n +mne_fragment=f +mne_free=f +mne_fusion=u +mne_homo=h +mne_hyperbola=h +mne_incenter=i +mne_intersection=n +mne_invert_sel=i +mne_jme=u +mne_mediatrix=m +mne_midpoint=i +mne_mod_seg=y +mne_z_up=p +mne_z_down=d +mne_new=n +mne_open=o +mne_ortocenter=o +mne_parabola=p +mne_parallel=a +mne_paste=a +mne_perp=p +mne_point=p +mne_point_col=p +mne_polygon=p +mne_polyline=o +mne_protractor=o +mne_rect=e +mne_redo=r +mne_reg_pol=r +mne_rewind=r +mne_rotation=r +mne_ruler=l +mne_save=s +mne_save_as=a +mne_save_img=v +mne_print=p +mne_segment=s +mne_sel_all=l +mne_selection=e +mne_shape_col=s +mne_spline=l +mne_text=s +mne_trans=t +mne_triangle=t +mne_undo=u +mne_vertex=v +mne_zoom_all=z +mne_zoom_in=o +mne_zoom_out=m +mne_paint=i +mne_comment=m +mne_shape_fill=f +mne_union=u +mne_sym_diff=y + +# toast +toast_copy=shape/s copied +toast_no_copy=nothing selected +toast_paste=shape/s pasted + +# tooltips +tip_swap_color=Swap colors +tip_undo_redo=Undo/Redo history diff --git a/bin/jdrafting/resources/language/language_el.properties b/bin/jdrafting/resources/language/language_el.properties new file mode 100644 index 0000000..12399a7 --- /dev/null +++ b/bin/jdrafting/resources/language/language_el.properties @@ -0,0 +1,458 @@ +#language_el.properties +about=\u03A0\u03B5\u03C1\u03AF... +actions=\u0395\u03BD\u03AD\u03C1\u03B3\u03B5\u03B9\u03B5\u03C2 +add=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 +angle=\u0393\u03C9\u03BD\u03AF\u03B1 +angle_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 \u03B1\u03C0\u03CC \u03B3\u03C9\u03BD\u03AF\u03B1 +app_des=\u03B5\u03AF\u03BD\u03B1\u03B9 \u03BC\u03B9\u03B1 \u03B5\u03C6\u03B1\u03C1\u03BC\u03BF\u03B3\u03AE \u03C3\u03C7\u03B5\u03B4\u03AF\u03B1\u03C3\u03B7\u03C2 \u03B1\u03BD\u03BF\u03B9\u03C7\u03C4\u03BF\u03CD \u03BA\u03CE\u03B4\u03B9\u03BA\u03B1 \u03B3\u03B9\u03B1 \u03C4\u03BF \u03C3\u03C7\u03B5\u03B4\u03B9\u03B1\u03C3\u03BC\u03CC
\u03BC\u03B9\u03BA\u03C1\u03CE\u03BD \u03C3\u03BA\u03AF\u03C4\u03C3\u03C9\u03BD \u03A4\u03B5\u03C7\u03BD\u03B9\u03BA\u03BF\u03CD \u03A3\u03C7\u03B5\u03B4\u03AF\u03BF\u03C5.
\u0391\u03C5\u03C4\u03AE \u03B7 \u03B5\u03C6\u03B1\u03C1\u03BC\u03BF\u03B3\u03AE \u03B5\u03AF\u03BD\u03B1\u03B9 \u03B5\u03BC\u03C0\u03BD\u03B5\u03C5\u03C3\u03BC\u03AD\u03BD\u03B7 \u03B1\u03C0\u03CC \u03C4\u03B7\u03BD \u00ABClassical Construction\u00BB,
\u03B3\u03BD\u03C9\u03C3\u03C4\u03AE \u03BA\u03B1\u03B9 \u03C9\u03C2 \u00ABRuler-and-compass Construction\u00BB \u03AE \u00ABCompass-and-straightedge construction\u00BB.
\u03A9\u03C3\u03C4\u03CC\u03C3\u03BF, \u03BC\u03C0\u03BF\u03C1\u03B5\u03AF \u03BD\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03B7\u03B8\u03B5\u03AF \u03BA\u03B1\u03B9 \u03BC\u03B5 \u03AC\u03BB\u03BB\u03B1 \u03B5\u03C1\u03B3\u03B1\u03BB\u03B5\u03AF\u03B1 \u03C3\u03C7\u03B5\u03B4\u03AF\u03B1\u03C3\u03B7\u03C2.
(\u03A0\u03B5\u03C1\u03B9\u03C3\u03C3\u03CC\u03C4\u03B5\u03C1\u03B5\u03C2 \u03C0\u03BB\u03B7\u03C1\u03BF\u03C6\u03BF\u03C1\u03AF\u03B5\u03C2 \u03C3\u03C7\u03B5\u03C4\u03B9\u03BA\u03AC \u03BC\u03B5 \u03C4\u03B7\u03BD \u03BA\u03BB\u03B1\u03C3\u03B9\u03BA\u03AE \u03BA\u03B1\u03C4\u03B1\u03C3\u03BA\u03B5\u03C5\u03AE\: Wikipedia) +appearance=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7 +arc=\u03A4\u03CC\u03BE\u03BF +arc_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C4\u03CC\u03BE\u03BF\u03C5 +area_intersection=\u03A4\u03BF\u03BC\u03AE \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +area_intersection_des=\u03A3\u03C7\u03AE\u03BC\u03B1 \u03B1\u03C0\u03CC \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03B1 \u03C4\u03B5\u03BC\u03BD\u03CC\u03BC\u03B5\u03BD\u03B1 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03B1 +area_substract=\u0394\u03B9\u03B1\u03C6\u03BF\u03C1\u03AC \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +area_substract_des=\u03A3\u03C7\u03AE\u03BC\u03B1 \u03B1\u03C0\u03CC \u03C4\u03B7 \u03B4\u03B9\u03B1\u03C6\u03BF\u03C1\u03AC \u03B4\u03CD\u03BF \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +area_sym_diff=\u03A3\u03C5\u03BC\u03BC\u03B5\u03C4\u03C1\u03B9\u03BA\u03AE \u03B4\u03B9\u03B1\u03C6\u03BF\u03C1\u03AC +area_sym_diff_des=\u03A3\u03C5\u03BC\u03BC\u03B5\u03C4\u03C1\u03B9\u03BA\u03AE \u03B4\u03B9\u03B1\u03C6\u03BF\u03C1\u03AC \u03C4\u03C9\u03BD \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +area_union=\u0388\u03BD\u03C9\u03C3\u03B7 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +area_union_des=\u03A3\u03C7\u03AE\u03BC\u03B1 \u03B1\u03C0\u03CC \u03C4\u03B7\u03BD \u03AD\u03BD\u03C9\u03C3\u03B7 \u03C4\u03C9\u03BD \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +arrow=\u0392\u03AD\u03BB\u03BF\u03C2 +arrow_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03BC\u03BF\u03BD\u03BF\u03CD \u03AE \u03B4\u03B9\u03C0\u03BB\u03BF\u03CD \u03B2\u03AD\u03BB\u03BF\u03C5\u03C2 +axial_sym=\u0391\u03BE\u03BF\u03BD\u03B9\u03BA\u03AE \u03C3\u03C5\u03BC\u03BC\u03B5\u03C4\u03C1\u03AF\u03B1 +axial_sym_des=\u0391\u03BE\u03BF\u03BD\u03B9\u03BA\u03AE \u03C3\u03C5\u03BC\u03BC\u03B5\u03C4\u03C1\u03AF\u03B1 \u03C4\u03C9\u03BD \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +background_color=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03BA\u03B1\u03BC\u03B2\u03AC... +background_color_des=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03C6\u03CC\u03BD\u03C4\u03BF\u03C5 \u03C4\u03BF\u03C5 \u03BA\u03B1\u03BC\u03B2\u03AC +backward=\u03A0\u03C1\u03BF\u03B7\u03B3\u03BF\u03CD\u03BC\u03B5\u03BD\u03BF \u03BA\u03B1\u03C1\u03AD +backward_des=\u03A0\u03C1\u03BF\u03B7\u03B3\u03BF\u03CD\u03BC\u03B5\u03BD\u03BF \u03BA\u03B1\u03C1\u03AD +baricenter=\u0392\u03B1\u03C1\u03CD\u03BA\u03B5\u03BD\u03C4\u03C1\u03BF +bisectrix=\u0394\u03B9\u03C7\u03BF\u03C4\u03CC\u03BC\u03BF\u03C2 +bisectrix_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03B4\u03B9\u03C7\u03BF\u03C4\u03CC\u03BC\u03BF\u03C5 +bisectrix_dlg=\u03A4\u03B1 \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03B1 \u03B5\u03AF\u03BD\u03B1\u03B9 \u03C0\u03B1\u03C1\u03AC\u03BB\u03BB\u03B7\u03BB\u03B1 \u03AE \u03C3\u03C5\u03BC\u03C0\u03AF\u03C0\u03C4\u03BF\u03C5\u03BD +bisectrix_title=\u03A3\u03C6\u03AC\u03BB\u03BC\u03B1 \u03BA\u03B1\u03C4\u03AC \u03C4\u03BF\u03BD \u03C5\u03C0\u03BF\u03BB\u03BF\u03B3\u03B9\u03C3\u03BC\u03CC \u03C4\u03B7\u03C2 \u03B4\u03B9\u03C7\u03BF\u03C4\u03CC\u03BC\u03BF\u03C5 +bounds=\u039F\u03C1\u03B8\u03BF\u03B3\u03CE\u03BD\u03B9\u03B1 \u03CC\u03C1\u03B9\u03B1 +bounds_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03BF\u03C1\u03B8\u03BF\u03B3\u03C9\u03BD\u03AF\u03C9\u03BD \u03BF\u03C1\u03AF\u03C9\u03BD \u03B5\u03BD\u03CC\u03C2 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +cancel=\u0386\u03BA\u03C5\u03C1\u03BF +capable_arc=\u0397\u03BC\u03B9\u03BA\u03CD\u03BA\u03BB\u03B9\u03BF +capable_arc_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03B7\u03BC\u03B9\u03BA\u03C5\u03BA\u03BB\u03AF\u03BF\u03C5 \u03B1\u03C0\u03CC \u03AD\u03BD\u03B1 \u03C4\u03BC\u03AE\u03BC\u03B1 +central_sym=\u039A\u03B5\u03BD\u03C4\u03C1\u03B9\u03BA\u03AE \u03C3\u03C5\u03BC\u03BC\u03B5\u03C4\u03C1\u03AF\u03B1 +central_sym_des=\u039A\u03B5\u03BD\u03C4\u03C1\u03B9\u03BA\u03AE \u03C3\u03C5\u03BC\u03BC\u03B5\u03C4\u03C1\u03AF\u03B1 \u03C4\u03C9\u03BD \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +centroid=\u039A\u03B5\u03BD\u03C4\u03C1\u03BF\u03B5\u03B9\u03B4\u03AD\u03C2 +circumcenter=\u03A0\u03B5\u03C1\u03AF\u03BA\u03B5\u03BD\u03C4\u03C1\u03BF +circumference=\u03A0\u03B5\u03C1\u03B9\u03C6\u03AD\u03C1\u03B5\u03B9\u03B1 \u03BA\u03CD\u03BA\u03BB\u03BF\u03C5 +circumference_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C0\u03B5\u03C1\u03B9\u03C6\u03AD\u03C1\u03B5\u03B9\u03B1\u03C2 \u03BA\u03CD\u03BA\u03BB\u03BF\u03C5 +color=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2... +color_des=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +comment=\u03A3\u03C7\u03CC\u03BB\u03B9\u03BF +comment_des=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03C3\u03C7\u03BF\u03BB\u03AF\u03BF\u03C5 +conics=\u039A\u03C9\u03BD\u03B9\u03BA\u03AC +copy=\u0391\u03BD\u03C4\u03B9\u03B3\u03C1\u03B1\u03C6\u03AE \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03BF\u03C5 +copy_des=\u0391\u03BD\u03C4\u03B9\u03B3\u03C1\u03B1\u03C6\u03AE \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +degrees=\ \u03BC\u03BF\u03AF\u03C1\u03B5\u03C2 +delete=\u0394\u03B9\u03B1\u03B3\u03C1\u03B1\u03C6\u03AE \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD +delete_des=\u0394\u03B9\u03B1\u03B3\u03C1\u03B1\u03C6\u03AE \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +details=\u039B\u03B5\u03C0\u03C4\u03BF\u03BC\u03AD\u03C1\u03B5\u03B9\u03B5\u03C2 +dist_mult_dlg=\u03A0\u03BF\u03BB\u03BB\u03B1\u03C0\u03BB\u03B1\u03C3\u03B9\u03B1\u03C3\u03BC\u03CC\u03C2 \u03B1\u03C0\u03CC\u03C3\u03C4\u03B1\u03C3\u03B7\u03C2 \u03B2\u03AC\u03C3\u03B5\u03B9 \u03C3\u03C5\u03BD\u03C4\u03B5\u03BB\u03B5\u03C3\u03C4\u03AE +div_dlg=\u0391\u03C1\u03B9\u03B8\u03BC\u03CC\u03C2 \u03C4\u03BC\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +divisions=\u03A3\u03B7\u03BC\u03B5\u03AF\u03B1 \u03B4\u03B9\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7\u03C2 +divisions_des=\u0394\u03B9\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 \u03C3\u03B5 \u03C3\u03B7\u03BC\u03B5\u03AF\u03B1 +edit=\u0395\u03C0\u03B5\u03BE\u03B5\u03C1\u03B3\u03B1\u03C3\u03AF\u03B1 +ellipse=\u0388\u03BB\u03BB\u03B5\u03B9\u03C8\u03B7 +ellipse_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03AD\u03BB\u03BB\u03B5\u03B9\u03C8\u03B7\u03C2 +empty_intersection_error=\u039A\u03B5\u03BD\u03AE \u03C4\u03BF\u03BC\u03AE +empty_substract_error=\u039A\u03B5\u03BD\u03AE \u03B4\u03B9\u03B1\u03C6\u03BF\u03C1\u03AC +empty_sym_diff_error=\u039A\u03B5\u03BD\u03AE \u03C3\u03C5\u03BC\u03BC\u03B5\u03C4\u03C1\u03B9\u03BA\u03AE \u03B4\u03B9\u03B1\u03C6\u03BF\u03C1\u03AC +empty_union_error=\u039A\u03B5\u03BD\u03AE \u03AD\u03BD\u03C9\u03C3\u03B7 +end=\u03A4\u03B5\u03BB\u03B5\u03C5\u03C4\u03B1\u03AF\u03BF \u03BA\u03B1\u03C1\u03AD +end_des=\u03A4\u03B5\u03BB\u03B5\u03C5\u03C4\u03B1\u03AF\u03BF \u03BA\u03B1\u03C1\u03AD +exer_desc=\u03A0\u03B5\u03C1\u03B9\u03B3\u03C1\u03B1\u03C6\u03AE \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7\u03C2 +exer_prop=\u0399\u03B4\u03B9\u03CC\u03C4\u03B7\u03C4\u03B5\u03C2 \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7\u03C2 +exercise=\u0386\u03C3\u03BA\u03B7\u03C3\u03B7 +exit=\u0388\u03BE\u03BF\u03B4\u03BF\u03C2 +exit_des=\u0388\u03BE\u03BF\u03B4\u03BF\u03C2 \u03C0\u03C1\u03BF\u03B3\u03C1\u03AC\u03BC\u03BC\u03B1\u03C4\u03BF\u03C2 +exit_dlg=\u0388\u03BE\u03BF\u03B4\u03BF\u03C2 +exit_msg=\u03A0\u03B1\u03C1\u03AC\u03B2\u03BB\u03B5\u03C8\u03B7 \u03C4\u03C9\u03BD \u03B1\u03BB\u03BB\u03B1\u03B3\u03CE\u03BD; +ext_dlg=\u03A4\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 \u03C0\u03C1\u03AD\u03C0\u03B5\u03B9 \u03BD\u03B1 \u03B5\u03AF\u03BD\u03B1\u03B9 \u03AD\u03BD\u03B1 \u03B1\u03BD\u03BF\u03B9\u03C7\u03C4\u03CC \u03C3\u03C7\u03AE\u03BC\u03B1 +ext_title=\u03A3\u03C6\u03AC\u03BB\u03BC\u03B1 \u03BA\u03B1\u03C4\u03AC \u03C4\u03B7\u03BD \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +extremes=\u0386\u03BA\u03C1\u03B1 +extremes_des=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03B1\u03BA\u03C1\u03B1\u03AF\u03C9\u03BD \u03C3\u03B7\u03BC\u03B5\u03AF\u03C9\u03BD \u03C3\u03B5 \u03B1\u03BD\u03BF\u03B9\u03C7\u03C4\u03AC \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03B1 +eyedropper=\u03A3\u03C4\u03B1\u03B3\u03BF\u03BD\u03CC\u03BC\u03B5\u03C4\u03C1\u03BF +eyedropper_des=\u039B\u03AE\u03C8\u03B7 \u03C3\u03C4\u03C5\u03BB \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +file=\u0391\u03C1\u03C7\u03B5\u03AF\u03BF +fileinfo=\u03A0\u03BB\u03B7\u03C1\u03BF\u03C6\u03BF\u03C1\u03AF\u03B5\u03C2 \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7\u03C2 +fileinfo_des=\u039B\u03AE\u03C8\u03B7 \u03AE \u03C4\u03C1\u03BF\u03C0\u03BF\u03C0\u03BF\u03AF\u03B7\u03C3\u03B7 \u03C4\u03C9\u03BD \u03C0\u03BB\u03B7\u03C1\u03BF\u03C6\u03BF\u03C1\u03B9\u03CE\u03BD \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7\u03C2 +fill=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03B3\u03B5\u03BC\u03AF\u03C3\u03BC\u03B1\u03C4\u03BF\u03C2 +fill_des=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03B3\u03B5\u03BC\u03AF\u03C3\u03BC\u03B1\u03C4\u03BF\u03C2 \u03C4\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +fix_dist=\u03A3\u03C4\u03B1\u03B8\u03B5\u03C1\u03AE
\u03B1\u03C0\u03CC\u03C3\u03C4\u03B1\u03C3\u03B7 +forward=\u0395\u03C0\u03CC\u03BC\u03B5\u03BD\u03BF \u03BA\u03B1\u03C1\u03AD +forward_des=\u0395\u03C0\u03CC\u03BC\u03B5\u03BD\u03BF \u03BA\u03B1\u03C1\u03AD +fragment=\u039A\u03B1\u03C4\u03AC\u03C4\u03BC\u03B7\u03C3\u03B7 \u03C0\u03BB\u03B5\u03C5\u03C1\u03CE\u03BD \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +fragment_des=\u039A\u03B1\u03C4\u03AC\u03C4\u03BC\u03B7\u03C3\u03B7 \u03C0\u03BB\u03B5\u03C5\u03C1\u03CE\u03BD \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 \u03C3\u03B5 \u03BC\u03B5\u03BC\u03BF\u03BD\u03C9\u03BC\u03AD\u03BD\u03B1 \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03B1 +free_hand=\u0395\u03BB\u03B5\u03CD\u03B8\u03B5\u03C1\u03BF \u03C7\u03AD\u03C1\u03B9 +free_hand_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03B1\u03C5\u03B8\u03B1\u03AF\u03C1\u03B5\u03C4\u03BF\u03C5 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +func=\u03A3\u03C5\u03BD\u03AC\u03C1\u03C4\u03B7\u03C3\u03B7 +func_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03B3\u03C1\u03B1\u03C6\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 \u03B1\u03C0\u03CC \u03C3\u03C5\u03BD\u03AC\u03C1\u03C4\u03B7\u03C3\u03B7 +fusion=\u03A3\u03C5\u03B3\u03C7\u03CE\u03BD\u03B5\u03C5\u03C3\u03B7 \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +fusion_des=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03B1 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03B1 \u03C0\u03BF\u03C5 \u03B8\u03B1 \u03C3\u03C5\u03B3\u03C7\u03C9\u03BD\u03B5\u03C5\u03B8\u03BF\u03CD\u03BD. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C3\u03CD\u03BD\u03B4\u03B5\u03C3\u03B7 \u03C4\u03C9\u03BD \u03AC\u03BA\u03C1\u03C9\u03BD +height=\u038E\u03C8\u03BF\u03C2 +help=\u0392\u03BF\u03AE\u03B8\u03B5\u03B9\u03B1 +hide_all=\u0391\u03C0\u03CC\u03BA\u03C1\u03C5\u03C8\u03B7 \u03CC\u03BB\u03C9\u03BD +homo_dlg=\u03A3\u03C5\u03BD\u03C4\u03B5\u03BB\u03B5\u03C3\u03C4\u03AE\u03C2 \u03C0\u03C1\u03BF\u03BF\u03C0\u03C4\u03B9\u03BA\u03BF\u03CD +homothety=\u03A0\u03C1\u03BF\u03BF\u03C0\u03C4\u03B9\u03BA\u03CC +homothety_des=\u03A0\u03C1\u03BF\u03BF\u03C0\u03C4\u03B9\u03BA\u03CC \u03C4\u03C9\u03BD \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +hyperbola=\u03A5\u03C0\u03B5\u03C1\u03B2\u03BF\u03BB\u03AE +hyperbola_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C5\u03C0\u03B5\u03C1\u03B2\u03BF\u03BB\u03AE\u03C2 \u03B1\u03C0\u03CC \u03BF\u03C1\u03B8\u03BF\u03B3\u03CE\u03BD\u03B9\u03B1 \u03CC\u03C1\u03B9\u03B1 +incenter=\u0388\u03B3\u03BA\u03B5\u03BD\u03C4\u03C1\u03BF +inter=\u03A3\u03B7\u03BC\u03B5\u03AF\u03B1 \u03C4\u03BF\u03BC\u03AE\u03C2 +inter_des=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03B4\u03CD\u03BF \u03AE \u03C0\u03B5\u03C1\u03B9\u03C3\u03C3\u03CC\u03C4\u03B5\u03C1\u03B1 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03B1 (\u03BA\u03B1\u03BD\u03AD\u03BD\u03B1 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF) \u03B3\u03B9\u03B1 \u03BD\u03B1 \u03C0\u03C1\u03BF\u03C3\u03B8\u03AD\u03C3\u03B5\u03C4\u03B5 \u03C4\u03BF\u03BC\u03AD\u03C2 \u03BC\u03B5\u03C4\u03B1\u03BE\u03CD \u03C4\u03BF\u03C5\u03C2 +inter_dlg=\u03A3\u03C6\u03AC\u03BB\u03BC\u03B1 \u03C3\u03B7\u03BC\u03B5\u03AF\u03C9\u03BD \u03C4\u03BF\u03BC\u03AE\u03C2 +inter_error=\u03A7\u03C9\u03C1\u03AF\u03C2 \u03C3\u03C5\u03BD\u03B4\u03AD\u03C3\u03BC\u03BF\u03C5\u03C2 +inter_msg=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03B4\u03CD\u03BF \u03AE \u03C0\u03B5\u03C1\u03B9\u03C3\u03C3\u03CC\u03C4\u03B5\u03C1\u03B1 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03B1 (\u03BA\u03B1\u03BD\u03AD\u03BD\u03B1 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF) +invert=\u0391\u03BD\u03C4\u03B9\u03C3\u03C4\u03C1\u03BF\u03C6\u03AE \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE\u03C2 +invert_des=\u0391\u03BD\u03C4\u03B9\u03C3\u03C4\u03C1\u03BF\u03C6\u03AE \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE\u03C2 +item_action=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7/\u03B1\u03C0\u03CC\u03BA\u03C1\u03C5\u03C8\u03B7 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 \u03B5\u03BD\u03B5\u03C1\u03B3\u03B5\u03B9\u03CE\u03BD +item_ruler_prot=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7/\u03B1\u03C0\u03CC\u03BA\u03C1\u03C5\u03C8\u03B7 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 \u03C7\u03AC\u03C1\u03B1\u03BA\u03B1 & \u03BC\u03BF\u03B9\u03C1\u03BF\u03B3\u03BD\u03C9\u03BC\u03CC\u03BD\u03B9\u03BF\u03C5 +item_shape=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7/\u03B1\u03C0\u03CC\u03BA\u03C1\u03C5\u03C8\u03B7 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +item_status=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7/\u03B1\u03C0\u03CC\u03BA\u03C1\u03C5\u03C8\u03B7 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 \u03BA\u03B1\u03C4\u03AC\u03C3\u03C4\u03B1\u03C3\u03B7\u03C2 +item_style=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7/\u03B1\u03C0\u03CC\u03BA\u03C1\u03C5\u03C8\u03B7 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 \u03C3\u03C4\u03C5\u03BB +item_tool=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7/\u03B1\u03C0\u03CC\u03BA\u03C1\u03C5\u03C8\u03B7 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 \u03B5\u03C1\u03B3\u03B1\u03BB\u03B5\u03AF\u03C9\u03BD +jme_dlg=\u03A0\u03B1\u03C1\u03AC\u03C3\u03C4\u03B1\u03C3\u03B7 JME +jme_doc=\u03A4\u03B5\u03BA\u03BC\u03B7\u03C1\u03AF\u03C9\u03C3\u03B7 JME +jme_examples=\u03C0\u03B1\u03C1\u03B1\u03B4\u03B5\u03AF\u03B3\u03BC\u03B1\u03C4\u03B1 +jme_intervals=\u0391\u03C1\u03B9\u03B8\u03BC\u03CC\u03C2 \u03B4\u03B9\u03B1\u03C3\u03C4\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +jme_max=\u039C\u03AD\u03B3\u03B9\u03C3\u03C4\u03B7 \u03C4\u03B9\u03BC\u03AE 'x' \u03AE 't' +jme_min=\u0395\u03BB\u03AC\u03C7\u03B9\u03C3\u03C4\u03B7 \u03C4\u03B9\u03BC\u03AE 'x '\u03AE 't' +lbl_fill=\u0393\u03AD\u03BC\u03B9\u03C3\u03BC\u03B1 +lbl_fill_color=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03B3\u03B5\u03BC\u03AF\u03C3\u03BC\u03B1\u03C4\u03BF\u03C2 +lbl_line_color=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 +lbl_no_fill=\u03A7\u03C9\u03C1\u03AF\u03C2 \u03B3\u03AD\u03BC\u03B9\u03C3\u03BC\u03B1 +lbl_point_color=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 +mediatrix=\u039C\u03B5\u03C3\u03BF\u03BA\u03AC\u03B8\u03B5\u03C4\u03BF\u03C2 +mediatrix_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03B9\u03C5\u03C1\u03B3\u03AF\u03B1 \u03BC\u03B5\u03C3\u03BF\u03BA\u03B1\u03B8\u03AD\u03C4\u03BF\u03C5 \u03B5\u03BD\u03CC\u03C2 \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +midpoint=\u039C\u03AD\u03C3\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF +midpoint_des=\u039F\u03C1\u03B9\u03C3\u03BC\u03CC\u03C2 \u03C4\u03BF\u03C5 \u03BC\u03AD\u03C3\u03BF\u03C5 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 \u03C3\u03C4\u03BF \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03C4\u03C9\u03BD \u03BF\u03C1\u03AF\u03C9\u03BD \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +mne_about=\u03C0 +mne_angle=\u03B3 +mne_arc=\u03BE +mne_arrow=\u03B2 +mne_backward=\u03C0 +mne_baricenter=\u03B2 +mne_bisectrix=\u03B9 +mne_bounds=\u03B8 +mne_canvas_col=\u03BA +mne_cap_arc=\u03B7 +mne_circ=\u03C6 +mne_circumcenter=\u03C0 +mne_comment=\u03C7 +mne_copy=\u03B3 +mne_delete=\u03B4 +mne_divisions=\u03B4 +mne_ellipse=\u03B5 +mne_end=\u03C4 +mne_ex_metadata=\u03BB +mne_exit=\u03BE +mne_extremes=\u03B1 +mne_eyedropper=\u03B3 +mne_forward=\u03BC +mne_fragment=\u03BB +mne_free=\u03B5 +mne_fusion=\u03C5 +mne_homo=\u03C1 +mne_hyperbola=\u03C5 +mne_incenter=\u03BA +mne_intersection=\u03BF +mne_invert_sel=\u03C3 +mne_jme=\u03C5 +mne_mediatrix=\u03C3 +mne_menu_appea=\u03B5 +mne_menu_conics=\u03BA +mne_menu_edit=\u03B5 +mne_menu_exercise=\u03BA +mne_menu_file=\u03B1 +mne_menu_help=\u03B2 +mne_menu_poly=\u03C0 +mne_menu_shapes=\u03C7 +mne_menu_style=\u03C3 +mne_menu_tools=\u03B3 +mne_menu_transform=\u03BC +mne_menu_trian=\u03C4 +mne_menu_view=\u03BF +mne_midpoint=\u03B5 +mne_mod_seg=\u03C4 +mne_new=\u03C3 +mne_open=\u03BD +mne_ortocenter=\u03BF +mne_paint=\u03B2 +mne_parabola=\u03C0 +mne_parallel=\u03C0 +mne_paste=\u03B9 +mne_perp=\u03BA +mne_point=\u03C3 +mne_point_col=\u03C3 +mne_polygon=\u03C0 +mne_polyline=\u03C4 +mne_print=\u03B5 +mne_protractor=\u03B3 +mne_rect=\u03BF +mne_redo=\u03B5 +mne_reg_pol=\u03BA +mne_rewind=\u03B5 +mne_rotation=\u03C0 +mne_ruler=\u03C7 +mne_save=\u03B1 +mne_save_as=\u03C9 +mne_save_img=\u03B5 +mne_segment=\u03BC +mne_sel_all=\u03BB +mne_selection=\u03BF +mne_shape_col=\u03C7 +mne_shape_fill=\u03BC +mne_spline=\u03BB +mne_sym_axial=\u03B1 +mne_sym_central=\u03BA +mne_sym_diff=\u03C3 +mne_text=\u03BF +mne_trans=\u03BC +mne_triangle=\u03C4 +mne_undo=\u03C1 +mne_union=\u03B5 +mne_vertex=\u03C1 +mne_z_down=\u03BA +mne_z_up=\u03C0 +mne_zoom_all=\u03BB +mne_zoom_in=\u03BC +mne_zoom_out=\u03C3 +modify=\u03A4\u03C1\u03BF\u03C0\u03BF\u03C0\u03BF\u03AF\u03B7\u03C3\u03B7 \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +modify_des=\u03A4\u03C1\u03BF\u03C0\u03BF\u03C0\u03BF\u03AF\u03B7\u03C3\u03B7 \u03B5\u03BD\u03CC\u03C2 \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 \u03BA\u03B1\u03C4\u03AC \u03BC\u03AE\u03BA\u03BF\u03C2 \u03C4\u03B7\u03C2 \u03B5\u03C5\u03B8\u03B5\u03AF\u03B1\u03C2 \u03C4\u03BF\u03C5 +move_down=\u03A3\u03C7\u03AE\u03BC\u03B1\u03C4\u03B1 \u03BA\u03AC\u03C4\u03C9 +move_down_des=\u039C\u03B5\u03AF\u03C9\u03C3\u03B7 \u03C4\u03BF\u03C5 \u03B4\u03B5\u03AF\u03BA\u03C4\u03B7 \u03C4\u03C9\u03BD \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +move_up=\u03A3\u03C7\u03AE\u03BC\u03B1\u03C4\u03B1 \u03C0\u03AC\u03BD\u03C9 +move_up_des=\u0391\u03CD\u03BE\u03B7\u03C3\u03B7 \u03C4\u03BF\u03C5 \u03B4\u03B5\u03AF\u03BA\u03C4\u03B7 \u03C4\u03C9\u03BD \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +new=\u039D\u03AD\u03B1 \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7 +new_arc=\u03C4\u03CC\u03BE\u03BF +new_arrow=\u03B2\u03AD\u03BB\u03BF\u03C2 +new_bisectrix=> \u03B4\u03B9\u03C7\u03BF\u03C4\u03CC\u03BC\u03BF\u03C2 \u03C4\u03BF\u03C5 +new_bounds=> \u03CC\u03C1\u03B9\u03B1 \u03C4\u03BF\u03C5 +new_capable_arc=> \u03B7\u03BC\u03B9\u03BA\u03CD\u03BA\u03BB\u03B9\u03BF \u03C4\u03BF\u03C5 +new_center_reg=\u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03BA\u03B1\u03BD\u03BF\u03BD\u03B9\u03BA\u03BF\u03CD \u03C0\u03BF\u03BB\u03C5\u03B3\u03CE\u03BD\u03BF\u03C5 +new_centroid=> \u03BA\u03B5\u03BD\u03C4\u03C1\u03BF\u03B5\u03B9\u03B4\u03AD\u03C2 \u03C4\u03BF\u03C5 +new_circumference=\u03C0\u03B5\u03C1\u03B9\u03C6\u03AD\u03C1\u03B5\u03B9\u03B1 \u03BA\u03CD\u03BA\u03BB\u03BF\u03C5 +new_circumference_center=\u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03C0\u03B5\u03C1\u03B9\u03C6\u03AD\u03C1\u03B5\u03B9\u03B1\u03C2 \u03BA\u03CD\u03BA\u03BB\u03BF\u03C5 +new_circumscribed=\u03BF\u03C1\u03B9\u03BF\u03B8\u03B5\u03C4\u03B7\u03BC\u03AD\u03BD\u03B7 \u03C0\u03B5\u03C1\u03B9\u03C6\u03AD\u03C1\u03B5\u03B9\u03B1 \u03BA\u03CD\u03BA\u03BB\u03BF\u03C5 +new_des=\u0388\u03BD\u03B1\u03C1\u03BE\u03B7 \u03BC\u03B9\u03B1\u03C2 \u03BD\u03AD\u03B1\u03C2 \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7\u03C2 +new_directrix=\u03B4\u03B9\u03B5\u03C5\u03B8\u03B5\u03C4\u03BF\u03CD\u03C3\u03B1 +new_div=> \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03B4\u03B9\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7\u03C2 \u03C4\u03BF\u03C5 +new_ellipse=\u03AD\u03BB\u03BB\u03B5\u03B9\u03C8\u03B7 +new_extreme=> \u03AC\u03BA\u03C1\u03BF \u03C4\u03BF\u03C5 +new_focus=\u03B5\u03C3\u03C4\u03AF\u03B1\u03C3\u03B7 +new_fragment=> \u03BA\u03B1\u03C4\u03AC\u03C4\u03BC\u03B7\u03C3\u03B7 \u03C4\u03BF\u03C5 +new_free_hand=\u03B1\u03C5\u03B8\u03B1\u03AF\u03C1\u03B5\u03C4\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 +new_fusion=> \u03C3\u03C5\u03B3\u03C7\u03CE\u03BD\u03B5\u03C5\u03C3\u03B7 \u03C4\u03C9\u03BD +new_h_axis=\u03BF\u03C1\u03B9\u03B6\u03CC\u03BD\u03C4\u03B9\u03BF\u03C2 \u03AC\u03BE\u03BF\u03BD\u03B1\u03C2 +new_hype_bounds=\u03CC\u03C1\u03B9\u03B1 \u03C5\u03C0\u03B5\u03C1\u03B2\u03BF\u03BB\u03AE\u03C2 +new_hype_branch=\u03BA\u03BB\u03AC\u03B4\u03BF\u03C2 \u03C5\u03C0\u03B5\u03C1\u03B2\u03BF\u03BB\u03AE\u03C2 +new_hype_center=\u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03C5\u03C0\u03B5\u03C1\u03B2\u03BF\u03BB\u03AE\u03C2 +new_hype_vertex=\u03BA\u03BF\u03C1\u03C5\u03C6\u03AE \u03C5\u03C0\u03B5\u03C1\u03B2\u03BF\u03BB\u03AE\u03C2 +new_img_axis=\u03C6\u03B1\u03BD\u03C4\u03B1\u03C3\u03C4\u03B9\u03BA\u03CC\u03C2 \u03AC\u03BE\u03BF\u03BD\u03B1\u03C2 \u03C5\u03C0\u03B5\u03C1\u03B2\u03BF\u03BB\u03AE\u03C2 +new_intersection=> \u03C4\u03BF\u03BC\u03AE \u03C4\u03BF\u03C5 +new_join=> \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03BC\u03AE\u03C2 \u03BC\u03B5\u03C4\u03B1\u03BE\u03CD +new_main_axis=\u03BA\u03CD\u03C1\u03B9\u03BF\u03C2 \u03AC\u03BE\u03BF\u03BD\u03B1\u03C2 \u03C5\u03C0\u03B5\u03C1\u03B2\u03BF\u03BB\u03AE\u03C2 +new_mediatrix=> \u03BC\u03B5\u03C3\u03BF\u03BA\u03AC\u03B8\u03B5\u03C4\u03BF\u03C2 \u03C4\u03BF\u03C5 +new_midpoint=> \u03BC\u03AD\u03C3\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 +new_para_bounds=\u03CC\u03C1\u03B9\u03B1 \u03C0\u03B1\u03C1\u03B1\u03B2\u03BF\u03BB\u03AE\u03C2 +new_para_vertex=\u03BA\u03BF\u03C1\u03C5\u03C6\u03AE \u03C0\u03B1\u03C1\u03B1\u03B2\u03BF\u03BB\u03AE\u03C2 +new_parabola=\u03C0\u03B1\u03C1\u03B1\u03B2\u03BF\u03BB\u03AE +new_parallel=> \u03C0\u03B1\u03C1\u03AC\u03BB\u03BB\u03B7\u03BB\u03BF\u03C2 \u03BC\u03B5 +new_perpendicular=> \u03BA\u03AC\u03B8\u03B5\u03C4\u03B7 \u03C0\u03C1\u03BF\u03C2 +new_point=\u03C3\u03B7\u03BC\u03B5\u03AF\u03BF +new_polygon=\u03C0\u03BF\u03BB\u03CD\u03B3\u03C9\u03BD\u03BF +new_polyline=\u03C4\u03B5\u03B8\u03BB\u03B1\u03C3\u03BC\u03AD\u03BD\u03B7 +new_rectangle=\u03BF\u03C1\u03B8\u03BF\u03B3\u03CE\u03BD\u03B9\u03BF +new_regular_polygon=\u03BA\u03B1\u03BD\u03BF\u03BD\u03B9\u03BA\u03CC \u03C0\u03BF\u03BB\u03CD\u03B3\u03C9\u03BD\u03BF +new_segment=\u03C4\u03BC\u03AE\u03BC\u03B1 +new_segment_extreme=\u03B1\u03BA\u03C1\u03B1\u03AF\u03BF \u03C4\u03BC\u03AE\u03BC\u03B1 +new_spline=\u03BA\u03B1\u03BC\u03C0\u03CD\u03BB\u03B7 +new_square=\u03C4\u03B5\u03C4\u03C1\u03AC\u03B3\u03C9\u03BD\u03BF +new_substract=> \u03B1\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u03C4\u03BF\u03C5 +new_sym_diff=> \u03C3\u03C5\u03BC\u03BC\u03B5\u03C4\u03C1\u03B9\u03BA\u03AE \u03B4\u03B9\u03B1\u03C6\u03BF\u03C1\u03AC \u03C4\u03BF\u03C5 +new_triangle=\u03C4\u03C1\u03AF\u03B3\u03C9\u03BD\u03BF +new_union=> \u03AD\u03BD\u03C9\u03C3\u03B7 \u03C4\u03BF\u03C5 +new_v_axis=\u03BA\u03B1\u03C4\u03B1\u03BA\u03CC\u03C1\u03C5\u03C6\u03BF\u03C2 \u03AC\u03BE\u03BF\u03BD\u03B1\u03C2 +new_vertex=> \u03BA\u03BF\u03C1\u03C5\u03C6\u03AE \u03C4\u03BF\u03C5 +not_saved=\u0394\u03B5\u03BD \u03B1\u03C0\u03BF\u03B8\u03B7\u03BA\u03B5\u03CD\u03C4\u03B7\u03BA\u03B5 +open=\u0386\u03BD\u03BF\u03B9\u03B3\u03BC\u03B1... +open_des=\u0386\u03BD\u03BF\u03B9\u03B3\u03BC\u03B1 \u03BC\u03B9\u03B1\u03C2 \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7\u03C2 +ortocenter=\u039F\u03C1\u03B8\u03CC\u03BA\u03B5\u03BD\u03C4\u03C1\u03BF +overwrite1=\u03A4\u03BF \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF \u03C5\u03C0\u03AC\u03C1\u03C7\u03B5\u03B9. \u039D\u03B1 \u03B1\u03BD\u03C4\u03B9\u03BA\u03B1\u03C4\u03B1\u03C3\u03C4\u03B1\u03B8\u03B5\u03AF; +overwrite2=\u0391\u03BD\u03C4\u03B9\u03BA\u03B1\u03C4\u03AC\u03C3\u03C4\u03B1\u03C3\u03B7 +paint=\u0392\u03B1\u03C6\u03AE +paint_des=\u0392\u03B1\u03C6\u03AE \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +para=\u03A0\u03B1\u03C1\u03AC\u03BB\u03BB\u03B7\u03BB\u03B7 +para_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C0\u03B1\u03C1\u03B1\u03BB\u03BB\u03AE\u03BB\u03BF\u03C5 \u03B1\u03C0\u03CC \u03C4\u03BC\u03AE\u03BC\u03B1 +parabola=\u03A0\u03B1\u03C1\u03B1\u03B2\u03BF\u03BB\u03AE +parabola_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C0\u03B1\u03C1\u03B1\u03B2\u03BF\u03BB\u03AE\u03C2 \u03B1\u03C0\u03CC \u03BF\u03C1\u03B8\u03BF\u03B3\u03CE\u03BD\u03B9\u03B1 \u03CC\u03C1\u03B9\u03B1 +paste=\u0395\u03C0\u03B9\u03BA\u03CC\u03BB\u03BB\u03B7\u03C3\u03B7 +paste_des=\u0395\u03C0\u03B9\u03BA\u03CC\u03BB\u03BB\u03B7\u03C3\u03B7 \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +paste_style=\u0395\u03C0\u03B9\u03BA\u03CC\u03BB\u03BB\u03B7\u03C3\u03B7 \u03C3\u03C4\u03C5\u03BB +paste_style_des=\u0395\u03C0\u03B9\u03BA\u03CC\u03BB\u03BB\u03B7\u03C3\u03B7 \u03C4\u03C1\u03AD\u03C7\u03BF\u03BD\u03C4\u03BF\u03C2 \u03C3\u03C4\u03C5\u03BB \u03C7\u03C1\u03CE\u03BC\u03B1\u03C4\u03BF\u03C2 \u03BA\u03B1\u03B9 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 +perp=\u039A\u03AC\u03B8\u03B5\u03C4\u03BF\u03C2 +perp_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03BA\u03B1\u03B8\u03AD\u03C4\u03BF\u03C5 \u03B1\u03C0\u03CC \u03C4\u03BC\u03AE\u03BC\u03B1 +point=\u03A3\u03B7\u03BC\u03B5\u03AF\u03BF +point_color=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5... +point_color_des=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 +point_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03BD\u03AD\u03BF\u03C5 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 +point_thickness=

\u03A0\u03AC\u03C7\u03BF\u03C2

\u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 +polygon=\u03A0\u03BF\u03BB\u03CD\u03B3\u03C9\u03BD\u03BF +polygon_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C0\u03BF\u03BB\u03CD\u03B3\u03C9\u03BD\u03BF\u03C5 +polygon_tools=\u0395\u03C1\u03B3\u03B1\u03BB\u03B5\u03AF\u03B1 \u03C0\u03BF\u03BB\u03C5\u03B3\u03CE\u03BD\u03BF\u03C5 +polyline=\u03A4\u03B5\u03B8\u03BB\u03B1\u03C3\u03BC\u03AD\u03BD\u03B7 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE +polyline_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C4\u03B5\u03B8\u03BB\u03B1\u03C3\u03BC\u03AD\u03BD\u03B7\u03C2 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 +print=\u0395\u03BA\u03C4\u03CD\u03C0\u03C9\u03C3\u03B7... +print_des=\u0395\u03BA\u03C4\u03CD\u03C0\u03C9\u03C3\u03B7 \u03C3\u03C4\u03BF\u03BD \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03BF \u03B5\u03BA\u03C4\u03C5\u03C0\u03C9\u03C4\u03AE +protractor=\u039C\u03BF\u03B9\u03C1\u03BF\u03B3\u03BD\u03C9\u03BC\u03CC\u03BD\u03B9\u03BF +protractor_des=\u039A\u03B1\u03C4\u03B1\u03B3\u03C1\u03B1\u03C6\u03AE \u03B3\u03C9\u03BD\u03AF\u03B1\u03C2 +rectangle=\u039F\u03C1\u03B8\u03BF\u03B3\u03CE\u03BD\u03B9\u03BF +rectangle_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C0\u03BF\u03BB\u03C5\u03B3\u03CE\u03BD\u03BF\u03C5 +redo=\u0395\u03C0\u03B1\u03BD\u03AC\u03BB\u03B7\u03C8\u03B7 \u03B5\u03BD\u03AD\u03C1\u03B3\u03B5\u03B9\u03B1\u03C2 +reg_poly=\u039A\u03B1\u03BD\u03BF\u03BD\u03B9\u03BA\u03CC \u03C0\u03BF\u03BB\u03CD\u03B3\u03C9\u03BD\u03BF +reg_poly_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03BA\u03B1\u03BD\u03BF\u03BD\u03B9\u03BA\u03BF\u03CD \u03C0\u03BF\u03BB\u03CD\u03B3\u03C9\u03BD\u03BF\u03C5 +reg_poly_dlg=\u0391\u03C1\u03B9\u03B8\u03BC\u03CC\u03C2 \u03BA\u03BF\u03C1\u03C5\u03C6\u03CE\u03BD / \u03C0\u03BB\u03B5\u03C5\u03C1\u03CE\u03BD +remove=\u039A\u03B1\u03C4\u03AC\u03C1\u03B3\u03B7\u03C3\u03B7 +rewind=\u0395\u03C0\u03B1\u03BD\u03B1\u03C6\u03BF\u03C1\u03AC +rewind_des=\u0395\u03C0\u03B1\u03BD\u03B1\u03C6\u03BF\u03C1\u03AC +rotation=\u03A0\u03B5\u03C1\u03B9\u03C3\u03C4\u03C1\u03BF\u03C6\u03AE +rotation_des=\u03A0\u03B5\u03C1\u03B9\u03C3\u03C4\u03C1\u03BF\u03C6\u03AE \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +ruler=\u03A7\u03AC\u03C1\u03B1\u03BA\u03B1\u03C2 +ruler_des=\u039A\u03B1\u03C4\u03B1\u03B3\u03C1\u03B1\u03C6\u03AE \u03B1\u03C0\u03CC\u03C3\u03C4\u03B1\u03C3\u03B7\u03C2 +ruler_prot=\u03A7\u03AC\u03C1\u03B1\u03BA\u03B1\u03C2 & \u039C\u03BF\u03B9\u03C1\u03BF\u03B3\u03BD\u03C9\u03BC\u03CC\u03BD\u03B9\u03BF +save=\u0391\u03C0\u03BF\u03B8\u03AE\u03BA\u03B5\u03C5\u03C3\u03B7 +save_as=\u0391\u03C0\u03BF\u03B8\u03AE\u03BA\u03B5\u03C5\u03C3\u03B7 \u03C9\u03C2... +save_as_des=\u0391\u03C0\u03BF\u03B8\u03AE\u03BA\u03B5\u03C5\u03C3\u03B7 \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7\u03C2 \u03C9\u03C2 +save_close=\u0391\u03C0\u03BF\u03B8\u03AE\u03BA\u03B5\u03C5\u03C3\u03B7 & \u039A\u03BB\u03B5\u03AF\u03C3\u03B9\u03BC\u03BF +save_des=\u0391\u03C0\u03BF\u03B8\u03AE\u03BA\u03B5\u03C5\u03C3\u03B7 \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7\u03C2 +save_error1=\u03A3\u03C6\u03AC\u03BB\u03BC\u03B1 \u03BA\u03B1\u03C4\u03AC \u03C4\u03B7\u03BD \u03B1\u03C0\u03BF\u03B8\u03AE\u03BA\u03B5\u03C5\u03C3\u03B7 +save_error2=\u039A\u03B5\u03BD\u03AE \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7 +save_image=\u0391\u03C0\u03BF\u03B8\u03AE\u03BA\u03B5\u03C5\u03C3\u03B7 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B1\u03C2... +save_image_acce1=\u03A1\u03C5\u03B8\u03BC\u03AF\u03C3\u03B5\u03B9\u03C2 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B1\u03C2 +save_image_acce2=\u03A3\u03C5\u03BC\u03C0\u03B5\u03C1\u03AF\u03BB\u03B7\u03C8\u03B7 \u03C6\u03CC\u03BD\u03C4\u03BF\u03C5 +save_image_acce3=\u03A3\u03C5\u03BC\u03C0\u03B5\u03C1. \u03BF\u03BD\u03BF\u03BC\u03AC\u03C4\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +save_image_des=\u0395\u03BE\u03B1\u03B3\u03C9\u03B3\u03AE \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7\u03C2 \u03C9\u03C2 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B1\u03C2 SVG, PNG +segment=\u03A4\u03BC\u03AE\u03BC\u03B1 +segment_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +sel_2_error=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03B4\u03CD\u03BF \u03AE \u03C0\u03B5\u03C1\u03B9\u03C3\u03C3\u03CC\u03C4\u03B5\u03C1\u03B1 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03B1 +select_all=\u0395\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03CC\u03BB\u03C9\u03BD +select_all_des=\u0395\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03CC\u03BB\u03C9\u03BD +selected_shapes_msg=\u03A0\u03C1\u03AD\u03C0\u03B5\u03B9 \u03BD\u03B1 \u03B5\u03C0\u03B9\u03BB\u03AD\u03BE\u03B5\u03C4\u03B5 \u03C4\u03BF\u03C5\u03BB\u03AC\u03C7\u03B9\u03C3\u03C4\u03BF\u03BD \u03AD\u03BD\u03B1 \u03C3\u03C7\u03AE\u03BC\u03B1 +selection=\u039F\u03C1\u03B8\u03BF\u03B3\u03CE\u03BD\u03B9\u03B1 \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE +selection_des=\u0395\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD \u03C3\u03B5 \u03BF\u03C1\u03B8\u03BF\u03B3\u03CE\u03BD\u03B9\u03B1 \u03C0\u03B5\u03C1\u03B9\u03BF\u03C7\u03AE +shape_desc=\u03A0\u03B5\u03C1\u03B9\u03B3\u03C1\u03B1\u03C6\u03AE \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +shape_name=\u0391\u03BD\u03B1\u03B3\u03BD\u03C9\u03C1\u03B9\u03C3\u03C4\u03B9\u03BA\u03CC \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +shape_prop=\u0399\u03B4\u03B9\u03CC\u03C4\u03B7\u03C4\u03B5\u03C2 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +shapes=\u03A3\u03C7\u03AE\u03BC\u03B1\u03C4\u03B1 +show_all=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7 \u03CC\u03BB\u03C9\u03BD +spline=\u039A\u03B1\u03BC\u03C0\u03CD\u03BB\u03B7 +spline_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03BA\u03B1\u03BC\u03C0\u03CD\u03BB\u03B7\u03C2 \u03BC\u03B5\u03C4\u03B1\u03BE\u03CD \u03C3\u03B7\u03BC\u03B5\u03AF\u03C9\u03BD +start_frame=\u039A\u03B1\u03C1\u03AD \u03B5\u03BA\u03BA\u03AF\u03BD\u03B7\u03C3\u03B7\u03C2 +style=\u03A3\u03C4\u03C5\u03BB +text=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7/\u0391\u03C0\u03CC\u03BA\u03C1\u03C5\u03C8\u03B7 \u03BF\u03BD\u03BF\u03BC\u03AC\u03C4\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +text_des=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7/\u0391\u03C0\u03CC\u03BA\u03C1\u03C5\u03C8\u03B7 \u03BF\u03BD\u03BF\u03BC\u03AC\u03C4\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +thickness=

\u03A0\u03AC\u03C7\u03BF\u03C2

\u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 +tip_swap_color=\u0391\u03BD\u03C4\u03B9\u03BC\u03B5\u03C4\u03AC\u03B8\u03B5\u03C3\u03B7 \u03C7\u03C1\u03C9\u03BC\u03AC\u03C4\u03C9\u03BD +tip_undo_redo=\u0399\u03C3\u03C4\u03BF\u03C1\u03B9\u03BA\u03CC \u03B1\u03BD\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7\u03C2/\u03B5\u03C0\u03B1\u03BD\u03AC\u03BB\u03B7\u03C8\u03B7\u03C2 +tit_style=\u03A3\u03C4\u03C5\u03BB \u0393\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 / \u03A3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 +title=\u03A4\u03AF\u03C4\u03BB\u03BF\u03C2 +toast_copy=\u03C3\u03C7\u03AE\u03BC\u03B1/\u03C4\u03B1 \u03B1\u03BD\u03C4\u03B9\u03B3\u03C1\u03AC\u03C6\u03B7\u03BA\u03B1\u03BD +toast_no_copy=\u03B4\u03B5\u03BD \u03C5\u03C0\u03AC\u03C1\u03C7\u03B5\u03B9 \u03C4\u03AF\u03C0\u03BF\u03C4\u03B1 \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03BF +toast_paste=\u03C3\u03C7\u03AE\u03BC\u03B1/\u03C4\u03B1 \u03B5\u03C0\u03B9\u03BA\u03BF\u03BB\u03BB\u03AE\u03B8\u03B7\u03BA\u03B1\u03BD +tools=\u0395\u03C1\u03B3\u03B1\u03BB\u03B5\u03AF\u03B1 +transforms=\u039C\u03B5\u03C4\u03B1\u03C3\u03C7\u03B7\u03BC\u03B1\u03C4\u03B9\u03C3\u03BC\u03BF\u03AF +translation=\u039C\u03B5\u03C4\u03B1\u03BA\u03AF\u03BD\u03B7\u03C3\u03B7 +translation_des=\u039C\u03B5\u03C4\u03B1\u03BA\u03AF\u03BD\u03B7\u03C3\u03B7 \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +triangle=\u03A4\u03C1\u03AF\u03B3\u03C9\u03BD\u03BF +triangle_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C4\u03C1\u03B9\u03B3\u03CE\u03BD\u03BF\u03C5 +triangle_tools=\u0395\u03C1\u03B3\u03B1\u03BB\u03B5\u03AF\u03B1 \u03C4\u03C1\u03B9\u03B3\u03CE\u03BD\u03BF\u03C5 +txt_angle1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03BA\u03BF\u03C1\u03C5\u03C6\u03AE +txt_angle2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03B5\u03BB\u03B5\u03C5\u03C4\u03B1\u03AF\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03B7\u03C2 \u03C0\u03C1\u03CE\u03C4\u03B7\u03C2 \u03C0\u03BB\u03B5\u03C5\u03C1\u03AC\u03C2 +txt_angle3=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03B5\u03BB\u03B5\u03C5\u03C4\u03B1\u03AF\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03B7\u03C2 \u03B4\u03B5\u03CD\u03C4\u03B5\u03C1\u03B7\u03C2 \u03C0\u03BB\u03B5\u03C5\u03C1\u03AC\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C3\u03C5\u03BC\u03C0\u03BB\u03B7\u03C1\u03C9\u03BC\u03B1\u03C4\u03B9\u03BA\u03AE \u03B3\u03C9\u03BD\u03AF\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03C0\u03B1\u03C1\u03B1\u03C0\u03BB\u03B7\u03C1\u03C9\u03BC\u03B1\u03C4\u03B9\u03BA\u03AE \u03B3\u03C9\u03BD\u03AF\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Alt \u03B3\u03B9\u03B1 \u03C0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03BA\u03B1\u03B9 \u03C4\u03C9\u03BD \u03B4\u03CD\u03BF \u03C0\u03BB\u03B5\u03C5\u03C1\u03CE\u03BD +txt_arc1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03C4\u03BF\u03C5 \u03C4\u03CC\u03BE\u03BF\u03C5 +txt_arc2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03B7\u03BD \u03B1\u03C1\u03C7\u03AE \u03C4\u03B7\u03C2 \u03B3\u03C9\u03BD\u03AF\u03B1\u03C2 +txt_arc3=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03AD\u03BB\u03BF\u03C2 \u03C4\u03B7\u03C2 \u03B3\u03C9\u03BD\u03AF\u03B1\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03B5\u03BD\u03B1\u03BB\u03BB\u03B1\u03B3\u03AE \u03BA\u03C5\u03C1\u03C4\u03AE\u03C2/\u03BC\u03B7 \u03BA\u03C5\u03C1\u03C4\u03AE\u03C2 \u03B3\u03C9\u03BD\u03AF\u03B1\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C3\u03C4\u03B1\u03B8\u03B5\u03C1\u03AE \u03B3\u03C9\u03BD\u03AF\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Alt Gr \u03B3\u03B9\u03B1 \u03B1\u03BA\u03AD\u03C1\u03B1\u03B9\u03B5\u03C2 \u03B3\u03C9\u03BD\u03AF\u03B5\u03C2 +txt_area_substract1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C0\u03C1\u03CE\u03C4\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 +txt_area_substract2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B4\u03B5\u03CD\u03C4\u03B5\u03C1\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 +txt_arrow1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B1\u03C1\u03C7\u03B9\u03BA\u03CC \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 \u03B2\u03AD\u03BB\u03BF\u03C5\u03C2 +txt_arrow2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03B5\u03BB\u03B9\u03BA\u03CC \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 \u03B2\u03AD\u03BB\u03BF\u03C5\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C3\u03C4\u03B1\u03B8\u03B5\u03C1\u03AD\u03C2 \u03B3\u03C9\u03BD\u03AF\u03B5\u03C2 45\u00BA. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03B4\u03B9\u03C0\u03BB\u03CC \u03B2\u03AD\u03BB\u03BF\u03C2 +txt_axial_sym1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C0\u03C1\u03CE\u03C4\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 \u03AC\u03BE\u03BF\u03BD\u03B1 +txt_axial_sym2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B4\u03B5\u03CD\u03C4\u03B5\u03C1\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 \u03AC\u03BE\u03BF\u03BD\u03B1 +txt_bisectrix1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C0\u03C1\u03CE\u03C4\u03BF \u03C4\u03BC\u03AE\u03BC\u03B1 +txt_bisectrix2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B4\u03B5\u03CD\u03C4\u03B5\u03C1\u03BF \u03C4\u03BC\u03AE\u03BC\u03B1 +txt_bisectrix3=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C0\u03C1\u03CE\u03C4\u03BF \u03AC\u03BA\u03C1\u03BF \u03C4\u03B7\u03C2 \u03B4\u03B9\u03C7\u03BF\u03C4\u03CC\u03BC\u03BF\u03C5 +txt_bisectrix4=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B4\u03B5\u03CD\u03C4\u03B5\u03C1\u03BF \u03AC\u03BA\u03C1\u03BF \u03C4\u03B7\u03C2 \u03B4\u03B9\u03C7\u03BF\u03C4\u03CC\u03BC\u03BF\u03C5 +txt_bounds1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 \u03C0\u03BF\u03C5 \u03B8\u03B1 \u03C0\u03C1\u03BF\u03C3\u03C4\u03B5\u03B8\u03BF\u03CD\u03BD \u03BF\u03C1\u03B8\u03BF\u03B3\u03CE\u03BD\u03B9\u03B1 \u03CC\u03C1\u03B9\u03B1 +txt_cap1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BC\u03AE\u03BC\u03B1 +txt_cap2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03BC\u03B9\u03B1 \u03B1\u03C0\u03CC \u03C4\u03B9\u03C2 \u03B4\u03CD\u03BF \u03C0\u03BB\u03B5\u03C5\u03C1\u03AD\u03C2 +txt_central_sym1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03C3\u03C5\u03BC\u03BC\u03B5\u03C4\u03C1\u03AF\u03B1\u03C2 +txt_circ1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03C4\u03B7\u03C2 \u03C0\u03B5\u03C1\u03B9\u03C6\u03AD\u03C1\u03B5\u03B9\u03B1\u03C2 \u03BA\u03CD\u03BA\u03BB\u03BF\u03C5. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03B5\u03BD\u03B1\u03BB\u03BB\u03B1\u03B3\u03AE \u03B1\u03BA\u03C4\u03AF\u03BD\u03B1\u03C2 / \u03B4\u03B9\u03B1\u03BC\u03AD\u03C4\u03C1\u03BF\u03C5 +txt_circ2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03B1\u03BA\u03C4\u03AF\u03BD\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03B5\u03BD\u03B1\u03BB\u03BB\u03B1\u03B3\u03AE \u03B1\u03BA\u03C4\u03AF\u03BD\u03B1\u03C2 / \u03B4\u03B9\u03B1\u03BC\u03AD\u03C4\u03C1\u03BF\u03C5. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03C0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF\u03C5 +txt_comment1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B1\u03C1\u03C7\u03B9\u03BA\u03CC \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 \u03C0\u03BB\u03B1\u03B9\u03C3\u03AF\u03BF\u03C5 \u03BA\u03B5\u03B9\u03BC\u03AD\u03BD\u03BF\u03C5 +txt_div1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 \u03C0\u03BF\u03C5 \u03B8\u03B1 \u03C0\u03C1\u03BF\u03C3\u03C4\u03B5\u03B8\u03BF\u03CD\u03BD \u03C3\u03B7\u03BC\u03B5\u03AF\u03B1 \u03B4\u03B9\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7\u03C2. \u039C\u03C0\u03BF\u03C1\u03B5\u03AF\u03C4\u03B5 \u03BD\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03AE\u03C3\u03B5\u03C4\u03B5 \u03C4\u03B7\u03BD \u03A3\u03C4\u03B1\u03B8\u03B5\u03C1\u03AE \u03B1\u03C0\u03CC\u03C3\u03C4\u03B1\u03C3\u03B7 +txt_ellipse1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03AE \u03B3\u03C9\u03BD\u03AF\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03B5\u03BD\u03B1\u03BB\u03BB\u03B1\u03BA\u03C4\u03B9\u03BA\u03AE \u03BB\u03B5\u03B9\u03C4\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF\u03C5 / \u03B3\u03C9\u03BD\u03AF\u03B1\u03C2 +txt_ellipse2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03BC\u03AD\u03B3\u03B5\u03B8\u03BF\u03C2 \u03AD\u03BB\u03BB\u03B5\u03B9\u03C8\u03B7\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03C0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03AC\u03BE\u03BF\u03BD\u03B1 \u03BA\u03B1\u03B9 \u03B5\u03C3\u03C4\u03AF\u03B1\u03C3\u03B7\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03B5\u03BD\u03B1\u03BB\u03BB\u03B1\u03BA\u03C4\u03B9\u03BA\u03AE \u03BB\u03B5\u03B9\u03C4\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF\u03C5 / \u03B3\u03C9\u03BD\u03AF\u03B1\u03C2. \u039C\u03C0\u03BF\u03C1\u03B5\u03AF\u03C4\u03B5 \u03BD\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03AE\u03C3\u03B5\u03C4\u03B5 \u03C7\u03AC\u03C1\u03B1\u03BA\u03B1 +txt_ext1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 \u03C0\u03BF\u03C5 \u03B8\u03B1 \u03C0\u03C1\u03BF\u03C3\u03C4\u03B5\u03B8\u03BF\u03CD\u03BD \u03B1\u03BA\u03C1\u03B1\u03AF\u03B1 \u03C3\u03B7\u03BC\u03B5\u03AF\u03B1 (\u03C0\u03C1\u03AD\u03C0\u03B5\u03B9 \u03BD\u03B1 \u03B5\u03AF\u03BD\u03B1\u03B9 \u03B1\u03BD\u03BF\u03B9\u03C7\u03C4\u03CC \u03C3\u03C7\u03AE\u03BC\u03B1) +txt_eyedropper=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C3\u03C7\u03AE\u03BC\u03B1 \u03BB\u03AE\u03C8\u03B7 \u03C3\u03C4\u03C5\u03BB \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03BB\u03AE\u03C8\u03B7 \u03C3\u03C4\u03C5\u03BB \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 +txt_fragment1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 \u03B3\u03B9\u03B1 \u03BA\u03B1\u03C4\u03AC\u03C4\u03BC\u03B7\u03C3\u03B7 \u03C0\u03BB\u03B5\u03C5\u03C1\u03CE\u03BD. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C7\u03C1\u03AE\u03C3\u03B7 \u03C4\u03B7\u03C2 \u03C4\u03C1\u03AD\u03C7\u03BF\u03C5\u03C3\u03B1\u03C2 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 \u03B1\u03BD\u03C4\u03AF \u03C4\u03B7\u03C2 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 \u03C4\u03BF\u03C5 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +txt_free1=\u03A3\u03CD\u03C1\u03B5\u03C4\u03B5 \u03C4\u03BF \u03C0\u03BF\u03BD\u03C4\u03AF\u03BA\u03B9 \u03B3\u03B9\u03B1 \u03C3\u03C7\u03B5\u03B4\u03AF\u03B1\u03C3\u03B7 \u03B5\u03BB\u03B5\u03CD\u03B8\u03B5\u03C1\u03BF\u03C5 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2. \u0394\u03B9\u03C0\u03BB\u03CC \u03BA\u03BB\u03B9\u03BA \u03B3\u03B9\u03B1 \u03BF\u03BB\u03BF\u03BA\u03BB\u03AE\u03C1\u03C9\u03C3\u03B7 +txt_hand=\u03A7\u03C1\u03AE\u03C3\u03B7 \u03C4\u03BF\u03C5 \u03C4\u03C1\u03BF\u03C7\u03BF\u03CD \u03C0\u03BF\u03BD\u03C4\u03B9\u03BA\u03B9\u03BF\u03CD \u03B3\u03B9\u03B1 \u03BC\u03B5\u03B3\u03AD\u03B8\u03C5\u03BD\u03C3\u03B7. \u03A3\u03CD\u03C1\u03B5\u03C4\u03B5 \u03BC\u03B5 \u03B4\u03B5\u03BE\u03AF \u03BA\u03BB\u03B9\u03BA \u03B3\u03B9\u03B1 \u03BD\u03B1 \u03BC\u03B5\u03C4\u03B1\u03BA\u03B9\u03BD\u03AE\u03C3\u03B5\u03C4\u03B5 \u03C4\u03B1 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03B1. Shift \u03C0\u03AC\u03BD\u03C9 \u03B1\u03C0\u03CC \u03C3\u03C7\u03AE\u03BC\u03B1 \u03B3\u03B9\u03B1 \u03B1\u03C0\u03BF-\u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03C0\u03BF\u03BB\u03BB\u03CE\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD. \u03A3\u03CD\u03C1\u03B5\u03C4\u03B5 \u03B3\u03B9\u03B1 \u03BC\u03B5\u03C4\u03B1\u03BA\u03AF\u03BD\u03B7\u03C3\u03B7 \u03C4\u03BF\u03C5 \u03C0\u03B5\u03B4\u03AF\u03BF\u03C5 \u03C0\u03C1\u03BF\u03B2\u03BF\u03BB\u03AE\u03C2 +txt_homothety1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03C4\u03BF\u03C5 \u03C0\u03C1\u03BF\u03BF\u03C0\u03C4\u03B9\u03BA\u03BF\u03CD. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03B1\u03C1\u03BD\u03B7\u03C4\u03B9\u03BA\u03CC \u03C3\u03C5\u03BD\u03C4\u03B5\u03BB\u03B5\u03C3\u03C4\u03AE. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03B1\u03BD\u03B5\u03C3\u03C4\u03C1\u03B1\u03BC\u03BC\u03AD\u03BD\u03BF \u03C3\u03C5\u03BD\u03C4\u03B5\u03BB\u03B5\u03C3\u03C4\u03AE +txt_hyperbola1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B1\u03C1\u03C7\u03B9\u03BA\u03CC \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03C9\u03BD \u03BF\u03C1\u03AF\u03C9\u03BD \u03C4\u03B7\u03C2 \u03C5\u03C0\u03B5\u03C1\u03B2\u03BF\u03BB\u03AE\u03C2 +txt_hyperbola2=\u039A\u03B9\u03BD\u03AE\u03C3\u03C4\u03B5 \u03C4\u03BF \u03C0\u03BF\u03BD\u03C4\u03AF\u03BA\u03B9 \u03B3\u03B9\u03B1 \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03C4\u03BF\u03C5 \u03C4\u03B5\u03BB\u03B5\u03C5\u03C4\u03B1\u03AF\u03BF\u03C5 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5. \u039C\u03C0\u03BF\u03C1\u03B5\u03AF\u03C4\u03B5 \u03BD\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03AE\u03C3\u03B5\u03C4\u03B5 \u03C4\u03BF\u03BD \u03C7\u03AC\u03C1\u03B1\u03BA\u03B1 +txt_hyperbola3=\u039A\u03B9\u03BD\u03AE\u03C3\u03C4\u03B5 \u03C4\u03BF \u03C0\u03BF\u03BD\u03C4\u03AF\u03BA\u03B9 \u03B3\u03B9\u03B1 \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03C4\u03BF\u03C5 \u03B2\u03B1\u03C3\u03B9\u03BA\u03BF\u03CD \u03AC\u03BE\u03BF\u03BD\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control\u03B3\u03B9\u03B1 \u03C0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03B2\u03BF\u03B7\u03B8\u03B7\u03C4\u03B9\u03BA\u03CE\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +txt_jme1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03AD\u03BD\u03B1\u03C1\u03BE\u03B7\u03C2 \u03B3\u03B9\u03B1 \u03C4\u03B9\u03C2 \u03C3\u03C5\u03BD\u03C4\u03B5\u03C4\u03B1\u03B3\u03BC\u03AD\u03BD\u03B5\u03C2 +txt_jme2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03B5\u03BB\u03B9\u03BA\u03CC \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03B3\u03B9\u03B1 \u03C4\u03BF\u03BD \u03AC\u03BE\u03BF\u03BD\u03B1 x +txt_mediatrix1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BC\u03AE\u03BC\u03B1 +txt_mediatrix2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03B7\u03BD \u03B1\u03C1\u03C7\u03AE \u03C4\u03B7\u03C2 \u03BC\u03B5\u03C3\u03BF\u03BA\u03B1\u03B8\u03AD\u03C4\u03BF\u03C5 +txt_mediatrix3=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03AD\u03BB\u03BF\u03C2 \u03C4\u03B7\u03C2 \u03BC\u03B5\u03C3\u03BF\u03BA\u03B1\u03B8\u03AD\u03C4\u03BF\u03C5 +txt_midpoint=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 \u03C0\u03BF\u03C5 \u03B8\u03B1 \u03C0\u03C1\u03BF\u03C3\u03C4\u03B5\u03B8\u03B5\u03AF \u03C4\u03BF \u03BC\u03AD\u03C3\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03C9\u03BD \u03BF\u03C1\u03AF\u03C9\u03BD \u03C4\u03BF\u03C5 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C7\u03C1\u03AE\u03C3\u03B7 \u03C4\u03BF\u03C5 \u03C3\u03C4\u03C5\u03BB \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03BA\u03B5\u03BD\u03C4\u03C1\u03BF\u03B5\u03B9\u03B4\u03AD\u03C2 +txt_modify1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03BC\u03AE\u03BC\u03B1 \u03C0\u03C1\u03BF\u03C2 \u03C4\u03C1\u03BF\u03C0\u03BF\u03C0\u03BF\u03AF\u03B7\u03C3\u03B7 +txt_modify2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C0\u03C1\u03CE\u03C4\u03BF \u03AC\u03BA\u03C1\u03BF +txt_modify3=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B4\u03B5\u03CD\u03C4\u03B5\u03C1\u03BF \u03AC\u03BA\u03C1\u03BF. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C4\u03C1\u03AD\u03C7\u03BF\u03BD \u03C3\u03C4\u03C5\u03BB \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 \u03B1\u03BD\u03C4\u03AF \u03C4\u03BF\u03C5 \u03C3\u03C4\u03C5\u03BB \u03C4\u03BF\u03C5 \u03C0\u03B1\u03BB\u03B9\u03BF\u03CD \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +txt_paint1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C3\u03C7\u03AE\u03BC\u03B1 \u03B3\u03B9\u03B1 \u03B3\u03AD\u03BC\u03B9\u03C3\u03BC\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03B4\u03B9\u03B1\u03B3\u03C1\u03B1\u03C6\u03AE \u03B3\u03B5\u03BC\u03AF\u03C3\u03BC\u03B1\u03C4\u03BF\u03C2. Alt \u03B3\u03B9\u03B1 \u03C7\u03C1\u03CE\u03BC\u03B1 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2. Shift \u03B3\u03B9\u03B1 \u03C7\u03C1\u03CE\u03BC\u03B1 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 \u03B1\u03BD\u03C4\u03AF \u03B3\u03B9\u03B1 \u03C7\u03C1\u03CE\u03BC\u03B1 \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +txt_para1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BC\u03AE\u03BC\u03B1 +txt_para2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03B7\u03BD \u03B1\u03C1\u03C7\u03AE \u03C4\u03B7\u03C2 \u03C0\u03B1\u03C1\u03B1\u03BB\u03BB\u03AE\u03BB\u03BF\u03C5 +txt_para3=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03AD\u03BB\u03BF\u03C2 \u03C4\u03B7\u03C2 \u03C0\u03B1\u03C1\u03B1\u03BB\u03BB\u03AE\u03BB\u03BF\u03C5 +txt_parabola1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B1\u03C1\u03C7\u03B9\u03BA\u03CC \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03C9\u03BD \u03BF\u03C1\u03AF\u03C9\u03BD \u03C4\u03B7\u03C2 \u03C0\u03B1\u03C1\u03B1\u03B2\u03BF\u03BB\u03AE\u03C2 +txt_parabola2=\u039A\u03B9\u03BD\u03AE\u03C3\u03C4\u03B5 \u03C4\u03BF \u03C0\u03BF\u03BD\u03C4\u03AF\u03BA\u03B9 \u03B3\u03B9\u03B1 \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03C4\u03BF\u03C5 \u03C4\u03B5\u03BB\u03B5\u03C5\u03C4\u03B1\u03AF\u03BF\u03C5 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 \u03C4\u03C9\u03BD \u03BF\u03C1\u03AF\u03C9\u03BD \u03C4\u03B7\u03C2 \u03C0\u03B1\u03C1\u03B1\u03B2\u03BF\u03BB\u03AE\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03C0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03B5\u03C0\u03B9\u03C0\u03BB\u03AD\u03BF\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C4\u03B5\u03C4\u03C1\u03AC\u03B3\u03C9\u03BD\u03B1 \u03CC\u03C1\u03B9\u03B1. \u039C\u03C0\u03BF\u03C1\u03B5\u03AF\u03C4\u03B5 \u03BD\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03AE\u03C3\u03B5\u03C4\u03B5 \u03C7\u03AC\u03C1\u03B1\u03BA\u03B1 +txt_paste_style1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 \u03C0\u03BF\u03C5 \u03B8\u03B1 \u03B5\u03C6\u03B1\u03C1\u03BC\u03BF\u03C3\u03C4\u03B5\u03AF \u03C4\u03BF \u03C3\u03C4\u03C5\u03BB \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03BC\u03B7 \u03B5\u03C6\u03B1\u03C1\u03BC\u03BF\u03B3\u03AE \u03B3\u03B5\u03BC\u03AF\u03C3\u03BC\u03B1\u03C4\u03BF\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03B5\u03C6\u03B1\u03C1\u03BC\u03BF\u03B3\u03AE \u03C4\u03BF\u03C5 \u03C3\u03C4\u03C5\u03BB \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 +txt_perp1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BC\u03AE\u03BC\u03B1 +txt_perp2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03B7\u03BD \u03B1\u03C1\u03C7\u03AE \u03C4\u03B7\u03C2 \u03BA\u03B1\u03B8\u03AD\u03C4\u03BF\u03C5 +txt_perp3=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03AD\u03BB\u03BF\u03C2 \u03C4\u03B7\u03C2 \u03BA\u03B1\u03B8\u03AD\u03C4\u03BF\u03C5 +txt_point=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03B8\u03AD\u03C3\u03B7 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03BD\u03B1 \u03BC\u03B7\u03BD \u03C0\u03C1\u03BF\u03C3\u03B1\u03C1\u03BC\u03BF\u03C3\u03C4\u03B5\u03AF \u03C3\u03B5 \u03C4\u03BF\u03BC\u03AE. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C7\u03C1\u03AE\u03C3\u03B7 \u03C3\u03C4\u03C5\u03BB \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 \u03B1\u03BD\u03C4\u03AF \u03B3\u03B9\u03B1 \u03C3\u03C4\u03C5\u03BB \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 +txt_poly=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03B7\u03BD \u03BA\u03BF\u03C1\u03C5\u03C6\u03AE. \u0394\u03B9\u03C0\u03BB\u03CC \u03BA\u03BB\u03B9\u03BA \u03B3\u03B9\u03B1 \u03BF\u03BB\u03BF\u03BA\u03BB\u03AE\u03C1\u03C9\u03C3\u03B7 +txt_prot1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03BA\u03BF\u03C1\u03C5\u03C6\u03AE \u03B3\u03C9\u03BD\u03AF\u03B1\u03C2 +txt_prot2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C0\u03C1\u03CE\u03C4\u03B7\u03C2 \u03C0\u03BB\u03B5\u03C5\u03C1\u03AC\u03C2 +txt_prot3=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03B4\u03B5\u03CD\u03C4\u03B5\u03C1\u03B7\u03C2 \u03C0\u03BB\u03B5\u03C5\u03C1\u03AC\u03C2 +txt_rect1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B1\u03C1\u03C7\u03B9\u03BA\u03CC \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 \u03BF\u03C1\u03B8\u03BF\u03B3\u03C9\u03BD\u03AF\u03BF\u03C5 +txt_rect2=\u039C\u03B5\u03C4\u03B1\u03BA\u03B9\u03BD\u03AE\u03C3\u03C4\u03B5 \u03C4\u03BF \u03C0\u03BF\u03BD\u03C4\u03AF\u03BA\u03B9 \u03B3\u03B9\u03B1 \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03C4\u03BF\u03C5 \u03C4\u03B5\u03BB\u03B5\u03C5\u03C4\u03B1\u03AF\u03BF\u03C5 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 \u03C4\u03BF\u03C5 \u03BF\u03C1\u03B8\u03BF\u03B3\u03C9\u03BD\u03AF\u03BF\u03C5. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C4\u03B5\u03C4\u03C1\u03AC\u03B3\u03C9\u03BD\u03BF. \u039C\u03C0\u03BF\u03C1\u03B5\u03AF\u03C4\u03B5 \u03BD\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03AE\u03C3\u03B5\u03C4\u03B5 \u03C7\u03AC\u03C1\u03B1\u03BA\u03B1 +txt_reg_poly1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03C4\u03BF\u03C5 \u03C0\u03BF\u03BB\u03C5\u03B3\u03CE\u03BD\u03BF\u03C5 +txt_reg_poly2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03B1\u03BA\u03C4\u03AF\u03BD\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03C0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03B2\u03BF\u03B7\u03B8\u03B7\u03C4\u03B9\u03BA\u03CE\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD. \u039C\u03C0\u03BF\u03C1\u03B5\u03AF\u03C4\u03B5 \u03BD\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03AE\u03C3\u03B5\u03C4\u03B5 \u03C7\u03AC\u03C1\u03B1\u03BA\u03B1 +txt_rotation1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03C0\u03B5\u03C1\u03B9\u03C3\u03C4\u03C1\u03BF\u03C6\u03AE\u03C2 +txt_rotation2=\u039A\u03B9\u03BD\u03AE\u03C3\u03C4\u03B5 \u03C4\u03BF \u03C0\u03BF\u03BD\u03C4\u03AF\u03BA\u03B9 \u03B3\u03B9\u03B1 \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03B3\u03C9\u03BD\u03AF\u03B1\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C3\u03C4\u03B1\u03B8\u03B5\u03C1\u03AE \u03B3\u03C9\u03BD\u03AF\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03B1\u03BA\u03AD\u03C1\u03B1\u03B9\u03B5\u03C2 \u03B3\u03C9\u03BD\u03AF\u03B5\u03C2 +txt_ruler1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B1\u03C1\u03C7\u03B9\u03BA\u03CC \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF +txt_ruler2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03B5\u03BB\u03B5\u03C5\u03C4\u03B1\u03AF\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03C3\u03C4\u03B7\u03BD \u03C4\u03C1\u03AD\u03C7\u03BF\u03C5\u03C3\u03B1 \u03B1\u03C0\u03CC\u03C3\u03C4\u03B1\u03C3\u03B7. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03B1\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 +txt_seg1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B1\u03C1\u03C7\u03B9\u03BA\u03CC \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +txt_seg2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03B5\u03BB\u03B9\u03BA\u03CC \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C3\u03C4\u03B1\u03B8\u03B5\u03C1\u03AD\u03C2 \u03B3\u03C9\u03BD\u03AF\u03B5\u03C2 45\u00BA. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03C0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03AC\u03BA\u03C1\u03C9\u03BD +txt_sel1=\u039C\u03B5\u03C4\u03B1\u03BA\u03B9\u03BD\u03AE\u03C3\u03C4\u03B5 \u03C4\u03BF \u03C0\u03BF\u03BD\u03C4\u03AF\u03BA\u03B9 \u03B3\u03B9\u03B1 \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03BC\u03B9\u03B1\u03C2 \u03BF\u03C1\u03B8\u03BF\u03B3\u03CE\u03BD\u03B9\u03B1\u03C2 \u03C0\u03B5\u03C1\u03B9\u03BF\u03C7\u03AE\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5Control \u03B3\u03B9\u03B1 \u03B5\u03C0\u03B9\u03B2\u03BF\u03BB\u03AE \u03CC\u03BB\u03BF\u03C5 \u03C4\u03BF\u03C5 \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03BF\u03C5 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +txt_translate1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C0\u03C1\u03CE\u03C4\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 \u03B4\u03B9\u03B1\u03BD\u03CD\u03C3\u03BC\u03B1\u03C4\u03BF\u03C2 +txt_translate2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B4\u03B5\u03CD\u03C4\u03B5\u03C1\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 \u03B4\u03B9\u03B1\u03BD\u03CD\u03C3\u03BC\u03B1\u03C4\u03BF\u03C2. \u039C\u03C0\u03BF\u03C1\u03B5\u03AF\u03C4\u03B5 \u03BD\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03AE\u03C3\u03B5\u03C4\u03B5 \u03C4\u03BF\u03BD \u03C7\u03AC\u03C1\u03B1\u03BA\u03B1 +txt_triangle_points1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03C1\u03AF\u03B1 \u03C3\u03B7\u03BC\u03B5\u03AF\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03B9\u03C3\u03CC\u03C0\u03BB\u03B5\u03C5\u03C1\u03BF. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03BF\u03C1\u03B8\u03BF\u03B3\u03CE\u03BD\u03B9\u03BF +txt_triangle_points2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03C1\u03AF\u03B1 \u03C3\u03B7\u03BC\u03B5\u03AF\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C3\u03C4\u03C5\u03BB \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2. +txt_vtx1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 \u03C0\u03BF\u03C5 \u03B8\u03B1 \u03C0\u03C1\u03BF\u03C3\u03C4\u03B5\u03B8\u03BF\u03CD\u03BD \u03BA\u03BF\u03C1\u03C5\u03C6\u03AD\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C3\u03C4\u03C5\u03BB \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2. +undo=\u0391\u03BD\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 +vertex=\u039A\u03BF\u03C1\u03C5\u03C6\u03AE +vertex_des=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03C3\u03B7\u03BC\u03B5\u03AF\u03C9\u03BD \u03BA\u03BF\u03C1\u03C5\u03C6\u03AE\u03C2 \u03C3\u03B5 \u03C3\u03C7\u03AE\u03BC\u03B1 +view=\u03A0\u03C1\u03BF\u03B2\u03BF\u03BB\u03AE +width=\u03A0\u03BB\u03AC\u03C4\u03BF\u03C2 +zoom_all=\u039C\u03B5\u03B3\u03AD\u03B8\u03C5\u03BD\u03C3\u03B7 \u03CC\u03BB\u03C9\u03BD +zoom_all_des=\u03A0\u03C1\u03BF\u03C3\u03B1\u03C1\u03BC\u03BF\u03B3\u03AE \u03C0\u03B5\u03C1\u03B9\u03B5\u03C7\u03BF\u03BC\u03AD\u03BD\u03BF\u03C5 \u03C3\u03C4\u03BF\u03BD \u03BA\u03B1\u03BC\u03B2\u03AC +zoom_in=\u039C\u03B5\u03B3\u03AD\u03B8\u03C5\u03BD\u03C3\u03B7 +zoom_in_des=\u039C\u03B5\u03B3\u03AD\u03B8\u03C5\u03BD\u03C3\u03B7 +zoom_out=\u03A3\u03BC\u03AF\u03BA\u03C1\u03C5\u03BD\u03C3\u03B7 +zoom_out_des=\u03A3\u03BC\u03AF\u03BA\u03C1\u03C5\u03BD\u03C3\u03B7 diff --git a/bin/jdrafting/resources/language/language_en.properties b/bin/jdrafting/resources/language/language_en.properties new file mode 100644 index 0000000..8409af2 --- /dev/null +++ b/bin/jdrafting/resources/language/language_en.properties @@ -0,0 +1,480 @@ +# general +app_des=is an open-source drafting application to design small Technical Drawing sketches.
This application is inspired by the 'Classical Construction',
also known as 'Ruler-and-compass Construction' or 'Compass-and-straightedge construction'.
However, another drawing tools can be used.
(More info about classical construction: Wikipedia) +width=Width +height=Height +degrees=deg +not_saved=Not saved +cancel=Cancel +add=Add +remove=Remove + +# labels +thickness=

Line

thickness +point_thickness=

Point

thickness +lbl_fill=Fill +lbl_no_fill=No fill +lbl_line_color=Line color +lbl_point_color=Point color +lbl_fill_color=Fill color + +# actions +new=New exercise +new_des=Start a new exercise +open=Open... +open_des=Open an exercise +save=Save +save_des=Save exercise +save_as=Save as... +save_as_des=Save exercise as +save_image=Save image... +save_image_des=Export exercise as image SVG, PNG +print=Print... +print_des=Print in selected printer +fileinfo=Exercise info +fileinfo_des=Get or modify exercise info +redo=Redo +undo=Undo +move_up=Up shapes +move_up_des=Increase selected shapes index +move_down=Down shapes +move_down_des=Decrease selected shapes index +copy=Copy selected +copy_des=Copy selected shapes +exit=Exit +exit_des=Quit program +about=About... +zoom_all=Zoom all +zoom_all_des=Adjust content to canvas +zoom_in=Zoom in +zoom_in_des=Zoom in +zoom_out=Zoom out +zoom_out_des=Zoom out +delete=Delete selected +delete_des=Delete selected shapes +background_color=Canvas color... +background_color_des=Canvas background color +color=Shape color... +color_des=Shape color +point_color=Point color... +point_color_des=Point color +eyedropper=Eyedropper +eyedropper_des=Capture shape style +paste_style=Paste style +paste_style_des=Paste current style color and stroke +text=See/Hide shape names +text_des=See/Hide shape names +backward=Previous frame +backward_des=Previous frame +forward=Next frame +forward_des=Next frame +end=End frame +end_des=End frame +rewind=Rewind +rewind_des=Rewind +selection=Rectangular selection +selection_des=Select shapes in a rectangular area +invert=Invert selection +invert_des=Invert selection +select_all=Select all +select_all_des=Select all +point=Point +point_des=Create a new point +segment=Segment +segment_des=Create segment +arrow=Arrow +arrow_des=Create single or double arrow +polygon_tools=Polygon tools +reg_poly=Regular polygon +reg_poly_des=Create regular polygon +polygon=Polygon +polygon_des=Create polygon +polyline=Polyline +polyline_des=Create polyline +free_hand=Free hand +free_hand_des=Create an arbitrary shape +rectangle=Rectangle +rectangle_des=Create rectangle +ellipse=Ellipse +ellipse_des=Create ellipse +arc=Arc +arc_des=Create arc +circumference=Circumference +circumference_des=Create circumference +angle=Angle +angle_des=Create a segment by angle +triangle_tools=Triangle tools +triangle=Triangle +triangle_des=Create triangle +incenter=Incenter +ortocenter=Ortocenter +baricenter=Baricenter +circumcenter=Circumcenter +comment=Comment +comment_des=Add comment +spline=Spline +spline_des=Create spline between points +parabola=Parabola +parabola_des=Create parabola from rectangle bounds +hyperbola=Hyperbola +hyperbola_des=Create hyperbola from rectangle bounds +func_des=Create graph from function +func=Function +ruler=Ruler +ruler_des=Capture distance +protractor=Protractor +protractor_des=Capture angle +divisions=Division points +divisions_des=Divide shape in points +modify=Modify segment +modify_des=Modify a segment along its container straight +midpoint=Midpoint +midpoint_des=Set midpoint in the center of the shape bounds +centroid=Centroid +vertex=Vertex +vertex_des=Add vertex points to shape +extremes=Extremes +extremes_des=Add extreme vertex to open shapes +inter=Intersection points +inter_des=Select two or more shapes (none points) to add intersections between them +fragment=Fragment shape sides +fragment_des=Fragment shape sides into individual segments +fusion=Fusion shapes +fusion_des=Select shapes to be merged. Shift for connect extremes +bounds=Rectangle bounds +bounds_des=Create rectangle bounds of a shape +perp=Perpendicular +perp_des=Create perpendicular from segment +para=Parallel +para_des=Create parallel from segment +mediatrix=Mediatrix +mediatrix_des=Create mediatrix of a segment +bisectrix=Bisectrix +bisectrix_des=Create bisectrix +capable_arc=Capable arc +capable_arc_des=Create capable arc of a segment +translation=Translate +translation_des=Translate selected shapes +rotation=Rotate +rotation_des=Rotate selected shapes +homothety=Homothety +homothety_des=Homothety of the selected shapes +central_sym=Central symmetry +central_sym_des=Central symmetry of the selected shapes +axial_sym=Axial symmetry +axial_sym_des=Axial symmetry of the selected shapes +paint=Paint +paint_des=Paint shape +fix_dist=Fix
distance +paste=Paste +paste_des=Paste selected shapes +fill=Fill color +fill_des=Fill color of the shapes +area_intersection=Shape intersection +area_intersection_des=Shape from selected intersected shapes +area_union=Shape union +area_union_des=Shape from union of the selected shapes +area_substract=Shape difference +area_substract_des=Shape from the difference of two shapes +area_sym_diff=Symmetric difference +area_sym_diff_des=Symmetric difference of the selected shapes + +# menus +file=File +edit=Edit +style=Style +shapes=Shapes +exercise=Exercise +transforms=Transforms +conics=Conics +view=View +appearance=Appearance +help=Help + +item_style=Show/hide style bar +item_ruler_prot=Show/hide ruler&protractor bar +item_status=Show/hide status bar +item_action=Show/hide action bar +item_shape=Show/hide shape bar +item_tool=Show/hide tool bar +show_all=Show all +hide_all=Hide all + +# toolbars +tools=Tools +actions=Actions +tit_style=Line/Point style +ruler_prot=Ruler and Protractor + +# dialogs +exit_msg=Ignore changes? +exit_dlg=Exit +inter_msg=Select two or more shapes (none points) +inter_dlg=Intersection points error +ext_dlg=The shape must be an open shape +ext_title=Error while selecting shape +bisectrix_dlg=Parallel or coincident segments +bisectrix_title=Error while calculating bisectrix +div_dlg=Number of divisions +reg_poly_dlg=Number of vertex/sides +save_image_acce1=Image setting +save_image_acce2=Include background +save_image_acce3=Include shape names +overwrite1=File exists, overwrite? +overwrite2=Overwrite +exer_prop=Exercise properties +title=Title +exer_desc=Exercise description +start_frame=Start frame +shape_name=Shape identifier +shape_prop=Shape properties +shape_desc=Shape description +save_close=Save and Close +details=Details +selected_shapes_msg=You must select at least one shape +homo_dlg=Homothety factor +dist_mult_dlg=Multiply distance by factor +jme_dlg=JME expression +jme_examples=examples +jme_doc=JME documentation +jme_min=Min 'x' or 't' value +jme_max=Max 'x' or 't' value +jme_intervals=Number of intervals +save_error1=Error when saving +save_error2=Empty exercise +sel_2_error=Select two o more shapes +inter_error=No joins +empty_intersection_error=Empty intersection +empty_union_error=Empty union +empty_substract_error=Empty difference +empty_sym_diff_error=Empty symmetric difference + +# new shapes default description +new_point=point +new_segment=segment +new_segment_extreme=segment extreme +new_arc=arc +new_circumference=circumference +new_circumference_center=circumference center +new_arrow=arrow +new_triangle=triangle +new_rectangle=rectangle +new_square=square +new_ellipse=ellipse +new_regular_polygon=regular polygon +new_polygon=polygon +new_polyline=polyline +new_v_axis=vertical axis +new_h_axis=horizontal axis +new_parabola=parabola +new_spline=spline +new_free_hand=free hand +new_midpoint=> midpoint of +new_centroid=> centroid of +new_vertex=> vertex of +new_extreme=> extreme of +new_parallel=> parallel to +new_perpendicular=> perpendicular to +new_mediatrix=> mediatrix of +new_bisectrix=> bisectrix of +new_capable_arc=> capable arc of +new_fragment=> fragment of +new_div=> division point of +new_join=> intersection point between +new_bounds=> bounds of +new_fusion=> fusion of +new_focus=focus +new_para_vertex=parabola vertex +new_para_bounds=parabola bounds +new_directrix=directrix +new_hype_branch=hyperbola branch +new_hype_vertex=hyperbola vertex +new_hype_bounds=hyperbola bounds +new_hype_center=hyperbola center +new_main_axis=hyperbola main axis +new_img_axis=hyperbola imaginary axis +new_circumscribed=circumscribed circumference +new_center_reg=regular polygon center +new_intersection=> intersection of +new_union=> union of +new_substract=> substract of +new_sym_diff=> symmetric difference of + +# status msg +txt_hand=Use mouse wheel to zoom. Mouse dragging with second buttom to move shapes. Shift over shape for select/unselect several shapes. Drag to move viewport +txt_eyedropper=Select shape to capture line style. Shift for capture point style. Control to capture color on screen +txt_paste_style1=Select shape to apply line style. Control for don't apply fill. Shift to apply point style +txt_ruler1=Select start point +txt_ruler2=Select end point. Shift for add to current distance. Control for substract +txt_prot1=Select vertex angle +txt_prot2=Select first side point +txt_prot3=Select second side point +txt_sel1=Drag mouse to select a rectangular area. Control to force full shape selection +txt_point=Select point position. Control for non-intersection adjusting. Shift to use line style instead of point style +txt_seg1=Select segment start point +txt_seg2=Select segment end point. Shift for 45º fixed angles. Control to add extremes +txt_arrow1=Select arrow start point +txt_arrow2=Select arrow end point. Shift for 45º fixed angles. Control for double arrow +txt_circ1=Select circumference center. Shift for switch radius/diameter +txt_circ2=Select radius. Shift for switch radius/diameter. Control to add center +txt_arc1=Select arc center +txt_arc2=Select start angle +txt_arc3=Select end angle. Control for conjugate angle. Shift for fixed angle. Alt Gr for integer angles +txt_angle1=Select vertex +txt_angle2=Select end point of the first side +txt_angle3=Select end point of the second side. Shift for suplementary angle. Control for complementary angle. Alt for include both sides +txt_rect1=Select rectangle start point +txt_rect2=Move mouse to select rectangle end point. Shift for square. You can use ruler +txt_ellipse1=Select center or corner. Shift for alternate center/corner mode +txt_ellipse2=Select ellipse size. Control to add axis and focuses. Shift for alternate center/corner mode. You can use ruler +txt_poly=Select vertex. Double click to finish +txt_free1=Drag mouse to draw free shape. Double click to finish +txt_modify1=Select segment to modify +txt_modify2=Select first extreme +txt_modify3=Select second extreme. Shift for current line style instead of old segment style +txt_midpoint=Select shape to add midpoint of the shape bounds. Shift to use line style. Control for centroid +txt_vtx1=Select shape to add vertex. Shift for line style +txt_ext1=Select shape to add extreme vertex (must be an open shape) +txt_div1=Select shape to add divisions points. You can use Fix distance +txt_fragment1=Select shape to fragment sides. Press Shift for use current stroke instead of shape stroke +txt_bounds1=Select shape to add rectangle bounds +txt_perp1=Select segment +txt_perp2=Select perpendicular start +txt_perp3=Select perpendicular end +txt_para1=Select segment +txt_para2=Select parallel start +txt_para3=Select parallel end +txt_mediatrix1=Select segment +txt_mediatrix2=Select mediatrix start +txt_mediatrix3=Select mediatrix end +txt_bisectrix1=Select first segment +txt_bisectrix2=Select second segment +txt_bisectrix3=Select first bisectrix extreme +txt_bisectrix4=Select second bisectrix extreme +txt_cap1=Select segment +txt_cap2=Select one of the two sides +txt_translate1=Select first point of direction vector +txt_translate2=Select second point of direction vector. You can use ruler +txt_rotation1=Select rotation center +txt_rotation2=Move mouse to selectangle. Shift for fixed angle. Control for integer angles +txt_homothety1=Select homothety center. Shift for negative factor. Control for inverted factor +txt_central_sym1=Select symmetry center +txt_axial_sym1=Select first point of axis +txt_axial_sym2=Select second point of axis +txt_triangle_points1=Select three points. Shift for equilateral. Control for right +txt_triangle_points2=Select three points. Shift for line style +txt_parabola1=Select parabola bounds start point +txt_parabola2=Move mouse to select parabola bounds end point. Control to add additional shapes. Shift for square bounds. You can use ruler +txt_hyperbola1=Select hyperbola bounds start point +txt_hyperbola2=Move mouse to select end point. You can use the ruler +txt_hyperbola3=Move mouse to select main axis. Control to add auxiliary shapes +txt_reg_poly1=Select center of the polygon +txt_reg_poly2=Select radius. Control to add auxiliary shapes. You can use ruler +txt_paint1=Select shape to fill. Control for delete fill. Alt for line color. Shift for point color +txt_comment1=Select start point of the text box +txt_area_substract1=Select first shape +txt_area_substract2=Select second shape +txt_jme1=Select start point for coordinates origin +txt_jme2=Select end point for x axis + +# mnemonics +mne_menu_file=f +mne_menu_edit=e +mne_menu_style=s +mne_menu_shapes=h +mne_menu_tools=t +mne_menu_exercise=x +mne_menu_view=v +mne_menu_help=l +mne_menu_transform=t +mne_menu_trian=t +mne_menu_poly=o +mne_menu_conics=i +mne_menu_appea=a + +mne_about=a +mne_angle=n +mne_arc=a +mne_arrow=r +mne_sym_axial=a +mne_backward=p +mne_baricenter=b +mne_bisectrix=b +mne_bounds=g +mne_canvas_col=c +mne_cap_arc=c +mne_sym_central=c +mne_circ=c +mne_circumcenter=c +mne_copy=c +mne_delete=t +mne_divisions=s +mne_ellipse=e +mne_end=e +mne_ex_metadata=x +mne_exit=e +mne_extremes=e +mne_eyedropper=e +mne_forward=n +mne_fragment=f +mne_free=f +mne_fusion=u +mne_homo=h +mne_hyperbola=h +mne_incenter=i +mne_intersection=n +mne_invert_sel=i +mne_jme=u +mne_mediatrix=m +mne_midpoint=i +mne_mod_seg=y +mne_z_up=p +mne_z_down=d +mne_new=n +mne_open=o +mne_ortocenter=o +mne_parabola=p +mne_parallel=a +mne_paste=a +mne_perp=p +mne_point=p +mne_point_col=p +mne_polygon=p +mne_polyline=o +mne_protractor=o +mne_rect=e +mne_redo=r +mne_reg_pol=r +mne_rewind=r +mne_rotation=r +mne_ruler=l +mne_save=s +mne_save_as=a +mne_save_img=v +mne_print=p +mne_segment=s +mne_sel_all=l +mne_selection=e +mne_shape_col=s +mne_spline=l +mne_text=s +mne_trans=t +mne_triangle=t +mne_undo=u +mne_vertex=v +mne_zoom_all=z +mne_zoom_in=o +mne_zoom_out=m +mne_paint=i +mne_comment=m +mne_shape_fill=f +mne_union=u +mne_sym_diff=y + +# toast +toast_copy=shape/s copied +toast_no_copy=nothing selected +toast_paste=shape/s pasted + +# tooltips +tip_swap_color=Swap colors +tip_undo_redo=Undo/Redo history diff --git a/bin/jdrafting/resources/language/language_es.properties b/bin/jdrafting/resources/language/language_es.properties new file mode 100644 index 0000000..802ea6b --- /dev/null +++ b/bin/jdrafting/resources/language/language_es.properties @@ -0,0 +1,480 @@ +# general +app_des=es una aplicación open-source para diseñar pequeños ejercicios de Dibujo Técnico.
Esta aplicación está inspirada por la 'Construcción clásica',
también conocida como 'Construcción de regla y compás'.
Sin embargo, otras herramientas de trazado pueden usarse.
(Más info sobre construcción clásica: Wikipedia) +width=Ancho +height=Alto +degrees=grad +not_saved=No guardado +cancel=Cancelar +add=Añadir +remove=Eliminar + +# labels +thickness=

Grosor

de línea +point_thickness=

Grosor

de punto +lbl_fill=Fill +lbl_no_fill=Sin relleno +lbl_line_color=Color línea +lbl_point_color=Color punto +lbl_fill_color=Color relleno + +# actions +new=Nuevo +new_des=Empezar un nuevo ejercicio +open=Abrir... +open_des=Abrir un ejercicio +save=Guardar +save_des=Guardar ejercicio +save_as=Guardar como... +save_as_des=Guardar ejercicio como +save_image=Guardar imagen... +save_image_des=Exportar ejercicio como imagen SVG, PNG +print=Imprimir... +print_des=Imprimir en la impresora seleccionada +fileinfo=Ejercicio info +fileinfo_des=Obtener o modificar la información del ejercicio +redo=Rehacer +undo=Deshacer +move_up=Subir figuras +move_up_des=Incrementar índice de las figuras seleccionadas +move_down=Bajar figuras +move_down_des=Decrementar índice de las figuras seleccionadas +copy=Copiar seleccionadas +copy_des=Copiar figuras seleccionadas +exit=Salir +exit_des=Salir +about=Acerca de... +zoom_all=Zoom a todo +zoom_all_des=Ajustar contenido a lienzo +zoom_in=Aumentar Zoom +zoom_in_des=Aumentar Zoom +zoom_out=Disminuir Zoom +zoom_out_des=Disminuir Zoom +delete=Borrar seleccionadas +delete_des=Borrar figuras seleccionadas +background_color=Color del lienzo... +background_color_des=Color del lienzo +color=Color de la figura... +color_des=Color de la figura +point_color=Color de punto... +point_color_des=Color de punto +eyedropper=Cuentagotas +eyedropper_des=Capturar estilo de figura +paste_style=Pegar estilo +paste_style_des=Pegar actual estilo y color de línea +text=Mostrar/ocultar nombre figuras +text_des=Mostrar/ocultar nombre figuras +backward=Paso previo +backward_des=Paso previo +forward=Siguiente paso +forward_des=Siguiente paso +end=Final +end_des=Final +rewind=Inicio +rewind_des=Inicio +selection=Selección rectangular +selection_des=Seleccionar figuras en un área rectangular +invert=Invertir selección +invert_des=Invertir selección +select_all=Seleccionar todo +select_all_des=Seleccionar todo +point=Punto +point_des=Crear un punto nuevo +segment=Segmento +segment_des=Crear segmento +arrow=Flecha +arrow_des=Crear flecha simple o doble +polygon_tools=Herramientas de polígono +reg_poly=Polígono regular +reg_poly_des=Crear polígono regular +polygon=Polígono +polygon_des=Crear polígono +polyline=Polilínea +polyline_des=Crear polilínea +free_hand=Mano libre +free_hand_des=Crear una figura arbitraria +rectangle=Rectangulo +rectangle_des=Crear rectángulo +ellipse=Elipse +ellipse_des=Crear elipse +arc=Arco +arc_des=Crear arco +circumference=Circunferencia +circumference_des=Crear circunferencia +angle=Ángulo +angle_des=Crear un segmento por ángulo +triangle_tools=Herramientas de triángulo +triangle=Triángulo +triangle_des=Crear triángulo +incenter=Incentro +ortocenter=Ortocentro +baricenter=Baricentro +circumcenter=Circuncentro +comment=Comentario +comment_des=Añadir comentario +spline=Spline +spline_des=Crear spline entre puntos +parabola=Parábola +parabola_des=Crear parábola a partir de rectángulo frontera +hyperbola=Hipérbola +hyperbola_des=Crear hipérbola a partir de rectángulo frontera +func_des=Crear gráfico desde función +func=Función +ruler=Regla +ruler_des=Capturar distancia +protractor=Transportador +protractor_des=Capturar ángulo +divisions=Puntos de división +divisions_des=Dividir figura en puntos +modify=Modificar segmento +modify_des=Modifica un segmento en su recta contenedora +midpoint=Punto medio +midpoint_des=Punto medio del rectángulo contenedor de la figura +centroid=Centroide +vertex=Vértices +vertex_des=Añadir vértices a la figura +extremes=Extremos +extremes_des=Añadir extremos a una figura no cerrada +inter=Puntos de intersección +inter_des=Seleccionar dos o más figuras (no puntos) para añadir intersecciones entre ellas +fragment=Fragmentar lados +fragment_des=Fragmenta una figura en sus lados constituyentes +fusion=Mezclar figuras +fusion_des=Seleccionar figuras para ser mezcladas. Shift para conectar extremos +bounds=Rectángulo frontera +bounds_des=Crear rectángulo frontera de una figura +perp=Perpendicular +perp_des=Perpendicular de un segmento +para=Paralela +para_des=Paralela a un segmento +mediatrix=Mediatriz +mediatrix_des=Mediatriz de un segmento +bisectrix=Bisectriz +bisectrix_des=Bisectriz entre dos segmentos +capable_arc=Arco capaz +capable_arc_des=Arco capaz de un segmento +translation=Trasladar +translation_des=Trasladar figuras seleccionadas +rotation=Rotar +rotation_des=Rotar figuras seleccionadas +homothety=Homotecia +homothety_des=Homotecia de las figuras seleccionadas +central_sym=Simetría central +central_sym_des=Simetría central de las figuras seleccionadas +axial_sym=Simetría axial +axial_sym_des=Simetría axial de las figuras seleccionadas +paint=Pintar +paint_des=Pintar figura +fix_dist=Fijar
distancia +paste=Pegar +paste_des=Pegar figuras seleccionadas +fill=Color de relleno\u2026 +fill_des=Color de relleno de las figuras +area_intersection=Intersección de figuras +area_intersection_des=Figura a partir de la intersección de las seleccionadas +area_union=Unión de figuras +area_union_des=Figura a partir de la unión de las seleccionadas +area_substract=Diferencia de figuras +area_substract_des=Figura a partir de la diferencia de otras dos +area_sym_diff=Diferencia simétrica de figuras +area_sym_diff_des=Figura a partir de la diferencia simétrica de las seleccionadas + +# menus +file=Archivo +edit=Editar +style=Estilo +shapes=Formas +exercise=Ejercicio +transforms=Transformaciones +conics=Cónicas +view=Vista +appearance=Apariencia +help=Ayuda + +item_style=Mostrar/Ocultar barra de estilo +item_ruler_prot=Mostrar/Ocultar barra de regla y compás +item_status=Mostrar/Ocultar barra de estado +item_action=Mostrar/Ocultar barra de acciones +item_shape=Mostrar/Ocultar barra de figuras +item_tool=Mostrar/Ocultar barra de herramientas +show_all=Mostrar todo +hide_all=Ocultar todo + +# toolbars +tools=Herramientas +actions=Acciones +tit_style=Estilo de línea/punto +ruler_prot=Regla y Transportador + +# dialogs +exit_msg=Ignorar cambios? +exit_dlg=Salir +inter_msg=Seleccione dos o más figuras (puntos no) +inter_dlg=Error en la intersección +ext_dlg=La figura debe ser abierta +ext_title=Error al seleccionar figura +bisectrix_dlg=Segmentos paralelos o coincidentes +bisectrix_title=Error en bisectriz +div_dlg=Número de divisiones +reg_poly_dlg=Número de vértices/lados +save_image_acce1=Parámetros de imagen +save_image_acce2=Incluir fondo +save_image_acce3=Incluir nombre de figuras +overwrite1=El fichero existe, ¿sobreescribir? +overwrite2=Sobreescribir +exer_prop=Propiedades del ejercicio +title=Título +exer_desc=Descripción del ejercicio +start_frame=Frame inicial +shape_name=Nombre de la figura +shape_prop=Propiedades de la figura +shape_desc=Descripción de la figura +save_close=Guardar y cerrar +details=Detalles +selected_shapes_msg=Se debe seleccionar al menos una figura +homo_dlg=Factor de homotecia +dist_mult_dlg=Multiplicar distancia por factor +jme_dlg=Expresión JME +jme_examples=ejemplos +jme_doc=Documentación JME +jme_min=Mínimo valor 'x' o 't' +jme_max=Máximo valor 'x' o 't' +jme_intervals=Número de intervalos +save_error1=Error al guardar +save_error2=El ejercicio está vacío +sel_2_error=Seleccione dos o más figuras +inter_error=No hay intersecciones +empty_intersection_error=Intersección vacía +empty_union_error=Unión vacía +empty_substract_error=Diferencia vacía +empty_sym_diff_error=Diferencia simétrica vacía + +# new shapes default description +new_point=punto +new_segment=segmento +new_segment_extreme=extremo de segmento +new_arc=arco +new_circumference=circunferencia +new_circumference_center=centro circunferencia +new_arrow=flecha +new_triangle=triángulo +new_rectangle=rentángulo +new_square=cuadrado +new_regular_polygon=polígono regular +new_polygon=polígono +new_polyline=polilínea +new_ellipse=elipse +new_v_axis=eje vertical +new_h_axis=eje horizontal +new_parabola=parábola +new_spline=spline +new_free_hand=mano alzada +new_midpoint=> punto medio de +new_centroid=> centroide de +new_vertex=> vértice de +new_extreme=> extremo de +new_parallel=> paralela a +new_perpendicular=> perpendicular a +new_mediatrix=> mediatriz de +new_bisectrix=> bisectriz de +new_capable_arc=> arco capaz de +new_fragment=> fragmento de +new_div=> division de +new_join=> punto de intersección entre +new_bounds=> frontera de +new_fusion=> fusión de +new_focus=foco +new_para_vertex=vértice de parábola +new_para_bounds=frontera de parábola +new_directrix=directriz +new_hype_branch=rama de hipérbola +new_hype_vertex=vértice de hipérbola +new_hype_bounds=frontera de hipérbola +new_hype_center=centro de hipérbola +new_main_axis=eje principal de hipérbola +new_img_axis=eje imaginario de hipérbola +new_circumscribed=circunferencia circunscrita +new_center_reg=centro de polígono regular +new_intersection=> intersección de +new_union=> unión de +new_substract=> diferencia de +new_sym_diff=> diferencia simétrica de + +# status msg +txt_hand=Use rueda de ratón para hacer zoom. Arrastrar ratón con botón secundario para mover figuras. Shift sobre la figura para seleccionar/deseleccionar. +txt_eyedropper=Seleccionar figura para capturar estilo de línea. Shift para capturar estilo de punto. Control para capturar color en pantalla +txt_paste_style1=Seleccionar figura para aplicar estilo de línea. Control para no aplicar relleno. Shift para aplicar estilo de punto. +txt_ruler1=Seleccionar punto de inicio +txt_ruler2=Seleccionar punto final. Shift para añadir distancia. Control para restar +txt_prot1=Seleccionar vértice del ángulo +txt_prot2=Seleccionar primer lado +txt_prot3=Seleccionar segundo lado +txt_sel1=Arrastrar ratón para seleccionar área rectangular. Control para seleccionar a partir de figura completa +txt_point=Seleccionar posición del punto. Control para no ajustar a intersección. Shift para usar estilo de línea en lugar de punto +txt_seg1=Seleccionar punto de inicio +txt_seg2=Seleccionar punto final. Shift para ángulos fijos cada 45º. Control para añadir extremos +txt_arrow1=Seleccionar punto de inicio +txt_arrow2=Seleccionar punto final. Shift para ángulos fijos cada 45º. Control para flecha doble +txt_circ1=Seleccionar centro de la circunferencia. Shift para intercambiar radio/diametro +txt_circ2=Seleccionar radio. Shift para intercambiar radio/diametro. Control para añadir centro +txt_arc1=Seleccionar centro del arco +txt_arc2=Seleccionar ángulo inicial +txt_arc3=Seleccionar ángulo final. Control para ángulo conjugado. Shift para ángulo fijo. Alt para ángulos enteros +txt_angle1=Seleccionar vértice +txt_angle2=Seleccionar primer lado +txt_angle3=Seleccionar segundo lado. Shift para ángulo suplementario. Control para ángulo complementario. Alt para añadir ambos lados +txt_rect1=Selecionar punto inicial del rectángulo +txt_rect2=Mover ratón para seleccionar punto final. Shift para cuadrado. Puede usar la regla +txt_ellipse1=Seleccionar centro o esquina. Shift para modo centro/esquina +txt_ellipse2=Seleccionar tamaño de la elipse. Control para añadir ejes y focos. Shift para modo centro/esquina. Puede usar la regla +txt_poly=Seleccionar vértices. Doble click para terminar +txt_free1=Arrartre ratón para dibujar una figura arbitraria. Doble click para terminar +txt_modify1=Seleccionar segmento a modificar +txt_modify2=Seleccionar primer extremo +txt_modify3=Seleccionar segundo extremo. Shift para actual estilo de línea en lugar del estilo de la figura +txt_midpoint=Seleccionar figura para añadir punto medio del rectángulo frontera. Shift para usar estilo de línea. Control para centroide +txt_vtx1=Seleccione figura para añadir vértices. Shift para estilo de línea +txt_ext1=Seleccione figura para añadir extremos (debe ser una figura abierta) +txt_div1=Seleccione figura para añadir puntos de división. Puede usar la regla para una distancia fijada +txt_fragment1=Seleccionar figura para fragmentar lados. Shift para usar estilo actual en lugar de estilo de figura +txt_bounds1=Seleccionar figura para añadir rectángulo frontera +txt_perp1=Seleccione segmento +txt_perp2=Seleccione comienzo de la perpendicular +txt_perp3=Seleccione fin de la perpendicular +txt_para1=Seleccione segmento +txt_para2=Seleccione comienzo de la paralela +txt_para3=Seleccione fin de la paralela +txt_mediatrix1=Seleccione segmento +txt_mediatrix2=Seleccione comienzo de la mediatriz +txt_mediatrix3=Seleccione final de la mediatriz +txt_bisectrix1=Seleccione primer segmento +txt_bisectrix2=Seleccione segundo segmento +txt_bisectrix3=Seleccione comienzo de la bisectriz +txt_bisectrix4=Seleccione final de la bisectriz +txt_cap1=Seleccione segmento +txt_cap2=Seleccione uno de los dos lados +txt_translate1=Seleccione primer punto del vector de dirección +txt_translate2=Seleccione segundo punto del vector de dirección. Puede usar la regla +txt_rotation1=Seleccione centro de rotación +txt_rotation2=Mueva ratón para seleccionar ángulo. Shift para ángulos fijos. Control para ángulos enteros +txt_homothety1=Seleccione centro de homotecia. Shift para factor negativo. Control para invertir factor +txt_central_sym1=Seleccione centro de simetría +txt_axial_sym1=Seleccione primer punto del eje +txt_axial_sym2=Seleccione segundo punto del eje +txt_triangle_points1=Seleccione tres puntos. Shift para equilátero. Control para rectángulo +txt_triangle_points2=Seleccione tres puntos. Shift para estilo de línea +txt_parabola1=Selecionar punto inicial de la frontera de la parábola +txt_parabola2=Mover ratón para seleccionar punto final. Control para añadir figuras auxiliares. Shift para frontera cuadrada. Puede usar la regla +txt_hyperbola1=Selecionar punto inicial de la frontera de la hipérbola +txt_hyperbola2=Mover ratón para seleccionar punto final. Puede usar la regla +txt_hyperbola3=Mover ratón para seleccionar eje principal. Control para añadir figuras auxiliares +txt_reg_poly1=Seleccione centro del polígono +txt_reg_poly2=Seleccione radio. Control para añadir figuras auxiliares. Puede usar la regla +txt_paint1=Seleccionar figura para rellenar. Control para eliminar relleno. Alt para usar color de línea. Shift para usar color de punto +txt_comment1=Selecionar punto inicial de la caja de texto +txt_area_substract1=Seleccionar primera figura +txt_area_substract2=Seleccionar segunda figura +txt_jme1=Seleccionar punto inicial del origen de coordenadas +txt_jme2=Seleccionar punto final del eje de abcisas + +# mnemonics +mne_menu_file=a +mne_menu_edit=e +mne_menu_style=s +mne_menu_shapes=f +mne_menu_tools=h +mne_menu_exercise=j +mne_menu_view=v +mne_menu_help=y +mne_menu_transform=t +mne_menu_trian=h +mne_menu_poly=e +mne_menu_conics=i +mne_menu_appea=a + +mne_about=a +mne_angle=n +mne_arc=a +mne_arrow=f +mne_sym_axial=i +mne_backward=p +mne_baricenter=b +mne_bisectrix=b +mne_bounds=c +mne_canvas_col=l +mne_cap_arc=r +mne_sym_central=s +mne_circ=c +mne_circumcenter=c +mne_copy=c +mne_delete=o +mne_divisions=n +mne_ellipse=e +mne_end=f +mne_ex_metadata=e +mne_exit=s +mne_extremes=e +mne_eyedropper=u +mne_forward=s +mne_fragment=f +mne_free=m +mne_fusion=z +mne_homo=h +mne_hyperbola=h +mne_incenter=i +mne_intersection=s +mne_invert_sel=i +mne_jme=u +mne_mediatrix=m +mne_midpoint=u +mne_mod_seg=o +mne_z_up=u +mne_z_down=b +mne_new=n +mne_open=a +mne_ortocenter=o +mne_parabola=p +mne_parallel=a +mne_paste=p +mne_perp=p +mne_point=p +mne_point_col=o +mne_polygon=o +mne_polyline=l +mne_protractor=d +mne_rect=r +mne_redo=r +mne_reg_pol=p +mne_rewind=i +mne_rotation=r +mne_ruler=g +mne_save=g +mne_save_as=u +mne_save_img=r +mne_print=i +mne_segment=s +mne_sel_all=e +mne_selection=s +mne_shape_col=c +mne_spline=l +mne_text=s +mne_trans=t +mne_triangle=t +mne_undo=d +mne_vertex=v +mne_zoom_all=z +mne_zoom_in=u +mne_zoom_out=d +mne_paint=i +mne_comment=o +mne_shape_fill=f +mne_union=u +mne_sym_diff=i + +# toast +toast_copy=figura/s copiada/s +toast_no_copy=nada seleccionado +toast_paste=figura/s pegada/s + +# tooltips +tip_swap_color=Intercambiar colores +tip_undo_redo=Historial deshacer/rehacer diff --git a/lib/batik-awt-util-1.8.jar b/lib/batik-awt-util-1.8.jar new file mode 100644 index 0000000..740acb7 Binary files /dev/null and b/lib/batik-awt-util-1.8.jar differ diff --git a/lib/batik-dom-1.8.jar b/lib/batik-dom-1.8.jar new file mode 100644 index 0000000..4899b51 Binary files /dev/null and b/lib/batik-dom-1.8.jar differ diff --git a/lib/batik-ext-1.8.jar b/lib/batik-ext-1.8.jar new file mode 100644 index 0000000..d7c850b Binary files /dev/null and b/lib/batik-ext-1.8.jar differ diff --git a/lib/batik-svggen-1.8.jar b/lib/batik-svggen-1.8.jar new file mode 100644 index 0000000..9aaa826 Binary files /dev/null and b/lib/batik-svggen-1.8.jar differ diff --git a/lib/batik-util-1.8.jar b/lib/batik-util-1.8.jar new file mode 100644 index 0000000..23e3625 Binary files /dev/null and b/lib/batik-util-1.8.jar differ diff --git a/lib/batik-xml-1.8.jar b/lib/batik-xml-1.8.jar new file mode 100644 index 0000000..1101455 Binary files /dev/null and b/lib/batik-xml-1.8.jar differ diff --git a/lib/cla_v1.0.jar b/lib/cla_v1.0.jar new file mode 100644 index 0000000..1d271e3 Binary files /dev/null and b/lib/cla_v1.0.jar differ diff --git a/lib/jme_6_2.jar b/lib/jme_6_2.jar new file mode 100644 index 0000000..5266bb0 Binary files /dev/null and b/lib/jme_6_2.jar differ diff --git a/src/LICENSE.txt b/src/LICENSE.txt new file mode 100644 index 0000000..1a233af --- /dev/null +++ b/src/LICENSE.txt @@ -0,0 +1,16 @@ +//////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016,2020,2021 Miguel Alejandro Moreno Barrientos +// +// JDrafting 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. +// +// JDrafting 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 JDrafting. If not, see . +//////////////////////////////////////////////////////////////////////// diff --git a/src/jdrafting/Exercise.java b/src/jdrafting/Exercise.java new file mode 100644 index 0000000..fd6360b --- /dev/null +++ b/src/jdrafting/Exercise.java @@ -0,0 +1,237 @@ + package jdrafting; + +import java.awt.Color; +import java.awt.geom.Rectangle2D; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import jdrafting.geom.JDraftingShape; + +/** + * JDrafting exercise info (shapes, index, title, ...) + * @author Miguel Alejandro Moreno Barrientos + * @since 0.1.0 + */ +public class Exercise implements Iterable, Serializable +{ + private String title, description; // exercise title & description + private List shapes; // list of shapes of the exercise + private Color backgroundColor = Color.WHITE; // current background + private int startIndex = 1; // frame index where starts exercise (set data in first frames) + transient private int frameIndex; // current frame index in presentation mode + + + ////////////////// + // CONSTRUCTORS // + ////////////////// + + public Exercise( String title, String description ) + { + shapes = new ArrayList<>(); + frameIndex = shapes.size(); + this.title = title; + this.description = description; + } + + public Exercise() { this( "", "" ); } + + + ///////////// + // GETTERS // + ///////////// + + public String getTitle() { return title; } + public String getDescription() { return description; } + public List getShapes() { return shapes; } + public Color getBackgroundColor() { return backgroundColor; } + public int getStartIndex() { return startIndex; } + public int getFrameIndex() { return frameIndex; } + + + ///////////// + // SETTERS // + ///////////// + + public Exercise setTitle(String title) { this.title = title; return this; } + public Exercise setDescription( String description ) + { + this.description = description; + + return this; + } + public void setBackgroundColor( Color backgroundColor ) + { + this.backgroundColor = backgroundColor; + } + public void setStartIndex( int startIndex ) { this.startIndex = startIndex; } + public void setFrameIndex( int frameIndex ) + { + this.frameIndex = frameIndex; + } + + public int setFrameAtEnd() + { + frameIndex = shapes.size(); + return frameIndex; + } + + /** + * Get shape at index + * @param index shape index [0,n) + * @return shape at index + */ + public JDraftingShape get( int index ) + { + return getShapes().get( index ); + } + + /** + * Set shape at index + * @param index shape index [0,n) + * @param jdshape shape to store + */ + public void set( int index, JDraftingShape jdshape ) + { + getShapes().set( index, jdshape ); + } + + + ///////////// + // METHODS // + ///////////// + + /** + * Recover serialized exercise from stream and set index at the end + * @param ois stream + * @throws IOException if an I/O error occurs. + * @throws ClassNotFoundException if the class of a serialized object could not be found. + */ + private void readObject( ObjectInputStream ois ) throws IOException, + ClassNotFoundException + { + ois.defaultReadObject(); + frameIndex = shapes.size(); // put frame index at end + if ( backgroundColor == null ) // (compatibility all files) // TODO + backgroundColor = Color.WHITE; + } + + /** + * Shape index + * @param jdshape shape + * @return index [0,n) or -1 if doesn't exist. + */ + public int indexOf( JDraftingShape jdshape ) + { + return getShapes().indexOf( jdshape ); + } + + /** + * New shape at index + * @param index index in [0,n] + * @param jdshape shape + * @return same index + * @throws ArrayIndexOutOfBoundsException + * if the index is out of range(index < 0 || index > size()) + */ + public int addShape( int index, JDraftingShape jdshape ) + { + getShapes().add( index, jdshape ); + if ( index <= getFrameIndex() ) + setFrameIndex( getFrameIndex() + 1 ); + + return index; + } + + /** + * Add shape at the end + * @param jdshape shape + * @return insertion index + */ + public int addShape( JDraftingShape jdshape ) + { + return addShape( getFrameIndex(), jdshape ); + } + + /** + * Remove shape at index + * @param index [0,n) + * @throws ArrayIndexOutOfBoundsException + * if the index is out of range(index < 0 || index >= size()) + */ + public void removeShape( int index ) + { + getShapes().remove( index ); + if ( index < getFrameIndex() ) + setFrameIndex( getFrameIndex() - 1 ); + if ( getStartIndex() > size() ) + setStartIndex( Math.max( 1, size() ) ); + } + + /** + * Remove shape + * @param jdshape shape + * @return removed shape index + */ + public int removeShape( JDraftingShape jdshape ) + { + int index = indexOf( jdshape ); + removeShape( index ); + + return index; + } + + /** + * Return a sublist of the shapes from zero until frame index (exclusive) + * @return sublist [0,idx) + */ + public List getFramesUntilIndex() + { + return getShapes().subList( 0, getFrameIndex() ); + } + + /** + * Check exercise has not shapes + * @return {@code true} if empty + */ + public boolean isEmpty() { return getShapes().isEmpty(); } + + /** + * Number of shapes in the exercise + * @return n + */ + public int size() { return getShapes().size(); } + + /** + * Check whether frame index is at the end + * @return {@code true} if so + */ + public boolean isIndexAtEnd() { return getFrameIndex() == size(); } + + /** + * Get the rectangle which contains all shapes in the exercise + * @return exercise bounds or null if empty + */ + public Rectangle2D getBounds() + { + if ( !getShapes().isEmpty() ) + { + Rectangle2D enclosure = getShapes().get( 0 ).getShape().getBounds2D(); + + for ( JDraftingShape jdshape : getShapes() ) + enclosure = enclosure.createUnion( jdshape.getShape().getBounds2D() ); + + return enclosure; + } + + return null; + } + + @Override + public Iterator iterator() { return getShapes().listIterator(); } + + private static final long serialVersionUID = -2951147031143369819L; +} diff --git a/src/jdrafting/geom/JDMath.java b/src/jdrafting/geom/JDMath.java new file mode 100644 index 0000000..f235e86 --- /dev/null +++ b/src/jdrafting/geom/JDMath.java @@ -0,0 +1,492 @@ +package jdrafting.geom; + +import static java.lang.Double.isNaN; +import static java.lang.Math.abs; + +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.util.LinkedList; +import java.util.List; + +/** + * Math utility operations + * @version 0.1.11 + */ +final public class JDMath +{ + /** + * Avoid instantiation + */ + private JDMath() {} + + @Override + protected Object clone() throws CloneNotSupportedException + { + throw new CloneNotSupportedException(); + } + + /** + * Round double to the nearest integer + * @param d value + * @return d rounded to int + */ + public static int nearInt( double d ) { return (int) ( d + 0.5 ); } + + /** + * Return vector p1 to p2 + * @param p1 start point + * @param p2 dest point + * @return vector p1 to p2 + */ + public static Point2D vector( Point2D p1, Point2D p2 ) + { + return new Point2D.Double( p2.getX() - p1.getX(), + p2.getY() - p1.getY() ); + } + + /** + * Return argument of a vector + * @param v vector + * @return the argument in -pi to pi range + */ + public static double vectorArg( Point2D v ) + { + return Math.atan2( v.getY(), v.getX() ); + } + + /** + * Return normal vector using levorotation + * @param v vector + * @return normal vector + */ + public static Point2D normal( Point2D v ) + { + return new Point2D.Double( -v.getY(), v.getX() ); + } + + /** + * Multiply vector by scalar + * @param v vector + * @param k scalar + * @return the product vector + */ + public static Point2D mulVector( Point2D v, double k ) + { + return new Point2D.Double( v.getX() * k, v.getY() * k ); + } + + /** + * Sum to vectors + * @param v term1 + * @param w term2 + * @return sum vector + */ + public static Point2D sumVectors( Point2D v, Point2D w ) + { + return new Point2D.Double( v.getX() + w.getX(), v.getY() + w.getY() ); + } + + /** + * Adjust a vector to a same direction vector with modulus size + * @param v vector + * @param size modulus + * @return the adjusted vector + */ + public static Point2D adjustVectorToSize( Point2D v, double size ) + { + return mulVector( unitary( v ), size ); + } + + /** + * Get unitary vector + * @param v vector + * @return the unitary vector v + */ + public static Point2D unitary( Point2D v ) + { + double mod = v.distance( 0, 0 ); + return mod > 0. + ? new Point2D.Double( v.getX() / mod, v.getY() / mod ) + : (Point2D) v.clone(); + } + + /** + * Get midpoint between p1 and p2 + * @param p1 extreme point + * @param p2 extreme point + * @return midpoint + */ + public static Point2D midpoint( Point2D p1, Point2D p2 ) + { + return new Point2D.Double( ( p1.getX() + p2.getX() ) / 2., + ( p1.getY() + p2.getY() ) / 2. ); + } + + /** + * Get centroid of a point set + * @param points vertex set + * @return the centroid + */ + public static Point2D centroid( Iterable points ) + { + Point2D center = new Point2D.Double(); + int size = 0; + for ( Point2D p : points ) + { + center = sumVectors( center, p ); + size++; + } + + return new Point2D.Double( center.getX() / size, center.getY() / size ); + } + + /** + * Get a position using center, angle and radius + * @param center center point + * @param ang angle in radians + * @param radius distance to center + * @return the new point + */ + public static Point2D pointRelativeToCenter( Point2D center, double ang, + double radius ) + { + return new Point2D.Double( center.getX() + radius * Math.cos( ang ), + center.getY() + radius * Math.sin( ang ) ); + } + + /** + * Get angle between two segments or two vectors + * @param l1 segment + * @param l2 segment + * @return angle in 0 to pi range + */ + public static double lineAng( Line2D l1, Line2D l2 ) + { + Point2D v1 = new Point2D.Double( l1.getX2() - l1.getX1(), + l1.getY2() - l1.getY1() ); + Point2D v2 = new Point2D.Double( l2.getX2() - l2.getX1(), + l2.getY2() - l2.getY1() ); + + // |a||b|cos(ang)=a*b ==> ang = acos((a*b)/(|a||b|) + double dot = v1.getX() * v2.getX() + v1.getY() * v2.getY(); + double modv1 = v1.distance( 0, 0 ), modv2 = v2.distance( 0, 0 ); + + return Math.acos( dot / ( modv1 * modv2 ) ); + } + + /** + * Dot product + * @param v term1 + * @param w term2 + * @return the dot product v*w + */ + public static double scalar( Point2D v, Point2D w ) + { + return v.getX() * w.getX() + v.getY() * w.getY(); + } + + /** + * Projection w over v
+ * p=|v*w/|v|| + * @param v vector + * @param w vector + * @return modulus of projection + */ + public static double projection( Point2D v, Point2D w ) + { + return abs( scalar( v, w ) / v.distance( 0, 0 ) ); + } + + /** + * Get incenter of a triangle + * @param A vertex + * @param B vertex + * @param C vertex + * @return the incenter + */ + public static Point2D incenter( Point2D A, Point2D B, Point2D C ) + { + double a = vector( B, C ).distance( 0, 0 ); + double b = vector( A, C ).distance( 0, 0 ); + double c = vector( A, B ).distance( 0, 0 ); + double abc = a + b + c; + + return new Point2D.Double( + ( a * A.getX() + b * B.getX() + c * C.getX() ) / abc, + ( a * A.getY() + b * B.getY() + c * C.getY() ) / abc ); + } + + /** + * Get ortocenter of a triangle + * @param A vertex + * @param B vertex + * @param C vertex + * @return the ortocenter or null if vertex are aligned + */ + public static Point2D ortocenter( Point2D A, Point2D B, Point2D C ) + { + Point2D vBC = vector( B, C ); + Point2D nBC = normal( vBC ); + Point2D vAC = vector( A, C ); + Point2D nAC = normal( vAC ); + + return linesIntersection( A, sumVectors( A, nBC ), + B, sumVectors( B, nAC ) ); + } + + /** + * Get baricenter of a triangle + * @param a vertex + * @param b vertex + * @param c vertex + * @return the baricenter + */ + public static Point2D baricenter( Point2D A, Point2D B, Point2D C ) + { + List vertex = new LinkedList<>(); + vertex.add( A ); + vertex.add( B ); + vertex.add( C ); + + return centroid( vertex ); + } + + /** + * Get circumcenter of a triangle + * @param a vertex + * @param b vertex + * @param c vertex + * @return the circumcenter + */ + public static Point2D circumcenter( Point2D A, Point2D B, Point2D C ) + { + double Ax = A.getX(), Ay = A.getY(); + double Bx = B.getX(), By = B.getY(); + double Cx = C.getX(), Cy = C.getY(); + double dCBx = Cx - Bx, dBCy = By - Cy; + double dACx = Ax - Cx, dCAy = Cy - Ay; + double dBAx = Bx - Ax, dABy = Ay - By; + double d = 2 * ( Ax * dBCy + Bx * dCAy + Cx * dABy ); + + return new Point2D.Double( + ( ( Ax * Ax + Ay * Ay ) * dBCy + + ( Bx * Bx + By * By ) * dCAy + + ( Cx * Cx + Cy * Cy ) * dABy ) / d, + ( ( Ax * Ax + Ay * Ay ) * dCBx + + ( Bx * Bx + By * By ) * dACx + + ( Cx * Cx + Cy * Cy ) * dBAx ) / d ); + } + + /** + * Shape length (poligonal length) + * @param shape the shape + * @param transform optional transform or null + * @return length + */ + public static double length( Shape shape, AffineTransform transform ) + { + double length = 0.; + PathIterator path = shape.getPathIterator( transform ); + double[] current = new double[6]; + double x = 0., y = 0.; + path.currentSegment( current ); + double x0 = current[0], y0 = current[1]; + + while ( !path.isDone() ) + { + int type = path.currentSegment( current ); + + if ( type == PathIterator.SEG_CLOSE + && ( current[0] != x0 || current[1] == y0 ) ) + { + length += Point2D.distance( x0, y0, current[0], current[1] ); + return length; + } + else if ( type != PathIterator.SEG_MOVETO ) + length += Point2D.distance( x, y, current[0], current[1] ); + + x = current[0]; + y = current[1]; + + path.next(); + } + + return length; + } + + /** + * Solve cuadratic equation ax^2+bx+c=0 + * @param a param a + * @param b param b + * @param c param c + * @return 2D array with two solutions + */ + public static double[] solveQuadratic( double a, double b, double c ) + { + double disc = Math.sqrt( b * b - 4 * a * c ); + + return new double[] { ( -b + disc ) / ( 2 * a ), + ( -b - disc ) / ( 2 * a ) }; + } + + /** + * @param d -2*x1 + * @param e -2*y1 + * @param f x1^2+y1^2-r1^2 + * @param d_ -2*x2 + * @param e_ -2*y2 + * @param f_ x2^2+y2^2-r2^2 + * @return a vector with tangency points or null if circles are externals or overlapping + */ + public static Point2D.Double[] circlesIntersection( double d, double e, double f, double d_, + double e_, double f_ ) + { + double dd = d - d_, de = e_ - e, df = f_ - f; + double dd2 = dd * dd, de2 = de * de, df2 = df * df; + + if ( de == 0 ) // same 'y' coordinates (e_-e=0) + { + double x = df / dd; + double[] solY = solveQuadratic( 1, e, x * x + d * x + f ); + + if ( isNaN( solY[0] ) ) // external or overlapping circles + return null; + + if ( solY[0] == solY[1] ) // tangent circles + return new Point2D.Double[] { new Point2D.Double( x, solY[0] ) }; + + // secant circles + return new Point2D.Double[] { new Point2D.Double( x, solY[0] ), + new Point2D.Double( x, solY[1] ) }; + } + + double u = de2 + dd2; + double v = de2 * d - 2 * df * dd + e * de * dd; + double w = df2 - e * de * df + f * de2; + + double[] solX = solveQuadratic( u, v, w ); + + if ( isNaN( solX[0] ) ) // external circles + return null; + + if ( solX[0] == solX[1] ) // tangent circles + return new Point2D.Double[] { + new Point2D.Double( solX[0], ( dd * solX[0] - df ) / de ) }; + + // secant circles + return new Point2D.Double[] { new Point2D.Double( solX[0], ( dd * solX[0] - df ) / de ), + new Point2D.Double( solX[1], ( dd * solX[1] - df ) / de ) }; + } + + public static Point2D.Double[] circlesIntersection( Point2D.Double center1, double radius1, + Point2D.Double center2, double radius2 ) + { + return circlesIntersection( -2 * center1.x, -2 * center1.y, + center1.distanceSq( 0, 0 ) - radius1 * radius1, -2 * center2.x, + -2 * center2.y, + center2.distanceSq( 0, 0 ) - radius2 * radius2 ); + } + + public static Point2D.Double[] circleLineIntersection( double a, double b, double c, double d, + double e, double f ) + { + if ( b == 0 ) // vertical line + { + double x = -c / a; + + double[] solY = solveQuadratic( a * a, a * a * e, c * c + a * ( a * f - c * d ) ); + + if ( isNaN( solY[0] ) ) // line and circle are external + return null; + + if ( solY[0] == solY[1] ) // line is tangent + return new Point2D.Double[] { new Point2D.Double( x, solY[0] ) }; + + // line and circle are secants + return new Point2D.Double[] { new Point2D.Double( x, solY[0] ), + new Point2D.Double( x, solY[1] ) }; + } + + double[] solX = solveQuadratic( a * a + b * b, 2 * a * c + b * ( b * d - a * e ), + c * c + b * ( b * f - e * c ) ); + + if ( isNaN( solX[0] ) ) // line and circle are external + return null; + + if ( solX[0] == solX[1] ) // line is tangent + return new Point2D.Double[] { new Point2D.Double( solX[0], ( -c - a * solX[0] ) / b ) }; + + // line and circle are secants + return new Point2D.Double[] { new Point2D.Double( solX[0], ( -c - a * solX[0] ) / b ), + new Point2D.Double( solX[1], ( -c - a * solX[1] ) / b ) }; + } + + public static Point2D.Double[] circleLineIntersection( Point2D.Double p1, Point2D.Double p2, + Point2D.Double center, double radius ) + { + return circleLineIntersection( p2.y - p1.y, p1.x - p2.x, p2.x * p1.y - p1.x * p2.y, + -2 * center.x, -2 * center.y, + center.distanceSq( 0, 0 ) - radius * radius ); + } + + /** + * Get intersection point between two segments by general straight equation + * @param a (a)x+by+cz=0 + * @param b ax+(b)y+cz=0 + * @param c ax+by+(c)z=0 + * @param a_ (a_)x+b_y+c_z=0 + * @param b_ a_x+(b_)y+c_z=0 + * @param c_ a_x+b_y+(c_)z=0 + * @return intersection point or null if lines are parallel or coincident + */ + public static Point2D linesIntersection( double a, double b, double c, + double a_, double b_, double c_ ) + { + double err = 1E-12; // (solve accuracy bugs in vertical lines) + // second line is vertical + if ( abs( b_ ) < err ) + { + // first line is vertical (parallel or coincident) + if ( abs( b ) < err ) + return null; + + // lines are secant + return new Point2D.Double( -c_ / a_, + ( a * c_ - a_ * c ) / ( a_ * b ) ); + } + + double div = b_ * a - b * a_; + + // lines are parallel or coincident + if ( div == 0 ) + return null; + + // lines are secant + double x = ( b * c_ - b_ * c ) / div; + + return new Point2D.Double( x, ( -c_ - a_ * x ) / b_ ); + } + + /** + * Get intersection point between two segments by extreme points + * @param p1 first segment start + * @param p2 first segment end + * @param q1 second segment start + * @param q2 second segment end + * @return intersection point or null if lines are parallel or coincident + */ + public static Point2D linesIntersection( Point2D p1, Point2D p2, + Point2D q1, Point2D q2 ) + { + return linesIntersection( + p2.getY() - p1.getY(), // a + p1.getX() - p2.getX(), // b + p2.getX() * p1.getY() - p1.getX() * p2.getY(), // c + q2.getY() - q1.getY(), // a_ + q1.getX() - q2.getX(), // b_ + q2.getX() * q1.getY() - q1.getX() * q2.getY() ); // c_ + } +} diff --git a/src/jdrafting/geom/JDPoint.java b/src/jdrafting/geom/JDPoint.java new file mode 100644 index 0000000..0acf074 --- /dev/null +++ b/src/jdrafting/geom/JDPoint.java @@ -0,0 +1,23 @@ +package jdrafting.geom; + +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +@SuppressWarnings("serial") +public class JDPoint extends Line2D.Double +{ + public JDPoint( Point2D point ) { super( point, point ); } + public JDPoint( double x, double y ) { super( x, y, x, y ); } + + public Point2D.Double toPoint2Ddouble() + { + return new Point2D.Double( getX1(), getY1() ); + } + + @Override + public Rectangle2D getBounds2D() + { + return new Rectangle2D.Double( getX1(), getY1(), Math.ulp( 0. ), Math.ulp( 0. ) ); + } +} diff --git a/src/jdrafting/geom/JDStrokes.java b/src/jdrafting/geom/JDStrokes.java new file mode 100644 index 0000000..3aca393 --- /dev/null +++ b/src/jdrafting/geom/JDStrokes.java @@ -0,0 +1,106 @@ +package jdrafting.geom; + +import java.awt.BasicStroke; +import java.util.Arrays; + +public enum JDStrokes +{ + PLAIN_ROUND { + @Override + public BasicStroke getStroke() + { + return new BasicStroke( 1f, + BasicStroke.CAP_ROUND, + BasicStroke.JOIN_ROUND ); + } + }, + PLAIN_SQUARE { + @Override + public BasicStroke getStroke() + { + return new BasicStroke( 1f, + BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER ); + } + }, + DOTTED { + @Override + public BasicStroke getStroke() + { + return new BasicStroke( 1f, + BasicStroke.CAP_BUTT, + BasicStroke.JOIN_BEVEL, + 1f, + new float[] { 2f, 2f }, + 0f ); + } + }, + MIXED { + @Override + public BasicStroke getStroke() + { + return new BasicStroke( 1f, + BasicStroke.CAP_ROUND, + BasicStroke.JOIN_BEVEL, + 1f, + new float[] { 8f, 7f, 2f, 7f }, + 0f ); + } + }, + DASHED { + @Override + public BasicStroke getStroke() { + return new BasicStroke( 1f, + BasicStroke.CAP_ROUND, + BasicStroke.JOIN_BEVEL, + 1f, + new float[] { 8f, 4f }, + 0f ); + } + + }; + + public abstract BasicStroke getStroke(); + + public static BasicStroke getStroke( BasicStroke stroke, + final float linewidh ) + { + if ( stroke.getDashArray() != null ) + { + float[] dashArray = new float[ stroke.getDashArray().length ]; + for ( int i = 0; i < stroke.getDashArray().length; i++ ) + dashArray[i] = (float) + ( stroke.getDashArray()[i] * Math.sqrt( linewidh ) ); + return new BasicStroke( linewidh, + stroke.getEndCap(), + stroke.getLineJoin(), + stroke.getMiterLimit() , + dashArray, + stroke.getDashPhase() ); + } + + return new BasicStroke( linewidh, + stroke.getEndCap(), + stroke.getLineJoin() ); + } + + /** + * Copy stroke with a different line width + * @param linewidth shape linewidth + * @param stroke original stroke + * @return new stroke identical to original except for line width + */ + public static BasicStroke cloneStrokeStyle( float linewidth, + BasicStroke stroke ) + { + return new BasicStroke( linewidth, + stroke.getEndCap(), + stroke.getLineJoin(), + stroke.getMiterLimit(), + stroke.getDashArray() == null + ? null + : Arrays.copyOf( stroke.getDashArray(), + stroke.getDashArray().length ), + stroke.getDashPhase() ); + } +} diff --git a/src/jdrafting/geom/JDraftingShape.java b/src/jdrafting/geom/JDraftingShape.java new file mode 100644 index 0000000..940d432 --- /dev/null +++ b/src/jdrafting/geom/JDraftingShape.java @@ -0,0 +1,365 @@ +package jdrafting.geom; + +import static jdrafting.gui.JDUtils.elvis; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import jdrafting.gui.JDUtils; + + +/** + * JDrafting shape model + * @since 0.1.0 + * @version 0.1.11.2 + */ +public class JDraftingShape implements Serializable +{ + // instance vars + private Shape shape; + private String name = ""; + private String description = ""; + private Color color = Color.BLACK, fill; + private Point2D textPosition = new Point2D.Double( 0.5, 0.5 ); + private boolean isText = false; + private Font font; + transient private BasicStroke stroke = JDStrokes.PLAIN_ROUND.getStroke(); + + // constructors + public JDraftingShape() {} + + public JDraftingShape( Shape shape, Color color, Color fill, BasicStroke stroke ) + { + this.shape = shape; + this.color = color; + this.fill = fill; + this.stroke = stroke; + name = toString(); + } + + public JDraftingShape( String name, String description, Shape shape, Color color, Color fill, + BasicStroke stroke ) + { + this( shape, color, fill, stroke ); + this.name = name; + this.description = description; + } + + public JDraftingShape( JDraftingShape other ) + { + shape = other.getShape(); + name = other.name; + description = other.description; + stroke = other.stroke; + color = other.color; + fill = other.fill; + font = other.font; + isText = other.isText; + } + + // I/O methods + private void writeObject( ObjectOutputStream oos ) throws IOException + { + oos.defaultWriteObject(); + oos.writeFloat( stroke.getLineWidth() ); + oos.writeInt( stroke.getEndCap() ); + oos.writeInt( stroke.getLineJoin() ); + oos.writeFloat( stroke.getMiterLimit() ); + oos.writeObject( stroke.getDashArray() ); + oos.writeFloat( stroke.getDashPhase() ); + } + + private void readObject( ObjectInputStream ois ) throws IOException, ClassNotFoundException + { + ois.defaultReadObject(); + float lineWidth = ois.readFloat(); + int endCap = ois.readInt(); + int lineJoin = ois.readInt(); + float miterLimit = ois.readFloat(); + float[] dashArray = (float[]) ois.readObject(); + float dashPhase = ois.readFloat(); + stroke = new BasicStroke( lineWidth, endCap, lineJoin, miterLimit, dashArray, dashPhase ); + } + + // getters + public Shape getShape() { return shape; } + public String getName() { return name; } + public String getDescription() { return description; } + public BasicStroke getStroke() { return stroke; } + public Color getColor() { return color; } + public Color getFill() { return fill; } + public boolean isText() { return isText; } + public Font getFont() { return font; } + public Point2D getTextPosition() { return textPosition; } + + + // setters + public Shape setShape( Shape shape ) { this.shape = shape; return shape; } + public String setName( String name ) { this.name = name; return name; } + public String setDescription( String description ) + { + this.description = description; + return description; + } + public Stroke setStroke( BasicStroke stroke ) + { + this.stroke = stroke; + return stroke; + } + public Color setColor( Color color ) + { + this.color = color; + return color; + } + public void setFill( Color fill ) + { + this.fill = fill; + } + public void setAsText( boolean isText ) + { + this.isText = isText; + } + public void setFont( Font font ) + { + this.font = font; + } + public void setTextPosition( Point2D textPosition ) + { + this.textPosition = textPosition; + } + + // methods + public static JDraftingShape createFromIterator( PathIterator pit, String name, + String description, Color color, Color fill, BasicStroke stroke ) + { + final Path2D path = new Path2D.Double(); + path.append( pit, false ); + + return new JDraftingShape( name, description, path, color, fill, stroke ); + } + + /** + * Translate shape + * @param dx delta x + * @param dy delta y + * @return current shape translated + */ + public JDraftingShape move( double dx, double dy ) + { + setShape( AffineTransform.getTranslateInstance( dx, dy ) + .createTransformedShape( getShape() ) ); + return this; + } + + /** + * Vertex list from path iterator + * @return vertex list + */ + public List getVertex() + { + final List vertex = new ArrayList<>(); + + final PathIterator pit = getShape().getPathIterator( null ); + double[] coords = new double[6]; + while ( !pit.isDone() ) + { + int type = pit.currentSegment( coords ); + if ( type != PathIterator.SEG_CLOSE ) + vertex.add( new Point2D.Double( coords[0], coords[1] ) ); + pit.next(); + } + + return vertex; + } + + /** + * Segment list from path iterator + * @return segment list + */ + public List getSegments() + { + final List segments = new ArrayList<>(); + final PathIterator pit = getShape().getPathIterator( null ); + final double[] coords = new double[6], + open = new double[2]; + double oldX = Double.NaN, oldY = Double.NaN; + + while ( !pit.isDone() ) + { + switch( pit.currentSegment( coords ) ) + { + case PathIterator.SEG_LINETO: + segments.add( + new Line2D.Double( oldX, oldY, oldX = coords[0], oldY = coords[1] ) ); + break; + case PathIterator.SEG_MOVETO: + pit.currentSegment( open ); + oldX = coords[0]; + oldY = coords[1]; + break; + case PathIterator.SEG_QUADTO: + segments.add( + new Line2D.Double( oldX, oldY, oldX = coords[2], oldY = coords[3] ) ); + break; + case PathIterator.SEG_CUBICTO: + segments.add( + new Line2D.Double( oldX, oldY, oldX = coords[4], oldY = coords[5] ) ); + break; + case PathIterator.SEG_CLOSE: + segments.add( new Line2D.Double( oldX, oldY, open[0], open[1] ) ); + break; + + } + pit.next(); + } + + return segments; + } + + /** + * Check whether shape is closed + * @param vertex vertex list + * @return {@code true} whether shape is closed + */ + public boolean isClosed( List vertex ) + { + return vertex.get( 0 ).equals( vertex.get( vertex.size() - 1 ) ); + } + + /** + * Check whether shape is a point + * @param vertex vertex list + * @return {@code true} if so + */ + public boolean isPoint( List vertex ) + { + return getSides( vertex ) == 0; + } + + /** + * Check whether shape is a segment + * @param vertex vertex list + * @return {@code true} if so + */ + public boolean isSegment( List vertex ) + { + return getSides( vertex ) == 1; + } + + /** + * Number of sides of the shape + * @param vertex vertex list + * @return sides (0: point, 1: line) + */ + public int getSides( List vertex ) + { + return vertex.size() == 2 && isClosed( vertex ) + ? 0 // point + : vertex.size() - 1; + } + + /** + * Get suitable font size for text type + * @param g2 canvas graphics + * @param transform canvas transform + * @return closest font size + */ + public Font adjustFontSizeToBounds( Graphics2D g2, AffineTransform transform ) + { + // get text box bounds + final Shape transShape = transform.createTransformedShape( getShape() ); + final double boundsWidth = transShape.getBounds2D().getWidth(), + boundsHeight = transShape.getBounds2D().getHeight(); + + // split text description + final String[] lines = getDescription().split("\n"); + + // adjust temporary font size to box using binary search + Font tmpfont; + int minSize = 4, maxSize = font.getSize(); + while ( true ) + { + int midpointSize = (minSize + maxSize) / 2; + tmpfont = new Font( font.getFamily(), font.getStyle(), midpointSize ); + + // finish, closest font to rectangle + if ( Math.abs(maxSize - minSize) <= 1 ) + return tmpfont; + + // get temporary text bounds + final FontMetrics fm = g2.getFontMetrics( tmpfont ); + + Rectangle2D txtBounds = Stream.of( lines ) // max width line + .max( (s1,s2) -> Double.compare( fm.getStringBounds( s1, g2 ).getWidth(), + fm.getStringBounds( s2, g2 ).getWidth() ) ) + .map( s -> fm.getStringBounds( s, g2 ) ) + .get(); + + txtBounds = new Rectangle2D.Double( txtBounds.getX(), txtBounds.getY(), + txtBounds.getWidth(), midpointSize * lines.length ); + + // text inside rectangle + if ( boundsWidth >= txtBounds.getWidth() && boundsHeight >= txtBounds.getHeight() ) + minSize = midpointSize; + // overflow + else + maxSize = midpointSize; + } + } + + /** + * Short shape description text + * @return short description text + */ + public String toShortString() + { + return String.format( "[name=%s,description=%s]", + getName() != null && getName().length() > 0 + ? getName() + : "(unnamed)", + getDescription() != null && getDescription().length() > 0 + ? getDescription() + : "" ); + } + + @Override + public String toString() + { + final StringBuilder sb = new StringBuilder(); + + sb.append( "[name=" ) + .append( ( getName() != null && getName().length() > 0 ? getName() : "(unnamed)" ) ) + .append( getDescription() != null && getDescription().length() > 0 + ? ",description=" + getDescription() + : "" ) + .append( ",color=" ).append( getColor() ) + .append( ",fill=" ).append( elvis( getFill(), "" ) ) + .append( ",isText=" ).append( isText() ) + .append( ",font=" ).append( elvis( getFont(), "" ) ) + .append( ",length=" ).append( JDMath.length( getShape(), null ) ) + .append( ",path=" ).append( JDUtils.pathToString( getShape() ) ) + .append( "]" ); + + return sb.toString(); + } + + private static final long serialVersionUID = -3554942998890931450L; +} diff --git a/src/jdrafting/gui/Application.java b/src/jdrafting/gui/Application.java new file mode 100644 index 0000000..59a9db9 --- /dev/null +++ b/src/jdrafting/gui/Application.java @@ -0,0 +1,1655 @@ +package jdrafting.gui; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleMnemonic; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.BasicStroke; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Desktop; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.geom.PathIterator; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Calendar; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.Vector; + +import javax.swing.AbstractAction; +import javax.swing.AbstractButton; +import javax.swing.Action; +import javax.swing.ActionMap; +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.JScrollPane; +import javax.swing.JSpinner; +import javax.swing.JSplitPane; +import javax.swing.JToggleButton; +import javax.swing.JToolBar; +import javax.swing.JWindow; +import javax.swing.KeyStroke; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UIManager.LookAndFeelInfo; +import javax.swing.border.BevelBorder; +import javax.swing.event.UndoableEditEvent; +import javax.swing.event.UndoableEditListener; +import javax.swing.undo.AbstractUndoableEdit; +import javax.swing.undo.CompoundEdit; +import javax.swing.undo.UndoManager; +import javax.swing.undo.UndoableEdit; +import javax.swing.undo.UndoableEditSupport; + +import jdrafting.Exercise; +import jdrafting.geom.JDStrokes; +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.controller.actions.AboutAction; +import jdrafting.gui.controller.actions.AngleAction; +import jdrafting.gui.controller.actions.ArcAction; +import jdrafting.gui.controller.actions.AreaIntersectionAction; +import jdrafting.gui.controller.actions.AreaSubstractAction; +import jdrafting.gui.controller.actions.AreaSymmetricDifferenceAction; +import jdrafting.gui.controller.actions.AreaUnionAction; +import jdrafting.gui.controller.actions.ArrowAction; +import jdrafting.gui.controller.actions.AxialSymmetryAction; +import jdrafting.gui.controller.actions.BackwardAction; +import jdrafting.gui.controller.actions.BisectrixAction; +import jdrafting.gui.controller.actions.BoundsAction; +import jdrafting.gui.controller.actions.CanvasColorAction; +import jdrafting.gui.controller.actions.CapableArcAction; +import jdrafting.gui.controller.actions.CentralSymmetryAction; +import jdrafting.gui.controller.actions.CircumferenceAction; +import jdrafting.gui.controller.actions.CopySelectedAction; +import jdrafting.gui.controller.actions.DeleteSelectedAction; +import jdrafting.gui.controller.actions.DivisionPointsAction; +import jdrafting.gui.controller.actions.EllipseAction; +import jdrafting.gui.controller.actions.EndAction; +import jdrafting.gui.controller.actions.ExerciseMetadataAction; +import jdrafting.gui.controller.actions.ExitAction; +import jdrafting.gui.controller.actions.ExtremesAction; +import jdrafting.gui.controller.actions.EyedropperAction; +import jdrafting.gui.controller.actions.ForwardAction; +import jdrafting.gui.controller.actions.FragmentAction; +import jdrafting.gui.controller.actions.FreeHandAction; +import jdrafting.gui.controller.actions.FusionAction; +import jdrafting.gui.controller.actions.HomothetyAction; +import jdrafting.gui.controller.actions.HyperbolaAction; +import jdrafting.gui.controller.actions.IntersectionsAction; +import jdrafting.gui.controller.actions.InvertSelectionAction; +import jdrafting.gui.controller.actions.LookFeelAction; +import jdrafting.gui.controller.actions.MathFunctionAction; +import jdrafting.gui.controller.actions.MediatrixAction; +import jdrafting.gui.controller.actions.MidpointAction; +import jdrafting.gui.controller.actions.ModifySegmentAction; +import jdrafting.gui.controller.actions.MoveZBufferAction; +import jdrafting.gui.controller.actions.NewAction; +import jdrafting.gui.controller.actions.OpenAction; +import jdrafting.gui.controller.actions.PaintAction; +import jdrafting.gui.controller.actions.ParabolaAction; +import jdrafting.gui.controller.actions.ParallelAction; +import jdrafting.gui.controller.actions.PasteAction; +import jdrafting.gui.controller.actions.PasteStyleAction; +import jdrafting.gui.controller.actions.PerpendicularAction; +import jdrafting.gui.controller.actions.PointAction; +import jdrafting.gui.controller.actions.PointColorAction; +import jdrafting.gui.controller.actions.PolyLineAction; +import jdrafting.gui.controller.actions.PolygonAction; +import jdrafting.gui.controller.actions.PrintAction; +import jdrafting.gui.controller.actions.ProtractorAction; +import jdrafting.gui.controller.actions.RectangleAction; +import jdrafting.gui.controller.actions.RedoAction; +import jdrafting.gui.controller.actions.RegularPolygonAction; +import jdrafting.gui.controller.actions.RewindAction; +import jdrafting.gui.controller.actions.RotationAction; +import jdrafting.gui.controller.actions.RulerAction; +import jdrafting.gui.controller.actions.SaveAction; +import jdrafting.gui.controller.actions.SaveImageAction; +import jdrafting.gui.controller.actions.SegmentAction; +import jdrafting.gui.controller.actions.SelectAllAction; +import jdrafting.gui.controller.actions.SelectionAction; +import jdrafting.gui.controller.actions.ShapeColorAction; +import jdrafting.gui.controller.actions.ShapeFillAction; +import jdrafting.gui.controller.actions.SplineAction; +import jdrafting.gui.controller.actions.TextBoxAction; +import jdrafting.gui.controller.actions.TextVisibleAction; +import jdrafting.gui.controller.actions.TranslationAction; +import jdrafting.gui.controller.actions.TriangleAction; +import jdrafting.gui.controller.actions.TrianglePointsAction; +import jdrafting.gui.controller.actions.UndoAction; +import jdrafting.gui.controller.actions.UndoRedoHistoryAction; +import jdrafting.gui.controller.actions.VertexAction; +import jdrafting.gui.controller.actions.ZoomAllAction; +import jdrafting.gui.controller.actions.ZoomInOutAction; +import jdrafting.gui.controller.mouse.HandListener; +import jdrafting.gui.controller.mouse.TrianglePointsListener; + +/** + * {@value #APPNAME} GUI class + * @author {@value #AUTHOR}, {@value #COPYLEFT} + * @since 0.1.0 + * @version {@value #VERSION} + */ +@SuppressWarnings("serial") +public class Application extends JFrame +{ + ////////////////////// + // STATIC CONSTANTS // + ////////////////////// + // metainfo + public static final String APPNAME = "JDrafting"; + public static final String VERSION = "0.1.12"; + public static final String AUTHOR = "Miguel Alejandro Moreno Barrientos"; + public static final String COPYLEFT = "2016,2020,2021"; + public static final String PROJECT_PAGE = + "http://miguelalejandromorenobarrientos.github.io/jdrafting"; + public static final String GITHUB_REPOSITORY = + "https://github.com/miguelalejandromorenobarrientos/jdrafting/tree/master"; + // colors + public static final String HTML_SHAPE_NAMES_COL = "#22DD22"; + // separators + private static final Dimension HSEP = new Dimension( 12, 0 ); + private static final Dimension VSEP = new Dimension( 0, 12 ); + + ///////////////// + // STATIC VARS // + ///////////////// + public static Color toolMainColor = Color.BLUE; + public static Locale locale = Locale.getDefault(); + public static String lookAndFeelClassName = UIManager.getSystemLookAndFeelClassName(); + public static boolean jmeEnabled = true; + + + /////////////////// + // INSTANCE VARS // + /////////////////// + private ActionMap actionMap = new ActionMap(); + private Exercise exercise; // current exercise + private Set selectedShapes; // set of selected shapes + private double angle = 90.; // angle for some tools (arc,rotations,...) + private double distance = 0.1; // ruler distance + private boolean useDistance = false; // use distance for some tools + private double flatnessValue = 10000.; // flatness for curved shapes + private String saveFilename; // current filename + private boolean visibleNames = true; // show shape names + private JDraftingShape[] innerClipboard = null; // copied shapes to paste + // style + private Color color = Color.BLACK, + fill = null, + pointColor = Color.DARK_GRAY; + private BasicStroke stroke = JDStrokes.PLAIN_ROUND.getStroke(); + private BasicStroke pointStroke = + new BasicStroke( 8f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ); + // undo/redo + public UndoManager undoManager; + public UndoableEditSupport undoRedoSupport; + // GUI components + public JWindow undoRedoWindow; + public Container contentPanel; + public JPanel centerPanel; + public JPanel northPanel; + public JPanel statusPanel; + public CanvasPanel canvas; + public JMenuBar menubar; + public JMenu menuFile; + public JMenu menuEdit; + public JMenu menuStyle; + public JMenu menuShapes; + public JMenu menuExercise; + public JMenu menuTools; + public JMenu menuTransform; + public JMenu menuTrianglePoints; + public JMenu menuPolygon; + public JMenu menuConics; + public JMenu menuView; + public JMenu menuAppearance; + public JMenu menuHelp; + public JToolBar styleToolbar,rulerProtToolbar, actionbar,shapebar,toolbar; + public JSpinner spinThickness, spinPointThickness, spinAngle; + public JButton buttonEyedropper, buttonPasteStyle; + public JButton buttonColor, buttonFill, buttonPointColor; + public JButton buttonRuler, buttonProtactor; + public JButton btnHistory; + public JCheckBox checkRuler; + public JButton buttonMultiplier; + public JComboBox comboLineStyle; + public JToggleButton toggleNames; + public JLabel labelStatus; + public ShapeList shapeList; + public JScrollPane scrollList; + public JSplitPane splitPanel; + public Toast currentToast; + + + ///////////////// + // CONSTRUCTOR // + ///////////////// + public Application() + { + // easter egg joke + final int year = Calendar.getInstance().get( Calendar.YEAR ); + if ( year >= 2030 ) + System.out.println( "\n\tYeah! My program still running in " + year + "!!\n" ); + + // create app window + initUI(); + createActions(); + canvas.requestFocus(); + setExtendedState( getExtendedState() | JFrame.MAXIMIZED_BOTH ); + + // start new exercise + setExercise( new Exercise() ); + + // set default Look and Feel + JFrame.setDefaultLookAndFeelDecorated( false ); + JDialog.setDefaultLookAndFeelDecorated( false ); + + try + { + UIManager.setLookAndFeel( lookAndFeelClassName ); + } + catch ( Exception e ) + { + System.out.println( e ); + System.exit( -1 ); + } + SwingUtilities.updateComponentTreeUI( this ); + + // exit app + setDefaultCloseOperation( DO_NOTHING_ON_CLOSE ); + addWindowListener( new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) + { + Application.this.exit(); + } + } ); + } + + + ///////// + // GUI // + ///////// + + /** + * Initialize GUI components + */ + private void initUI() + { + // config frame + setIconImage( getSmallIcon( "jdrafting.png" ).getImage() ); + setPreferredSize( JDUtils.getScreenSize( 0.8f, 0.8f ) ); + + // --- MENUBAR + setJMenuBar( menubar = new JMenuBar() ); + // menu File + menubar.add( menuFile = new JMenu( getLocaleText( "file" ) ) ); + menuFile.setMnemonic( getLocaleMnemonic( "mne_menu_file" ) ); + // menu Edit + menubar.add( menuEdit = new JMenu( getLocaleText( "edit" ) ) ); + menuEdit.setMnemonic( getLocaleMnemonic( "mne_menu_edit" ) ); + // menu Style + menubar.add( menuStyle = new JMenu( getLocaleText( "style" ) ) ); + menuStyle.setMnemonic( getLocaleMnemonic( "mne_menu_style" ) ); + // menu Shapes + menubar.add( menuShapes = new JMenu( getLocaleText( "shapes" ) ) ); + menuShapes.setMnemonic( getLocaleMnemonic( "mne_menu_shapes" ) ); + // menu Tools + menubar.add( menuTools = new JMenu( getLocaleText( "tools" ) ) ); + menuTools.setMnemonic( getLocaleMnemonic( "mne_menu_tools" ) ); + // submenu transform + menuTools.add( menuTransform = + new JMenu( getLocaleText( "transforms" ) ) ); + menuTransform.setMnemonic( + getLocaleMnemonic( "mne_menu_transform" ) ); + menuTools.addSeparator(); + // menu Exercise + menubar.add( menuExercise = new JMenu( getLocaleText( "exercise" ) ) ); + menuExercise.setMnemonic( getLocaleMnemonic( "mne_menu_exercise" ) ); + // menu View + menubar.add( menuView = new JMenu( getLocaleText( "view" ) ) ); + menuView.setMnemonic( KeyEvent.VK_V ); + // submenu Appearance + menuView.add( menuAppearance = + new JMenu( getLocaleText( "appearance" ) ) ); + menuAppearance.setToolTipText( "GUI Theme" ); + menuAppearance.setMnemonic( + getLocaleMnemonic( "mne_menu_appea" ) ); + menuView.addSeparator(); + // menu Help + menubar.add( menuHelp = new JMenu( getLocaleText( "help" ) ) ); + menuHelp.setMnemonic( getLocaleMnemonic( "mne_menu_help" ) ); + + // --- PANELS + // content panel + contentPanel = getContentPane(); + // center panel + contentPanel.add( centerPanel = new JPanel( new BorderLayout() ), + BorderLayout.CENTER ); + // north panel + contentPanel.add( northPanel = new JPanel(), BorderLayout.NORTH ); + northPanel.setLayout( + new BoxLayout( northPanel, BoxLayout.LINE_AXIS ) ); + JPanel auxPanel = new JPanel(); + northPanel.add( auxPanel ); + auxPanel.setBorder( + BorderFactory.createBevelBorder( BevelBorder.RAISED ) ); + auxPanel.setLayout( + new BoxLayout( auxPanel, BoxLayout.LINE_AXIS ) ); + // style panel + styleToolbar = new JToolBar( getLocaleText( "tit_style" ) ); + styleToolbar.addHierarchyListener( + (evt) -> Application.this.revalidate() ); + auxPanel.add( styleToolbar ); + styleToolbar.setLayout( + new BoxLayout( styleToolbar, BoxLayout.LINE_AXIS ) ); + styleToolbar.setBorder( BorderFactory.createTitledBorder( + getLocaleText( "tit_style" ) ) ); + // ruler & protractor panel + rulerProtToolbar = + new JToolBar( getLocaleText( "ruler_prot" ) ); + rulerProtToolbar.addHierarchyListener( + (evt) -> Application.this.revalidate() ); + auxPanel.add( rulerProtToolbar ); + rulerProtToolbar.setLayout( + new BoxLayout( rulerProtToolbar, BoxLayout.LINE_AXIS ) ); + rulerProtToolbar.setBorder( BorderFactory.createTitledBorder( + getLocaleText( "ruler_prot" ) ) ); + // status panel + contentPanel.add( + statusPanel = new JPanel( new FlowLayout( FlowLayout.LEFT ) ), + BorderLayout.SOUTH ); + statusPanel.setBorder( new BevelBorder( BevelBorder.LOWERED ) ); + + // --- TOOLBARS + // actionbar + centerPanel.add( actionbar = new JToolBar( getLocaleText( "actions" ) ), + BorderLayout.NORTH ); + // shapebar + centerPanel.add( shapebar = new JToolBar( getLocaleText( "shapes" ), + JToolBar.VERTICAL ), + BorderLayout.WEST ); + // toolbar + centerPanel.add( toolbar = new JToolBar( getLocaleText( "tools" ) ), + BorderLayout.SOUTH ); + + // --- STYLE PANEL + styleToolbar.add( Box.createHorizontalStrut( 3 ) ); + // eyedropper button + styleToolbar.add( buttonEyedropper = new JButton() ); + styleToolbar.add( Box.createHorizontalStrut( 3 ) ); + // paste style button + styleToolbar.add( buttonPasteStyle = new JButton() ); + styleToolbar.add( Box.createHorizontalStrut( 12 ) ); + // jlabel line thickness + JLabel labelThickness = new JLabel( getLocaleText( "thickness" ), JLabel.RIGHT ); + labelThickness.setMaximumSize( labelThickness.getPreferredSize() ); + styleToolbar.add( labelThickness ); + styleToolbar.add( Box.createHorizontalStrut( 3 ) ); + // line thikness spinner + styleToolbar.add( spinThickness = new JSpinner( new SpinnerNumberModel( + getStroke().getLineWidth(), .1, Double.POSITIVE_INFINITY, 1. ) ) ); + spinThickness.setPreferredSize( new Dimension( 60, 30 ) ); + spinThickness.setMinimumSize( spinThickness.getPreferredSize() ); + spinThickness.setMaximumSize( spinThickness.getPreferredSize() ); + spinThickness.addChangeListener( evt -> { + float thickness = (float) (double) spinThickness.getModel().getValue(); + BasicStroke bs = (BasicStroke) comboLineStyle.getSelectedItem(); + setStroke( JDStrokes.getStroke( bs, thickness ) ); + }); + styleToolbar.add( Box.createHorizontalStrut( 3 ) ); + // line color button + styleToolbar.add( buttonColor = new JButton() { + @Override + protected void paintComponent( Graphics g ) { + super.paintComponent( g ); + g.setColor( getColor() ); + g.fillRect( 0, 0, getWidth(), getHeight() ); + } + } ); + buttonColor.setPreferredSize( new Dimension( 30, 30 ) ); + buttonColor.setMinimumSize( buttonColor.getPreferredSize() ); + buttonColor.setMaximumSize( buttonColor.getPreferredSize() ); + styleToolbar.add( Box.createHorizontalStrut( 3 ) ); + // jcombobox line style + styleToolbar.add( comboLineStyle = new LineStyleComboBox( this ) ); + comboLineStyle.setPreferredSize( new Dimension( 200, 30 ) ); + comboLineStyle.setMinimumSize( new Dimension( 100, 30 ) ); + comboLineStyle.setMaximumSize( comboLineStyle.getPreferredSize() ); + styleToolbar.add( Box.createHorizontalStrut( 12 ) ); + // fill button + JLabel labelFill = new JLabel( getLocaleText( "lbl_fill" ), JLabel.RIGHT ); + labelFill.setMaximumSize( labelFill.getPreferredSize() ); + styleToolbar.add( labelFill ); + styleToolbar.add( Box.createHorizontalStrut( 3 ) ); + styleToolbar.add( buttonFill = new JButton() { + @Override + protected void paintComponent( Graphics g ) { + super.paintComponent( g ); + if ( getFill() == null ) // no fill, draw gray chess-board + for ( int n = 5, step = getWidth() / n, i = 0; i < n; i++ ) + for ( int j = 0; j < n; j++ ) + { + g.setColor( ((i+j)&1) == 0 ? Color.GRAY : Color.DARK_GRAY ); + g.fillRect( i*step, j*step, step, step ); + } + else + { + g.setColor( getFill() ); + g.fillRect( 0, 0, getWidth(), getHeight() ); + } + } + } ); + buttonFill.setPreferredSize( new Dimension( 30, 30 ) ); + buttonFill.setMinimumSize( buttonFill.getPreferredSize() ); + buttonFill.setMaximumSize( buttonFill.getPreferredSize() ); + styleToolbar.add( Box.createHorizontalStrut( 3 ) ); + // jlabel point thickness + JLabel labelPoint = new JLabel( getLocaleText( "point_thickness" ), JLabel.RIGHT ); + labelPoint.setMaximumSize( labelPoint.getPreferredSize() ); + styleToolbar.add( labelPoint ); + styleToolbar.add( Box.createHorizontalStrut( 3 ) ); + // point thickness spinner + styleToolbar.add( spinPointThickness = new JSpinner( + new SpinnerNumberModel( getPointStroke().getLineWidth(), .1, + Double.POSITIVE_INFINITY, 1. ) ) ); + spinPointThickness.setPreferredSize( new Dimension( 60, 30 ) ); + spinPointThickness.setMinimumSize( + spinPointThickness.getPreferredSize() ); + spinPointThickness.setMaximumSize( + spinPointThickness.getPreferredSize() ); + spinPointThickness.addChangeListener( evt -> { + float thickness = + (float) (double) spinPointThickness.getModel().getValue(); + BasicStroke bs = getPointStroke(); + setPointStroke( new BasicStroke( thickness, bs.getEndCap(), + bs.getLineJoin(), bs.getMiterLimit(), bs.getDashArray(), + bs.getDashPhase() ) ); + }); + styleToolbar.add( Box.createHorizontalStrut( 3 ) ); + // point color button + styleToolbar.add( buttonPointColor = new JButton() { + @Override + protected void paintComponent( Graphics g ) { + super.paintComponent( g ); + g.setColor( getPointColor() ); + g.fillRect( 0, 0, getWidth(), getHeight() ); + } + }); + JDUtils.fixSize( buttonPointColor, 30, 30 ); + styleToolbar.add( Box.createHorizontalStrut( 3 ) ); + + // --- RULER & PROTRACTOR PANEL + rulerProtToolbar.add( Box.createHorizontalStrut( 3 ) ); + // ruler button + rulerProtToolbar.add( buttonRuler = new JButton() ); + rulerProtToolbar.add( Box.createHorizontalStrut( 3 ) ); + // checkbox fixed distance + rulerProtToolbar.add( checkRuler = new JCheckBox( getLocaleText( "fix_dist" ) ) ); + checkRuler.setSelected( isUsingRuler() ); + checkRuler.setMaximumSize( checkRuler.getPreferredSize() ); + checkRuler.setMinimumSize( checkRuler.getPreferredSize() ); + checkRuler.addActionListener( evt -> setUseDistance( checkRuler.isSelected() ) ); + // multiplier button + rulerProtToolbar.add( buttonMultiplier = + new JButton( "xN" ) ); + buttonMultiplier.setMaximumSize( buttonMultiplier.getPreferredSize() ); + buttonMultiplier.setToolTipText( getLocaleText( "dist_mult_dlg" ) ); + buttonMultiplier.addActionListener( evt -> { + String result = (String) JOptionPane.showInputDialog( + this, getLocaleText( "dist_mult_dlg" ), "xN", + JOptionPane.QUESTION_MESSAGE, + getLargeIcon( "ruler.png" ), null, "1" ); + if ( result == null ) return; + try + { + setDistance( getDistance() * Double.valueOf( result ) ); + } + catch ( NumberFormatException e ) + { + JOptionPane.showMessageDialog( + this, e, "xN", JOptionPane.ERROR_MESSAGE ); + } + } ); + rulerProtToolbar.add( Box.createHorizontalStrut( 12 ) ); + // protractor button + rulerProtToolbar.add( buttonProtactor = new JButton() ); + // spinner angle + rulerProtToolbar.add( spinAngle = new JSpinner( new SpinnerNumberModel( + getAngle(), 0., Math.nextDown( 360. ), 1. ) ) ); + JDUtils.fixSize( spinAngle, 70, 30 ); + spinAngle.addFocusListener( new FocusAdapter() { + @Override + public void focusLost(FocusEvent e) + { + setAngle( (double) spinAngle.getModel().getValue() ); + } + }); + spinAngle.addKeyListener( new KeyAdapter() { + @Override + public void keyTyped(KeyEvent e) + { + setAngle( (double) spinAngle.getModel().getValue() ); + } + }); + spinAngle.addChangeListener( evt -> setAngle( (double) spinAngle.getModel().getValue() ) ); + rulerProtToolbar.add( new JLabel( getLocaleText( "degrees" ) ) ); + // final glue + northPanel.add( Box.createHorizontalGlue() ); + + // --- STATUS PANEL + // status label + statusPanel.add( labelStatus = new JLabel( " " ) ); + labelStatus.setFont( new Font( "serif", Font.ITALIC, 16 ) ); + + // --- CENTER PANEL + // canvas + canvas = new CanvasPanel( this, new Viewport() ); + canvas.getInputMap( JPanel.WHEN_IN_FOCUSED_WINDOW ).put( + KeyStroke.getKeyStroke( "ESCAPE" ), "cancel" ); + canvas.getActionMap().put( "cancel", new AbstractAction() { + @Override + public void actionPerformed( ActionEvent e ) + { + canvas.setCanvasListener( new HandListener( canvas ) ); + canvas.repaint(); + } + } ); + // shape list + scrollList = new JScrollPane( shapeList = new ShapeList( this ) ); + // add split panel with scroll list and canvas to center + splitPanel = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, + canvas, scrollList ); + splitPanel.setOneTouchExpandable( true ); + splitPanel.setDividerSize( 12 ); + splitPanel.setResizeWeight( 1. ); // extra space to canvas on resize + centerPanel.add( splitPanel, BorderLayout.CENTER ); + + // pack frame and set location + pack(); + setLocationRelativeTo( null ); // center window + } + + /** + * Define and place GUI actions + */ + private void createActions() + { + Action action; + + // New exercise + menuFile.add( registerAction( new NewAction( this ) ) ); + // Open exercise + menuFile.add( registerAction( new OpenAction( this ) ) ); + menuFile.addSeparator(); + // Save exercise + menuFile.add( registerAction( new SaveAction( this, false ) ) ); + // Save as ... + menuFile.add( registerAction( new SaveAction( this, true ) ) ); + // Save exercise as image + menuFile.add( registerAction( new SaveImageAction( this ) ) ); + menuFile.addSeparator(); + // Print as image + menuFile.add( registerAction( new PrintAction( this ) ) ); + menuFile.addSeparator(); + // Exit + menuFile.add( registerAction( new ExitAction( this ) ) ); + // Undo/Redo + menuEdit.add( registerAction( new UndoAction( this ) ) ); + registerAction( new UndoRedoHistoryAction( this ) ); + menuEdit.add( registerAction( new RedoAction( this ) ) ); + menuEdit.addSeparator(); + // Rectangular selection + menuEdit.add( registerAction( new SelectionAction( this ) ) ); + // Invert selection + menuEdit.add( registerAction( new SelectAllAction( this ) ) ); + // Invert selection + menuEdit.add( registerAction( new InvertSelectionAction( this ) ) ); + menuEdit.addSeparator(); + // Up shape + menuEdit.add( registerAction( new MoveZBufferAction( this, true ) ) ); + // Down shape + menuEdit.add( registerAction( new MoveZBufferAction( this, false ) ) ); + menuEdit.addSeparator(); + // Copy + menuEdit.add( registerAction( new CopySelectedAction( this ) ) ); + // Paste + menuEdit.add( registerAction( new PasteAction( this ) ) ); + // Delete + menuEdit.add( registerAction( new DeleteSelectedAction( this ) ) ); + // Zoom all + menuView.add( registerAction( new ZoomAllAction( this ) ) ); + // Zoom in + menuView.add( registerAction( new ZoomInOutAction( this, true ) ) ); + // Zoom out + menuView.add( registerAction( new ZoomInOutAction( this, false ) ) ); + menuView.addSeparator(); + // Visible names + menuView.add( registerAction( new TextVisibleAction( this ) ) ); + menuView.addSeparator(); + // See/Hide toolbars + List buttonList = new LinkedList<>(); + JCheckBoxMenuItem checkMenuItemStyleBar = new JCheckBoxMenuItem( + getLocaleText( "item_style" ), styleToolbar.isVisible() ); + checkMenuItemStyleBar.addActionListener( new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) + { + styleToolbar.setVisible( checkMenuItemStyleBar.isSelected() ); + } + }); + menuView.add( checkMenuItemStyleBar ); + buttonList.add( checkMenuItemStyleBar ); + JCheckBoxMenuItem checkMenuItemRulerProtBar = new JCheckBoxMenuItem( + getLocaleText( "item_ruler_prot" ), rulerProtToolbar.isVisible() ); + checkMenuItemRulerProtBar.addActionListener( new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) + { + rulerProtToolbar.setVisible( + checkMenuItemRulerProtBar.isSelected() ); + } + }); + menuView.add( checkMenuItemRulerProtBar ); + buttonList.add( checkMenuItemRulerProtBar ); + JCheckBoxMenuItem checkMenuItemStatusBar = new JCheckBoxMenuItem( + getLocaleText( "item_status" ), statusPanel.isVisible() ); + checkMenuItemStatusBar.addActionListener( new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) + { + statusPanel.setVisible( checkMenuItemStatusBar.isSelected() ); + } + }); + menuView.add( checkMenuItemStatusBar ); + buttonList.add( checkMenuItemStatusBar ); + JCheckBoxMenuItem checkMenuItemActionBar = new JCheckBoxMenuItem( + getLocaleText( "item_action" ), actionbar.isVisible() ); + checkMenuItemActionBar.addActionListener( new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) + { + actionbar.setVisible( checkMenuItemActionBar.isSelected() ); + } + }); + menuView.add( checkMenuItemActionBar ); + buttonList.add( checkMenuItemActionBar ); + JCheckBoxMenuItem checkMenuItemShapebar = new JCheckBoxMenuItem( + getLocaleText( "item_shape" ), shapebar.isVisible() ); + checkMenuItemShapebar.addActionListener( new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) + { + shapebar.setVisible( checkMenuItemShapebar.isSelected() ); + } + }); + menuView.add( checkMenuItemShapebar ); + buttonList.add( checkMenuItemShapebar ); + JCheckBoxMenuItem checkMenuItemToolbar = new JCheckBoxMenuItem( + getLocaleText( "item_tool" ), toolbar.isVisible() ); + checkMenuItemToolbar.addActionListener( new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) + { + toolbar.setVisible( checkMenuItemToolbar.isSelected() ); + } + }); + menuView.add( checkMenuItemToolbar ); + buttonList.add( checkMenuItemToolbar ); + // Show/Hide all + menuView.add( new AbstractAction() { + { + putValue( Action.NAME, getLocaleText( "hide_all" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( + KeyEvent.VK_E, InputEvent.SHIFT_MASK ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + for ( AbstractButton b : buttonList ) + if ( b.isSelected() ) + { + b.doClick(); + b.setSelected( false ); + } + } + }); + menuView.add( new AbstractAction() { + { + putValue( Action.NAME, getLocaleText( "show_all" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( + KeyEvent.VK_S, InputEvent.SHIFT_MASK ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + for ( AbstractButton b : buttonList ) + if ( !b.isSelected() ) + { + b.doClick(); + b.setSelected( true ); + } + } + }); + // Point shape + menuShapes.add( registerAction( new PointAction( this ) ) ); + // Segment shape + menuShapes.add( registerAction( new SegmentAction( this ) ) ); + // Arc shape + menuShapes.add( registerAction( new ArcAction( this ) ) ); + // Circumference shape + menuShapes.add( registerAction( new CircumferenceAction( this ) ) ); + // Angle shape + menuShapes.add( registerAction( new AngleAction( this ) ) ); + // Arrow shape + menuShapes.add( registerAction( new ArrowAction( this ) ) ); + menuShapes.addSeparator(); + // Triangle notable points menu + menuShapes.add( menuTrianglePoints = new JMenu( getLocaleText( "triangle_tools" ) ) ); + menuTrianglePoints.setMnemonic( getLocaleMnemonic( "mne_menu_trian" ) ); + menuTrianglePoints.setIcon( getSmallIcon( "triangle_popup.png" ) ); + // Triangle shape + menuTrianglePoints.add( registerAction( new TriangleAction( this ) ) ); + menuTrianglePoints.addSeparator(); + // Triangle notable points + menuTrianglePoints.add( registerAction( new TrianglePointsAction( + this, TrianglePointsListener.BARICENTER ) ) ); + menuTrianglePoints.add( registerAction( new TrianglePointsAction( + this, TrianglePointsListener.CIRCUMCENTER ) ) ); + menuTrianglePoints.add( registerAction( new TrianglePointsAction( + this, TrianglePointsListener.INCENTER ) ) ); + menuTrianglePoints.add( registerAction( new TrianglePointsAction( + this, TrianglePointsListener.ORTOCENTER ) ) ); + // Rectangle shape + menuShapes.add( registerAction( new RectangleAction( this ) ) ); + // Polygon menu + menuShapes.add( menuPolygon = new JMenu( getLocaleText( "polygon_tools" ) ) ); + menuPolygon.setMnemonic( getLocaleMnemonic( "mne_menu_poly" ) ); + menuPolygon.setIcon( getSmallIcon( "polygon_popup.png" ) ); + // Regular polygon shape + menuPolygon.add( registerAction( new RegularPolygonAction( this ) ) ); + // Polygon shape + menuPolygon.add( registerAction( new PolygonAction( this ) ) ); + // Polyline shape + menuPolygon.add( registerAction( new PolyLineAction( this ) ) ); + // Conics menu + menuShapes.add( menuConics = new JMenu( getLocaleText( "conics" ) ) ); + menuConics.setMnemonic( getLocaleMnemonic( "mne_menu_conics" ) ); + menuConics.setIcon( getSmallIcon( "conics.png" ) ); + // Circumference shape (repeated) + menuConics.add( new CircumferenceAction( this ) ); + // Ellipse shape + menuConics.add( registerAction( new EllipseAction( this ) ) ); + // Parabola shape + menuConics.add( registerAction( new ParabolaAction( this ) ) ); + // Hyperbola shape + menuConics.add( registerAction( new HyperbolaAction( this ) ) ); + menuShapes.addSeparator(); + // Spline shape + menuShapes.add( registerAction( new SplineAction( this ) ) ); + // Free hand shape + menuShapes.add( registerAction( new FreeHandAction( this ) ) ); + menuShapes.addSeparator(); + // Text box + menuShapes.add( registerAction( new TextBoxAction( this ) ) ); + // Function shape + if ( jmeEnabled ) + { + menuShapes.addSeparator(); + menuShapes.add( registerAction( new MathFunctionAction( this ) ) ); + } + // Translation transform + menuTransform.add( registerAction( new TranslationAction( this ) ) ); + // Rotation transform + menuTransform.add( registerAction( new RotationAction( this ) ) ); + // Homothety transform + menuTransform.add( registerAction( new HomothetyAction( this ) ) ); + // Central symmetry transform + menuTransform.add( registerAction( new CentralSymmetryAction( this ) ) ); + // Axial symmetry transform + menuTransform.add( registerAction( new AxialSymmetryAction( this ) ) ); + // Perpendicular tool + menuTools.add( registerAction( new PerpendicularAction( this ) ) ); + // Parallel tool + menuTools.add( registerAction( new ParallelAction( this ) ) ); + menuTools.addSeparator(); + // Mediatrix tool + menuTools.add( registerAction( new MediatrixAction( this ) ) ); + // Bisectrix tool + menuTools.add( registerAction( new BisectrixAction( this ) ) ); + // Capable arc tool + menuTools.add( registerAction( new CapableArcAction( this ) ) ); + menuTools.addSeparator(); + // Modify segment tool + menuTools.add( registerAction( new ModifySegmentAction( this ) ) ); + menuTools.addSeparator(); + // Midpoint tool + menuTools.add( registerAction( new MidpointAction( this ) ) ); + // Vertex tool + menuTools.add( registerAction( new VertexAction( this ) ) ); + // Extremes tool + menuTools.add( registerAction( new ExtremesAction( this ) ) ); + // Divisions points tool + menuTools.add( registerAction( new DivisionPointsAction( this ) ) ); + // Intersections tool + menuTools.add( registerAction( new IntersectionsAction( this ) ) ); + // Rectangle bounds tool + menuTools.add( registerAction( new BoundsAction( this ) ) ); + menuTools.addSeparator(); + // Fragment shape tool + menuTools.add( registerAction( new FragmentAction( this ) ) ); + // Fusion shape tool + menuTools.add( registerAction( new FusionAction( this ) ) ); + menuTools.addSeparator(); + // Area intersection + menuTools.add( registerAction( new AreaIntersectionAction( this ) ) ); + // Area union + menuTools.add( registerAction( new AreaUnionAction( this ) ) ); + // Area substract + menuTools.add( registerAction( new AreaSubstractAction( this ) ) ); + // Area symmetric difference + menuTools.add( registerAction( new AreaSymmetricDifferenceAction( this ) ) ); + menuTools.addSeparator(); + // Paint shape tool + menuTools.add( registerAction( new PaintAction( this ) ) ); + // Select shape color + menuStyle.add( action = registerAction( new ShapeColorAction( this ) ) ); + buttonColor.setAction( action ); + buttonColor.setIcon( null ); + buttonColor.setText( "" ); + buttonColor.setContentAreaFilled( false ); + buttonColor.setBorderPainted( false ); + buttonColor.setFocusPainted( true ); + buttonColor.setOpaque( true ); + // Select shape fill + menuStyle.add( action = registerAction( new ShapeFillAction( this ) ) ); + buttonFill.setAction( action ); + buttonFill.setIcon( null ); + buttonFill.setText( "" ); + buttonFill.setContentAreaFilled( false ); + buttonFill.setBorderPainted( false ); + buttonFill.setFocusPainted( true ); + buttonFill.setOpaque( true ); + // Select point color + menuStyle.add( action = registerAction( new PointColorAction( this ) ) ); + buttonPointColor.setAction( action ); + buttonPointColor.setIcon( null ); + buttonPointColor.setText( "" ); + buttonPointColor.setContentAreaFilled( false ); + buttonPointColor.setBorderPainted( false ); + buttonPointColor.setFocusPainted( true ); + buttonPointColor.setOpaque( true ); + // Canvas background color + menuStyle.add( registerAction( new CanvasColorAction( this ) ) ); + // Eyedropper + buttonEyedropper.setAction( action = registerAction( new EyedropperAction( this ) ) ); + buttonEyedropper.setText( "" ); + menuStyle.addSeparator(); + menuStyle.add( action ); + // Paste style + buttonPasteStyle.setAction( action = registerAction( new PasteStyleAction( this ) ) ); + buttonPasteStyle.setText( "" ); + menuStyle.add( action ); + // Ruler + buttonRuler.setAction( action = registerAction( new RulerAction( this ) ) ); + //buttonRuler.setPreferredSize( new Dimension( 36, 30 ) ); + buttonRuler.setContentAreaFilled( true ); + buttonRuler.setBorderPainted( false ); + buttonRuler.setFocusPainted( true ); + buttonRuler.setOpaque( false ); + buttonRuler.setText( "" ); + menuTools.addSeparator(); + menuTools.add( action ); + // Protractor + buttonProtactor.setAction( action = registerAction( new ProtractorAction( this ) ) ); + //buttonProtactor.setPreferredSize( new Dimension( 48, 30 ) ); + buttonProtactor.setContentAreaFilled( true ); + buttonProtactor.setBorderPainted( false ); + buttonProtactor.setFocusPainted( true ); + buttonProtactor.setOpaque( false ); + buttonProtactor.setText( "" ); + menuTools.add( action ); + // Rewind exercise + menuExercise.add( registerAction( new RewindAction( this ) ) ); + // Backward exercise + menuExercise.add( registerAction( new BackwardAction( this ) ) ); + // Forward exercise + menuExercise.add( registerAction( new ForwardAction( this ) ) ); + // End exercise + menuExercise.add( registerAction( new EndAction( this ) ) ); + menuExercise.addSeparator(); + // Exercise metadata + menuExercise.add( registerAction( new ExerciseMetadataAction( this ) ) ); + // Look & Feel + action = registerAction( new LookFeelAction( this, menuAppearance ) ); + LookAndFeelInfo[] laf = UIManager.getInstalledLookAndFeels(); + ButtonGroup group = new ButtonGroup(); + for ( LookAndFeelInfo lafi : laf ) + { + JRadioButtonMenuItem radioItem = new JRadioButtonMenuItem(); + radioItem.setAction( action ); + radioItem.setText( lafi.getName() ); + menuAppearance.add( radioItem ); + group.add( radioItem ); + if ( lookAndFeelClassName.equals( lafi.getClassName() ) ) + radioItem.setSelected( true ); + } + // Project page + menuHelp.add( new AbstractAction() { + private java.net.URI uri; + + { + try + { + uri = new URI( PROJECT_PAGE ); + putValue( Action.NAME, + "Project page: " + "" + uri.toString() + "" ); + putValue( Action.SMALL_ICON, + getSmallIcon( "homepage.png" ) ); + } + catch ( URISyntaxException e ) { System.err.println( e ); } + } + + @Override + public void actionPerformed(ActionEvent arg0) + { + if ( Desktop.isDesktopSupported() + && + Desktop.getDesktop().isSupported( Desktop.Action.BROWSE ) ) + try { Desktop.getDesktop().browse( uri ); } + catch ( IOException e ) { System.err.println( e ); } + } + } ); + // Github Repository + menuHelp.add( new AbstractAction() { + private java.net.URI uri; + + { + try + { + uri = new URI( GITHUB_REPOSITORY ); + putValue( Action.NAME, + "Github repository: " + "" + + uri.toString() + "" ); + putValue( Action.SMALL_ICON, + getSmallIcon( "github.png" ) ); + } + catch ( URISyntaxException e ) { System.err.println( e ); } + } + + @Override + public void actionPerformed(ActionEvent arg0) + { + if ( Desktop.isDesktopSupported() + && + Desktop.getDesktop().isSupported( Desktop.Action.BROWSE ) ) + try { Desktop.getDesktop().browse( uri ); } + catch ( IOException e ) { System.err.println( e ); } + } + } ); + menuHelp.addSeparator(); + // About... + menuHelp.add( registerAction( new AboutAction( this ) ) ); + + // --- TOOLBARS ACTIONS + // actionbar + actionbar.add( actionMap.get( getLocaleText( "new" ) ) ); + actionbar.add( actionMap.get( getLocaleText( "open" ) ) ); + actionbar.add( actionMap.get( getLocaleText( "save" ) ) ); + actionbar.add( actionMap.get( getLocaleText( "save_as" ) ) ); + actionbar.add( actionMap.get( getLocaleText( "save_image" ) ) ); + actionbar.add( actionMap.get( getLocaleText( "print" ) ) ); + actionbar.addSeparator( HSEP ); + actionbar.add( actionMap.get( getLocaleText( "fileinfo" ) ) ); + actionbar.addSeparator( HSEP ); + actionbar.add( actionMap.get( getLocaleText( "undo" ) ) ); + actionbar.add( btnHistory = new JButton( actionMap.get( "\u25BC" ) ) ); + actionbar.add( actionMap.get( getLocaleText( "redo" ) ) ); + actionbar.addSeparator( HSEP ); + actionbar.add( actionMap.get( getLocaleText( "move_up" ) ) ); + actionbar.add( actionMap.get( getLocaleText( "move_down" ) ) ); + actionbar.add( actionMap.get( getLocaleText( "copy" ) ) ); + actionbar.add( actionMap.get( getLocaleText( "paste" ) ) ); + actionbar.add( actionMap.get( getLocaleText( "delete" ) ) ); + actionbar.addSeparator( HSEP ); + actionbar.add( actionMap.get( getLocaleText( "color" ) ) ); + actionbar.add( actionMap.get( getLocaleText( "fill" ) ) ); + actionbar.add( actionMap.get( getLocaleText( "point_color" ) ) ); + actionbar.add( actionMap.get( getLocaleText( "background_color" ) ) ); + actionbar.addSeparator( HSEP ); + actionbar.add( actionMap.get( getLocaleText( "zoom_all" ) ) ); + actionbar.add( actionMap.get( getLocaleText( "zoom_in" ) ) ); + actionbar.add( actionMap.get( getLocaleText( "zoom_out" ) ) ); + actionbar.addSeparator( HSEP ); + JButton button; + final Color bColor = new Color( 44, 33, 22 ); // color of exercise player buttons + button = actionbar.add( actionMap.get( getLocaleText( "rewind" ) ) ); + button.setBackground( bColor ); + actionbar.add( Box.createHorizontalStrut( 2 ) ); + button = actionbar.add( actionMap.get( getLocaleText( "backward" ) ) ); + button.setBackground( bColor ); + actionbar.add( Box.createHorizontalStrut( 2 ) ); + button = actionbar.add( actionMap.get( getLocaleText( "forward" ) ) ); + button.setBackground( bColor ); + actionbar.add( Box.createHorizontalStrut( 2 ) ); + button = actionbar.add( actionMap.get( getLocaleText( "end" ) ) ); + button.setBackground( bColor ); + actionbar.add( Box.createHorizontalStrut( 2 ) ); + actionbar.addSeparator( HSEP ); + actionbar.add( toggleNames=new JToggleButton( actionMap.get( getLocaleText( "text" ) ) ) ); + toggleNames.setText( null ); + toggleNames.setSelected( isVisibleNames() ); + actionbar.addSeparator(); + actionbar.add( actionMap.get( getLocaleText( "select_all" ) ) ); + actionbar.add( actionMap.get( getLocaleText( "invert" ) ) ); + actionbar.addSeparator(); + actionbar.add( actionMap.get( getLocaleText( "exit" ) ) ); + // shapebar + shapebar.add( actionMap.get( getLocaleText( "selection" ) ) ); + shapebar.addSeparator( VSEP ); + shapebar.add( actionMap.get( getLocaleText( "point" ) ) ); + shapebar.add( actionMap.get( getLocaleText( "segment" ) ) ); + shapebar.add( actionMap.get( getLocaleText( "arc" ) ) ); + shapebar.add( actionMap.get( getLocaleText( "circumference" ) ) ); + shapebar.add( actionMap.get( getLocaleText( "angle" ) ) ); + shapebar.add( actionMap.get( getLocaleText( "arrow" ) ) ); + JButton trianglePopup = new JButton( new AbstractAction() { + @Override + public void actionPerformed( ActionEvent e ) + { + Component src = (Component) e.getSource(); + JPopupMenu popup = new JPopupMenu(); + popup.add( actionMap.get( getLocaleText( "triangle" ) ) ); + popup.addSeparator(); + popup.add( actionMap.get( getLocaleText( "baricenter" ) ) ); + popup.add( actionMap.get( getLocaleText( "ortocenter" ) ) ); + popup.add( actionMap.get( getLocaleText( "circumcenter" ) ) ); + popup.add( actionMap.get( getLocaleText( "incenter" ) ) ); + for ( Component c : popup.getComponents() ) + if ( c instanceof JMenuItem ) + ( (JMenuItem) c ).setIcon( (Icon) ( (JMenuItem) c ) + .getAction().getValue( Action.LARGE_ICON_KEY ) ); + popup.show( src, (int) src.getBounds().getWidth() - 4, -2 ); + } + }); + trianglePopup.setToolTipText( getLocaleText( "triangle_tools" ) ); + trianglePopup.setIcon( getLargeIcon( "triangle_popup.png" ) ); + shapebar.add( trianglePopup ); + shapebar.add( actionMap.get( getLocaleText( "rectangle" ) ) ); + JButton polygonPopup = new JButton( new AbstractAction() { + @Override + public void actionPerformed( ActionEvent e ) + { + Component src = (Component) e.getSource(); + JPopupMenu popup = new JPopupMenu(); + popup.add( actionMap.get( getLocaleText( "reg_poly" ) ) ); + popup.add( actionMap.get( getLocaleText( "polygon" ) ) ); + popup.add( actionMap.get( getLocaleText( "polyline" ) ) ); + for ( Component c : popup.getComponents() ) + if ( c instanceof JMenuItem ) + ( (JMenuItem) c ).setIcon( (Icon) ( (JMenuItem) c ) + .getAction().getValue( Action.LARGE_ICON_KEY ) ); + popup.show( src, (int) src.getBounds().getWidth() - 4, -2 ); + } + }); + polygonPopup.setToolTipText( getLocaleText( "polygon_tools" ) ); + polygonPopup.setIcon( getLargeIcon( "polygon_popup.png" ) ); + shapebar.add( polygonPopup ); + JButton conicsPopup = new JButton( new AbstractAction() { + @Override + public void actionPerformed( ActionEvent e ) + { + Component src = (Component) e.getSource(); + JPopupMenu popup = new JPopupMenu(); + popup.add( new CircumferenceAction( Application.this ) ); + popup.add( actionMap.get( getLocaleText( "ellipse" ) ) ); + popup.add( actionMap.get( getLocaleText( "parabola" ) ) ); + popup.add( actionMap.get( getLocaleText( "hyperbola" ) ) ); + for ( Component c : popup.getComponents() ) + if ( c instanceof JMenuItem ) + ( (JMenuItem) c ).setIcon( (Icon) ( (JMenuItem) c ) + .getAction().getValue( Action.LARGE_ICON_KEY ) ); + popup.show( src, (int) src.getBounds().getWidth() - 4, -2 ); + } + }); + conicsPopup.setToolTipText( getLocaleText( "conics" ) ); + conicsPopup.setIcon( getLargeIcon( "conics.png" ) ); + shapebar.add( conicsPopup ); + shapebar.add( actionMap.get( getLocaleText( "spline" ) ) ); + shapebar.add( actionMap.get( getLocaleText( "free_hand" ) ) ); + shapebar.add( actionMap.get( getLocaleText( "comment" ) ) ); + if ( jmeEnabled ) + { + shapebar.addSeparator( VSEP ); + shapebar.add( actionMap.get( getLocaleText( "func" ) ) ); + } + // toolbar + toolbar.add( actionMap.get( getLocaleText( "perp" ) ) ); + toolbar.add( actionMap.get( getLocaleText( "para" ) ) ); + toolbar.addSeparator( HSEP ); + toolbar.add( actionMap.get( getLocaleText( "mediatrix" ) ) ); + toolbar.add( actionMap.get( getLocaleText( "bisectrix" ) ) ); + toolbar.add( actionMap.get( getLocaleText( "capable_arc" ) ) ); + toolbar.addSeparator( HSEP ); + toolbar.add( actionMap.get( getLocaleText( "modify" ) ) ); + toolbar.addSeparator( HSEP ); + toolbar.add( actionMap.get( getLocaleText( "midpoint" ) ) ); + toolbar.add( actionMap.get( getLocaleText( "vertex" ) ) ); + toolbar.add( actionMap.get( getLocaleText( "extremes" ) ) ); + toolbar.add( actionMap.get( getLocaleText( "divisions" ) ) ); + toolbar.add( actionMap.get( getLocaleText( "inter" ) ) ); + toolbar.add( actionMap.get( getLocaleText( "bounds" ) ) ); + toolbar.addSeparator( HSEP ); + toolbar.add( actionMap.get( getLocaleText( "fragment" ) ) ); + toolbar.add( actionMap.get( getLocaleText( "fusion" ) ) ); + toolbar.addSeparator( HSEP ); + toolbar.add( actionMap.get( getLocaleText( "area_intersection" ) ) ); + toolbar.add( actionMap.get( getLocaleText( "area_union" ) ) ); + toolbar.add( actionMap.get( getLocaleText( "area_substract" ) ) ); + toolbar.add( actionMap.get( getLocaleText( "area_sym_diff" ) ) ); + toolbar.addSeparator( HSEP ); + toolbar.add( actionMap.get( getLocaleText( "translation" ) ) ); + toolbar.add( actionMap.get( getLocaleText( "rotation" ) ) ); + toolbar.add( actionMap.get( getLocaleText( "homothety" ) ) ); + toolbar.add( actionMap.get( getLocaleText( "central_sym" ) ) ); + toolbar.add( actionMap.get( getLocaleText( "axial_sym" ) ) ); + toolbar.addSeparator( HSEP ); + toolbar.add( actionMap.get( getLocaleText( "paint" ) ) ); + } + + private Action registerAction( Action action ) + { + actionMap.put( action.getValue( Action.NAME ), action ); + return action; + } + + /** + * Set automatic title bar text + */ + public void setAppTitle() + { + String title = APPNAME + " v" + VERSION; + if ( getExercise() != null && getExercise().getTitle().length() > 0 ) + title += " - " + getExercise().getTitle(); + if ( saveFilename != null && saveFilename.length() > 0 ) + title += " (" + saveFilename + ")"; + else + title += " (" + getLocaleText( "not_saved" ) + ")"; + if ( undoManager != null && undoManager.canUndo() ) + title += "*"; + + setTitle( title ); + } + + + ///////////////////// + // GETTERS/SETTERS // + ///////////////////// + + // canvas + public CanvasPanel getCanvas() { return canvas; } + + // inner clipboard + public JDraftingShape[] getInnerClipboard() { return innerClipboard; } + public void setInnerClipboard( JDraftingShape[] innerClipboard ) + { + this.innerClipboard = innerClipboard; + } + + // shape color + public Color getColor() { return color; } + public void setColor( Color color ) + { + this.color = color; + buttonColor.repaint(); + comboLineStyle.repaint(); + } + + // fill color + public Color getFill() { return fill; } + public void setFill( Color fill ) + { + this.fill = fill; + buttonFill.repaint(); + } + + // point color + public Color getPointColor() { return pointColor; } + public void setPointColor( Color pointColor ) + { + this.pointColor = pointColor; + buttonPointColor.repaint(); + } + + // canvas back color + public Color getBackColor() { return getExercise().getBackgroundColor(); } + public void setBackColor( Color backColor ) + { + getExercise().setBackgroundColor( backColor ); + canvas.setBackground( backColor ); + } + + // shape stroke + public BasicStroke getStroke() { return stroke; } + public void setStroke( BasicStroke stroke ) + { + this.stroke = stroke; + spinThickness.getModel().setValue( (double) stroke.getLineWidth() ); + comboLineStyle.repaint(); + } + + // point stroke + public BasicStroke getPointStroke() { return pointStroke; } + public void setPointStroke( BasicStroke pointStroke ) + { + this.pointStroke = pointStroke; + spinPointThickness.getModel().setValue( + (double) pointStroke.getLineWidth() ); + } + + // ruler distance + public double getDistance() { return distance; } + public void setDistance( double distance ) + { + this.distance = distance > 0. ? distance : Math.ulp( 0. ); + } + + // flag for distance using + public boolean isUsingRuler() { return useDistance; } + public void setUseDistance( boolean useDistance ) + { + this.useDistance = useDistance; + canvas.repaint(); + } + + // angle + public double getAngle() { return angle; } + public void setAngle( double angle ) + { + this.angle = angle; + spinAngle.getModel().setValue( angle ); + } + + // action map + public ActionMap getActionMap() { return actionMap; } + + // exercise + public Exercise getExercise() { return exercise; } + public void setExercise( Exercise exercise, String filename ) + { + this.exercise = exercise; + exercise.setFrameAtEnd(); + setSelectedShapes( new HashSet<>() ); + canvas.setViewport( exercise.isEmpty() + ? new Viewport() + : new Viewport( exercise.getBounds() ) ); + canvas.setBackground( getExercise().getBackgroundColor() ); + setSaveFilename( filename ); + setAppTitle(); + + // start new undo/redo system + initializeUndoRedoSystem(); + + // delete old shapelist and reset + shapeList.getModel().removeAllElements(); + for ( JDraftingShape jdshape : getExercise().getShapes() ) + shapeList.getModel().addElement( jdshape ); + + canvas.setCanvasListener( new HandListener( canvas ) ); + } + public void setExercise( Exercise exercise ) + { + setExercise( exercise, null ); + } + + // selected shapes + public Set getSelectedShapes() { return selectedShapes; } + public void setSelectedShapes( Set selected ) + { + selectedShapes = selected; + } + + // filename + public String getSaveFilename() { return saveFilename; } + public void setSaveFilename( String saveFilename ) + { + this.saveFilename = saveFilename; + setAppTitle(); + } + + // flag to show shape names + public boolean isVisibleNames() { return visibleNames; } + public void setVisibleNames( boolean visibleNames ) + { + this.visibleNames = visibleNames; + toggleNames.setSelected( visibleNames ); + } + + // flatness value to convert curved shapes + public double getFlatnessValue() { return flatnessValue; } + public void setFlatnessValue( double flat ) { flatnessValue = flat; } + + // status bar text + /** + * Set text and tooltip on status bar + * @param text status message + */ + public void setStatusText( String text ) + { + labelStatus.setText( text ); + labelStatus.setToolTipText( text ); + } + + + ///////////// + // HELPERS // + ///////////// + + public void openFile( File file ) + { + // load exercise + try ( FileInputStream is = new FileInputStream( file ) ) + { + final ObjectInputStream ois = new ObjectInputStream( is ); + setExercise( (Exercise) ois.readObject(), file.getAbsolutePath() ); + if ( getExercise().getStartIndex() < 1 ) // v0.1.11 (compatibility with previous ver.) + getExercise().setStartIndex(1); + } + catch ( IOException | ClassNotFoundException ex ) + { + JOptionPane.showMessageDialog( this, + "Can't open " + file.getName() + + ":
" + ex + "", + "Error while open " + file.getAbsolutePath(), + JOptionPane.ERROR_MESSAGE ); + } + } + + /** + * Adds to exercise a Path2D representation of the shape with the + * specified parameters. + * @param pit shape path iterator + * @param name shape identifier + * @param description shape comment + * @param color color of the shape + * @param fill fill color of the shape + * @param stroke stroke of the shape + * @param transaction transaction to add the shape for undo/redo system + * without launch UndoSupport listeners. + * A null value add the new UndoableEdit to UndoSupport main object, and + * launch listeners + * @return the inserted new shape + */ + public JDraftingShape addShapeFromIterator( PathIterator pit, String name, String description, + Color color, Color fill, BasicStroke stroke, CompoundEdit transaction ) + { + // create shape from iterator + final JDraftingShape jdshape = JDraftingShape.createFromIterator( + pit, name, description, color, fill, stroke ); + + // add new JDrafting shape + final int index = getExercise().addShape( jdshape ); + shapeList.getModel().add( index, jdshape ); + + // undo/redo system + final UndoableEdit addEdit = new EditAddShapeToExercise( jdshape, index ); + if ( transaction == null ) + undoRedoSupport.postEdit( addEdit ); + else + transaction.addEdit( addEdit ); + + // return new JDrafting shape from iterator + return jdshape; + } + + /** + * See {@link #addShapeFromIterator(PathIterator, String, String, Color, + * BasicStroke, CompoundEdit)} + */ + public JDraftingShape addShapeFromIterator( PathIterator pit, String name, String description, + Color color, Color fill, BasicStroke stroke ) + { + return addShapeFromIterator( pit, name, description, color, fill, stroke, null ); + } + + /** + * Remove shape from exercise + * @param jdshape shape to remove + */ + public int removeShape( JDraftingShape jdshape, CompoundEdit transaction ) + { + // remove shape + final int index = getExercise().removeShape( jdshape ); + shapeList.getModel().removeElement( jdshape ); + + // undo/redo system + final UndoableEdit remove = new EditRemoveShapeFromExercise( jdshape, index ); + if ( transaction == null ) + undoRedoSupport.postEdit( remove ); + else + transaction.addEdit( remove ); + + return index; + } + + /** + * See {@link #removeShape(JDraftingShape, CompoundEdit)} + */ + public int removeShape( JDraftingShape jdshape ) + { + return removeShape( jdshape, null ); + } + + /** + * Exit application + */ + public void exit() + { + // check exit dialog + if ( undoManager.canUndo() ) + { + int option = JOptionPane.showConfirmDialog( this, + getLocaleText( "exit_msg" ), + getLocaleText( "exit_dlg" ) + " " + APPNAME, + JOptionPane.YES_NO_OPTION ); + if ( option != JOptionPane.YES_OPTION ) return; // cancel exit + } + System.out.println( APPNAME + " exited." ); + + // stop canvas movement thread + canvas.getMovementThread().stopMe(); + try { canvas.getMovementThread().join(); } + catch ( InterruptedException e ) {} + + // close app + dispose(); + //System.exit( 0 ); + } + + + ////////////////////// + // UNDO/REDO SYSTEM // + ////////////////////// + + public class ExtendedUndoManager extends UndoManager + { + public int size() { return this. edits.size(); } + + @Override + public UndoableEdit editToBeRedone() { return super.editToBeRedone(); } + + @Override + public UndoableEdit editToBeUndone() { return super.editToBeUndone(); } + + public Vector getEdits() { return edits; } + } + + /** + * Initializes undo manager and support + */ + public void initializeUndoRedoSystem() + { + undoManager = new ExtendedUndoManager(); + undoManager.setLimit( 1000 ); + undoRedoSupport = new UndoableEditSupport(); + undoRedoSupport.addUndoableEditListener( new UndoRedoListener() ); + refreshUndoRedo(); + } + + public class UndoRedoListener implements UndoableEditListener + { + @Override + public void undoableEditHappened( UndoableEditEvent e ) + { + undoManager.addEdit( e.getEdit() ); + refreshUndoRedo(); + } + } + + /** + * Refresh action Undo/Redo + */ + public void refreshUndoRedo() + { + Action action; + action = actionMap.get( getLocaleText( "undo" ) ); + action.setEnabled( undoManager.canUndo() ); + action.putValue( Action.SHORT_DESCRIPTION, + "" + undoManager.getUndoPresentationName() + "" ); + action = actionMap.get( getLocaleText( "redo" ) ); + action.setEnabled( undoManager.canRedo() ); + action.putValue( Action.SHORT_DESCRIPTION, + "" + undoManager.getRedoPresentationName() + "" ); + + setAppTitle(); + /*getActionMap().get( getLocaleText( "save" ) ) + .setEnabled( undoManager.canUndo() );*/ + } + + /** + * UndoableEdit for add shapes to exercise + */ + public class EditAddShapeToExercise extends AbstractUndoableEdit + { + private JDraftingShape jdshape; + private int index; + + public EditAddShapeToExercise( JDraftingShape jdshape, int index ) + { + this.jdshape = jdshape; + this.index = index; + } + + @Override + public void undo() + { + getExercise().removeShape( index ); + shapeList.getModel().remove( index ); + } + + @Override + public void redo() + { + getExercise().addShape( index, jdshape ); + shapeList.getModel().add( index, jdshape ); + } + + @Override + public boolean canRedo() { return true; } + + @Override + public boolean canUndo() { return true; } + + @Override + public String getPresentationName() + { + return getLocaleText( "add" ) + " " + jdshape.toShortString(); + } + } + + /** + * UndoableEdit for remove individual shapes from exercise + */ + public class EditRemoveShapeFromExercise extends AbstractUndoableEdit + { + private JDraftingShape jdshape; + private int index; + + public EditRemoveShapeFromExercise( JDraftingShape jdsShape, int index ) + { + this.jdshape = jdsShape; + this.index = index; + } + + @Override + public void undo() + { + getExercise().addShape( index, jdshape ); + shapeList.getModel().add( index, jdshape ); + } + + @Override + public void redo() + { + getExercise().removeShape( index ); + shapeList.getModel().remove( index ); + } + + @Override + public boolean canRedo() { return true; } + + @Override + public boolean canUndo() { return true; } + + @Override + public String getPresentationName() + { + return getLocaleText( "remove" ) + " " + jdshape.toShortString(); + } + } + +} // class Application diff --git a/src/jdrafting/gui/CanvasPanel.java b/src/jdrafting/gui/CanvasPanel.java new file mode 100644 index 0000000..b0463da --- /dev/null +++ b/src/jdrafting/gui/CanvasPanel.java @@ -0,0 +1,716 @@ +package jdrafting.gui; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +import jdrafting.Exercise; +import jdrafting.geom.JDMath; +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.controller.mouse.AbstractCanvasMouseListener; +import jdrafting.gui.controller.mouse.HandListener; + +/** + * Panel to draw exercises + */ +@SuppressWarnings("serial") +public class CanvasPanel extends JPanel +{ + // Aspect Ratio constants + public static final int ADJUST_CANVAS = 1, SAME_RATIO = 2; + + // this comparator order points at the beginning, and the other + // shapes using the reverse order in the exercise + private final Comparator zBufferComparator = new Comparator() { + @Override + public int compare( JDraftingShape shape1, JDraftingShape shape2 ) + { + final boolean shape1isPoint = shape1.isPoint( shape1.getVertex() ), + shape2isPoint = shape2.isPoint( shape2.getVertex() ); + if ( shape1isPoint && shape2isPoint ) + return 0; + else if ( shape1isPoint ) + return -1; + else if ( shape2isPoint ) + return 1; + + return -Integer.compare( getApplication().getExercise().getShapes().indexOf( shape1 ), + getApplication().getExercise().getShapes().indexOf( shape2 ) ); + } + }; + + // instance vars + private Application app; + private Viewport viewport; // logic viewport + private int aspectRatio = SAME_RATIO; + private double borderPrecision = 8.; // min precision pixel to select shapes + + private static Font textFont = new Font( Font.MONOSPACED, Font.BOLD, 16 ); + + private AbstractCanvasMouseListener canvasListener; + private MoveCanvasThread movementThread; + + /** + * New canvas + * @param app Application parent + * @param viewport current logic viewport + */ + public CanvasPanel( Application app, Viewport viewport ) + { + this.app = app; + this.viewport = viewport; + + // register listeners + setCanvasListener( new HandListener( this ) ); + + // set background color + setOpaque( true ); + //setBackground( Color.WHITE ); + + // viewport movement when mouse over canvas border + movementThread = new MoveCanvasThread(); + movementThread.start(); + } + + ///////////////////// + // GETTERS/SETTERS // + ///////////////////// + + public Application getApplication() { return app; } + + /** + * Logic viewport of the canvas + * @return logic viewport + */ + public Viewport getViewport() { return viewport; } + + /** + * Set the logic viewport of the canvas + * @param viewport logic viewport + */ + public void setViewport( Viewport viewport ) { this.viewport = viewport; } + + /** + * Get auto-movement thread + * @return movement thread instance + */ + public MoveCanvasThread getMovementThread() { return movementThread; } + + /** + * Get transform from current logical viewport to canvas viewport + * @return transform logical->canvas + */ + public AffineTransform getTransform() + { + return getTransform( getViewport(), getCanvasViewport() ); + } + + /** + * Transform an original viewport to destiny viewport (like canvas to logic or logic to canvas) + * @param orig original viewport + * @param dest destiny viewport + * @return change transform + */ + public static AffineTransform getTransform( Viewport orig, Viewport dest ) + { + final AffineTransform transform = new AffineTransform(); + // invert Y-axis + transform.scale( 1, -1 ); + // put origin in left-lower corner + transform.translate( 0, -dest.getHeight() ); + + // transform from original viewport to destiny viewport + double sx = dest.getWidth() / orig.getWidth(); + double sy = dest.getHeight() / orig.getHeight(); + double tx = dest.getMinX() - orig.getMinX(); + double ty = dest.getMinY() - orig.getMinY(); + transform.scale( sx, sy ); + transform.translate( tx, ty ); + + return transform; + } + + /** + * Inverts a transform (inverse matrix) + * @param at transfrom + * @return inverse transform or null if it is singular + */ + public static AffineTransform getInverseTransform( AffineTransform at ) + { + try + { + return at.createInverse(); + } + catch ( NoninvertibleTransformException ex ) { return null; } + } + + /** + * Get transform from canvas viewport to current logical viewport + * @return transform canvas->logical + */ + public AffineTransform getInverseTransform() + { + return getInverseTransform( getTransform() ); + } + + @Override + public void paintComponent( Graphics g ) + { + super.paintComponent( g ); + + if ( app.getExercise() == null ) + return; + + final Graphics2D g2 = (Graphics2D) g; + + // High quality render + JDUtils.setHighQualityRender( g2 ); + + // draw exercise shapes + drawExercise( g2, getTransform(), app.getExercise(), + app.getSelectedShapes(), app.isVisibleNames() ); + + // draw frame info + if ( !app.getExercise().isIndexAtEnd() ) + { + int fontSize = 22; + g2.setFont( new Font( Font.SANS_SERIF, Font.BOLD, fontSize ) ); + String frameInfo = "Frame " + + app.getExercise().getFrameIndex() + + "/" + + app.getExercise().size(); + int width = getFontMetrics( g2.getFont() ).stringWidth( frameInfo ); + g2.setColor( Color.LIGHT_GRAY ); + g2.drawString( frameInfo, getWidth() - width - 4, fontSize + 2 ); + g2.setColor( Color.BLACK ); + g2.drawString( frameInfo, getWidth() - width - 6, fontSize ); + } + + // draw tool graphics + canvasListener.paintTool( g2 ); + } + + public static void drawExercise( Graphics2D g2, AffineTransform transform, + Exercise exercise, Set selected, boolean text ) + { + // draw non-selected shapes + for ( JDraftingShape jdshape : exercise.getFramesUntilIndex() ) + { + if ( selected.contains( jdshape ) ) + continue; + + // transform shape to canvas + final Shape transShape = transform.createTransformedShape( jdshape.getShape() ); + // set style + g2.setStroke( jdshape.getStroke() ); + + // fill shape if has fill color + if ( jdshape.getFill() != null ) + { + g2.setColor( jdshape.getFill() ); + g2.fill( transShape ); + } + // draw shape + g2.setColor( jdshape.getColor() ); + + if ( jdshape.isText() ) + drawText( jdshape, transShape, g2, transform ); + else + g2.draw( transShape ); + } + // draw selected shapes + for ( JDraftingShape jdshape: selected ) + { + // transform shape to canvas + Shape transShape = transform.createTransformedShape( jdshape.getShape() ); + + // set style + BasicStroke stroke = jdshape.getStroke(); + if ( stroke.getLineWidth() < 8f ) + g2.setStroke( new BasicStroke( 8f, stroke.getEndCap(), stroke.getLineJoin() ) ); + else + g2.setStroke( stroke ); + // draw selected shape + g2.setColor( new Color( 200, 32, 32, 222 ) ); + g2.draw( transShape ); + + if ( jdshape.isText() ) + drawText( jdshape, transShape, g2, transform ); + } + // draw identifiers + if ( text ) + { + for ( JDraftingShape jdshape : exercise.getFramesUntilIndex() ) + { + if ( jdshape.getName() != null // non empty or null strings + && jdshape.getName().length() > 0 ) + { + Color c = selected.contains( jdshape ) + ? new Color( 200, 32, 32, 222 ) + : jdshape.getColor(); + g2.setFont( textFont ); + g2.setColor( new Color( 0, 0, 0, 200 ) ); + + Rectangle2D bounds = getShapeCanvasBounds( jdshape, transform); + + double textX = bounds.getCenterX() + + jdshape.getTextPosition().getX() * bounds.getWidth(); + double textY = bounds.getCenterY() + - jdshape.getTextPosition().getY() * bounds.getHeight(); + float x = (float) textX, y = (float) textY; + + g2.drawString( jdshape.getName(), x + 1, y + 1 ); + g2.setColor( new Color( + c.getRed(), c.getGreen(), c.getBlue(), 150 ) ); + g2.drawString( jdshape.getName(), x, y ); + } + } + } + } + + private static void drawText( JDraftingShape jdshape, Shape transShape, Graphics2D g2, + AffineTransform transform ) + { + final Font font = jdshape.adjustFontSizeToBounds( g2, transform ); + g2.setFont( font ); + final Rectangle2D bounds = transShape.getBounds2D(); + drawMultilineText( g2, jdshape.getDescription(), + (float) bounds.getX(), (float) bounds.getY() ); + } + + public Viewport getCanvasViewport() + { + switch( aspectRatio ) + { + case ADJUST_CANVAS: + return new Viewport( 0, getWidth() - 1, 0, getHeight() - 1 ); + case SAME_RATIO: + Viewport logicView = getViewport(); + double canvasRatio = (double) getWidth() / getHeight(); + double logicRatio = logicView.getWidth() / logicView.getHeight(); + if ( canvasRatio >= logicRatio ) + return new Viewport( + 0, getWidth() / canvasRatio * logicRatio, + 0, getHeight() ); + else + return new Viewport( + 0, getWidth(), + 0, getHeight() * canvasRatio / logicRatio ); + default: + return null; + } + } + + /** + * Get all shapes near a canvas point (borderPrecision value) + * @param canvasPoint point on canvas viewport + * @return a list with shapes near canvas point + */ + public List getAllShapesAtCanvasPoint( Point2D canvasPoint ) + { + double x = canvasPoint.getX(), y = canvasPoint.getY(); + List shapes = new ArrayList<>(); + + for ( JDraftingShape jdshape : app.getExercise().getShapes() ) + { + double[] coords = new double[6]; + PathIterator pit = jdshape.getShape().getPathIterator( null ); + double lineWidth = jdshape.getStroke().getLineWidth(); + double border = Math.max( borderPrecision, lineWidth ); + Shape mouseRect = getInverseTransform().createTransformedShape( + new Rectangle2D.Double( x - border / 2., + y - border / 2., + border, + border ) ); + pit.currentSegment( coords ); + pit.next(); + Point2D start = new Point2D.Double( coords[0], coords[1] ); + Point2D end; + while ( !pit.isDone() ) + { + pit.currentSegment( coords ); + pit.next(); + end = new Point2D.Double( coords[0], coords[1] ); + Line2D line = new Line2D.Double( start, end ); + start = end; + if ( line.intersects( mouseRect.getBounds2D() ) ) + { + shapes.add( jdshape ); + break; + } + } + } + + return shapes; + } + + /** + * Get shape with max zbuffer near canvas point (borderPrecision value) + * @param canvasPoint point on canvas viewport + * @return the shape with max zbuffer near canvas point + */ + public JDraftingShape getShapeAtCanvasPoint( Point2D canvasPoint ) + { + double x = canvasPoint.getX(), y = canvasPoint.getY(); + + AffineTransform inverse = getInverseTransform(); + + // iterate over every shape in custom order + List reordered = + app.getExercise().getFramesUntilIndex() + .stream() + .sorted( zBufferComparator ) + .collect( Collectors.toList() ); + for ( JDraftingShape jdshape : reordered ) + { + double[] coords = new double[6]; + PathIterator pit = jdshape.getShape().getPathIterator( null ); + double lineWidth = jdshape.getStroke().getLineWidth(); + double border = Math.max( borderPrecision, lineWidth ); + Shape mouseRect = inverse.createTransformedShape( + new Rectangle2D.Double( x - border / 2., y - border / 2., + border, border ) ); + + pit.currentSegment( coords ); + pit.next(); + Point2D start = new Point2D.Double( coords[0], coords[1] ); + Point2D end; + while ( !pit.isDone() ) + { + pit.currentSegment( coords ); + pit.next(); + end = new Point2D.Double( coords[0], coords[1] ); + Line2D line = new Line2D.Double( start, end ); + start = end; + if ( line.intersects( mouseRect.getBounds2D() ) ) + return jdshape; + } + } + + return null; + } + + /** + * Get the position of a point near to mouse position or the logic mouse + * position if there isn't near points + * @param canvasPoint canvas position + * @return the logic position of the point or the logic mouse position + */ + public Point2D adjustToPoint( Point2D canvasPoint ) + { + // check for shapes at mouse position + JDraftingShape jdshape = getShapeAtCanvasPoint( canvasPoint ); + if ( jdshape != null ) + { + // check whether shape is a point + List vertex = jdshape.getVertex(); + if ( jdshape.isPoint( vertex ) ) + return vertex.get( 0 ); // return point + } + // no point, return logic mouse position + return getInverseTransform().transform( canvasPoint, null ); + } + + /** + * Get the position of a vertex near to mouse position (no points) + * @param canvasPoint canvas position + * @return the logic position of the vertex or null if doesn't exist + */ + public Point2D adjustToVertex( Point2D canvasPoint ) + { + // check for shapes at mouse position + JDraftingShape jdshape = getShapeAtCanvasPoint( canvasPoint ); + if ( jdshape != null ) + { + List vertex = jdshape.getVertex(); + if ( jdshape.isPoint( vertex ) ) // don't adjust to points + return null; + double x = canvasPoint.getX(), y = canvasPoint.getY(); + double lineWidth = jdshape.getStroke().getLineWidth(); + double border = Math.max( borderPrecision, lineWidth ); + Shape mouseRect = getInverseTransform().createTransformedShape( + new Rectangle2D.Double( x - border / 2., y - border / 2., + border, border ) ); + + for ( Point2D v : vertex ) + if ( mouseRect.contains( v ) ) + return v; // return a vertex near canvas point + } + // no vertex + return null; + } + + public JDraftingShape getShapeWithNameAtCanvasPoint( Point2D canMouse ) + { + AffineTransform transform = getTransform(); + + // iterate over every shape with name defined in custom order + List reordered = + app.getExercise().getFramesUntilIndex() + .stream() + .filter( s -> s.getName() != null && s.getName().length() > 0 ) + .sorted( zBufferComparator ) + .collect( Collectors.toList() ); + + for ( JDraftingShape jdshape : reordered ) + { + Rectangle2D bounds = getShapeCanvasBounds( jdshape, transform ); + + Point2D namePosition = getNamePosition( bounds, jdshape ); + + FontMetrics metrics = getGraphics().getFontMetrics( textFont ); + int w = metrics.stringWidth( jdshape.getName() ); + int h = textFont.getSize() + metrics.getDescent(); + + if ( new Rectangle2D.Double( namePosition.getX(), + namePosition.getY() - h + metrics.getDescent(), w, h ) + .contains( canMouse ) ) + return jdshape; + } + + return null; + } + + public static Point2D getNamePosition( Rectangle2D bounds, + JDraftingShape jdshape ) + { + return new Point2D.Double( + bounds.getCenterX() + + jdshape.getTextPosition().getX() * bounds.getWidth(), + bounds.getCenterY() + - jdshape.getTextPosition().getY() * bounds.getHeight() ); + } + + /** + * Get intersection points between two shapes + * @param jdshape1 one shape + * @param jdshape2 should be a different shape + * @return a list with the intersection points (can be empty) + */ + public static List intersectionPoints( JDraftingShape jdshape1, + JDraftingShape jdshape2 ) + { + List segments1 = jdshape1.getSegments(); + List segments2 = jdshape2.getSegments(); + List intersections = new LinkedList<>(); + if ( jdshape1.isPoint( jdshape1.getVertex() ) + || jdshape2.isPoint( jdshape2.getVertex() ) ) + return intersections; // one of them is a point, ignore intersection + + for ( Line2D seg1 : segments1 ) + for ( Line2D seg2 : segments2 ) + // if lines intersect, add joins + if ( seg1.intersectsLine( seg2 ) ) + { + Point2D join = JDMath.linesIntersection( + seg1.getP1(), seg1.getP2(), + seg2.getP1(), seg2.getP2() ); + if ( join != null ) // (avoid coincidents) + intersections.add( join ); + } + + return intersections; + } + + /** + * Get the nearest intersection to mouse + * @param mousePosition canvas mouse position + * @return the nearest intersection or null if there is no intersections + */ + public Point2D adjustPointToIntersection( Point2D mousePosition ) + { + final List shapes = getAllShapesAtCanvasPoint( mousePosition ); + if ( shapes.size() < 2 ) + return null; + + final Point2D logicMouse = getInverseTransform().transform( mousePosition, null ); + final JDraftingShape jdshape1 = shapes.get( 0 ), + jdshape2 = shapes.get( 1 ); + + final List intersections = intersectionPoints( jdshape1, jdshape2 ); + + return intersections // nearest intersection to mouse + .stream() + .min( ( point1, point2 ) -> Double.compare( point1.distanceSq( logicMouse ), + point2.distanceSq( logicMouse ) ) ) + .orElse( null ); + } + + public static Rectangle2D getExerciseBounds( Exercise exercise, + Viewport orig, Viewport dest ) + { + if ( !exercise.getShapes().isEmpty() ) + { + final AffineTransform transform = getTransform( orig, dest ); + + Rectangle2D enclosure = getShapeCanvasBounds( exercise.getShapes().get(0), transform ); + + for ( final JDraftingShape jdshape : exercise.getShapes() ) + enclosure = enclosure.createUnion( getShapeCanvasBounds( jdshape, transform ) ); + + return getInverseTransform(transform).createTransformedShape(enclosure).getBounds2D(); + } + + return null; + } + + public static Rectangle2D getShapeCanvasBounds( JDraftingShape jdshape, + AffineTransform transform ) + { + final Rectangle2D bounds = transform.createTransformedShape( + jdshape.getShape().getBounds2D() ).getBounds2D(); + final float lineWidth = jdshape.getStroke().getLineWidth(); + + return new Rectangle2D.Double( bounds.getMinX() - lineWidth / 2., + bounds.getMinY() - lineWidth / 2., + bounds.getWidth() + lineWidth, + bounds.getHeight() + lineWidth ); + } + + /** + * Get current canvas mouse listener (segment tool, hand,...) + * @return current listener + */ + public AbstractCanvasMouseListener getCanvasListener() + { + return canvasListener; + } + + /** + * Set new canvas mouse listener + * @param listener + */ + public void setCanvasListener( AbstractCanvasMouseListener listener ) + { + // remove previous listener + removeMouseListener( canvasListener ); + removeMouseMotionListener( canvasListener ); + removeMouseWheelListener( canvasListener ); + // update listener + canvasListener = listener; + addMouseListener( listener ); + addMouseMotionListener( listener ); + addMouseWheelListener( listener ); + + repaint(); // (needed to refresh all tools graphs) + } + + private static void drawMultilineText( Graphics2D g2, String txt, float x, float y ) + { + final FontMetrics fm = g2.getFontMetrics(); + + final String[] lines = txt.split( "\n" ); + int fila = 0; + for ( final String line : lines ) + g2.drawString( line, x, y + (++fila)*g2.getFont().getSize() - fm.getDescent() ); + } + + /** + * Move viewport when mouse near of the canvas bounds + */ + class MoveCanvasThread extends Thread + { + private static final int MAX_WAIT = 40; + private int wait = MAX_WAIT; + private boolean stop = false; + + public void stopMe() { stop = true; } + + @Override + public void run() + { + while ( !stop ) + { + try { Thread.sleep( wait < MAX_WAIT ? 20 : 200 ); } + catch( InterruptedException e ) { return; } + + if ( stop ) return; + + SwingUtilities.invokeLater( () -> { + + Point mouse = getMousePosition(); + if ( mouse == null + || getCanvasListener() instanceof HandListener ) + { + wait = MAX_WAIT; + return; + } + int border = 32; + Rectangle inner = new Rectangle( border, border, + getWidth() - 2 * border, getHeight() - 2 * border ); + if ( inner.contains( mouse ) ) + { + wait = MAX_WAIT; + return; + } + + if ( wait > 0 ) + { + wait--; + return; + } + + double factor = 100.; + Viewport oldView = getViewport(); + double dw = oldView.getWidth() / factor; + double dh = oldView.getHeight() / factor; + if ( mouse.getY() > getHeight() - border ) + { + setViewport( + new Viewport( oldView.getMinX(), + oldView.getMaxX(), + oldView.getMinY() - dh, + oldView.getMaxY() - dh ) ); + } + else if ( mouse.getY() <= border ) + { + setViewport( + new Viewport( oldView.getMinX(), + oldView.getMaxX(), + oldView.getMinY() + dh, + oldView.getMaxY() + dh ) ); + } + oldView = getViewport(); + if ( mouse.getX() > getWidth() - border ) + { + setViewport( + new Viewport( oldView.getMinX() + dw, + oldView.getMaxX() + dw, + oldView.getMinY(), + oldView.getMaxY() ) ); + } + else if ( mouse.getX() <= border ) + { + setViewport( + new Viewport( oldView.getMinX() - dw, + oldView.getMaxX() - dw, + oldView.getMinY(), + oldView.getMaxY() ) ); + } + repaint(); + } ); // lambda + } // while + } // method run + } // class MoveCanvasThread +} diff --git a/src/jdrafting/gui/EditShapeDialog.java b/src/jdrafting/gui/EditShapeDialog.java new file mode 100644 index 0000000..e819df9 --- /dev/null +++ b/src/jdrafting/gui/EditShapeDialog.java @@ -0,0 +1,140 @@ +package jdrafting.gui; + +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.KeyStroke; + +import jdrafting.geom.JDraftingShape; + +@SuppressWarnings("serial") +public class EditShapeDialog extends JDialog +{ + public EditShapeDialog( Application parent, JDraftingShape jdshape ) + { + super( parent, getLocaleText( "shape_prop" ), true ); + + JPanel panel = new JPanel( new BorderLayout() ); + panel.setBorder( BorderFactory.createEmptyBorder( 6, 6, 6, 6 ) ); + getContentPane().add( panel ); + + Box box = Box.createVerticalBox(); + panel.add( box ); + + JLabel labelName = new JLabel( getLocaleText( "shape_name" ) ); + labelName.setAlignmentX( LEFT_ALIGNMENT ); + box.add( labelName ); + + final JTextField textName = new JTextField( jdshape.getName() ); + textName.setAlignmentX( LEFT_ALIGNMENT ); + textName.setMaximumSize( new Dimension( + Integer.MAX_VALUE, textName.getPreferredSize().height ) ); + box.add( textName ); + + box.add( Box.createRigidArea( new Dimension( 0, 10 ) ) ); + + JLabel labelDesc = new JLabel( getLocaleText( "shape_desc" ) ); + labelDesc.setAlignmentX( LEFT_ALIGNMENT ); + box.add( labelDesc ); + + final JTextArea taDesc = + new JTextArea( jdshape.getDescription(), 6, 50 ); + JScrollPane scrollArea = new JScrollPane( taDesc ); + scrollArea.setAlignmentX( LEFT_ALIGNMENT ); + box.add( scrollArea ); + + box.add( Box.createRigidArea( new Dimension( 0, 10 ) ) ); + + JPanel btnPanel = new JPanel( new FlowLayout( FlowLayout.CENTER ) ); + btnPanel.setAlignmentX( LEFT_ALIGNMENT ); + btnPanel.setMaximumSize( new Dimension( + Integer.MAX_VALUE, textName.getPreferredSize().height ) ); + + box.add( btnPanel ); + + // ok button + JButton btnOk = new JButton( getLocaleText( "save_close" ) ); + btnPanel.add( btnOk ); + Action okAction = new AbstractAction() { + @Override + public void actionPerformed( ActionEvent e ) + { + // modify shape properties + jdshape.setName( textName.getText() ); + jdshape.setDescription( taDesc.getText() ); + + // refresh GUI + parent.getCanvas().repaint(); + parent.refreshUndoRedo(); + parent.scrollList.repaint(); + + dispose(); + } + }; + btnOk.addActionListener( okAction ); + // details button + JButton btnDetails = new JButton( getLocaleText( "details" ) ); + btnPanel.add( btnDetails ); + btnDetails.addActionListener( new ActionListener() { + @Override + public void actionPerformed( ActionEvent e ) + { + String message = + String.join( "\n{", jdshape.toString().split( "\\{" ) ); + JTextArea txtarea = new JTextArea( message ); + txtarea.setEditable( false ); + JScrollPane scroll = new JScrollPane( txtarea ); + scroll.setPreferredSize( new Dimension( + (int) txtarea.getPreferredSize().getWidth() + 20, 500 ) ); + JOptionPane.showMessageDialog( EditShapeDialog.this, scroll, + getLocaleText( "details" ) + ": " + jdshape.toString(), + JOptionPane.PLAIN_MESSAGE ); + } + }); + // cancel button + JButton btnCancel = new JButton( getLocaleText( "cancel" ) ); + btnPanel.add( btnCancel ); + Action cancelAction = new AbstractAction() { + @Override + public void actionPerformed( ActionEvent e ) + { + setVisible( false ); + dispose(); + } + }; + btnCancel.addActionListener( cancelAction ); + + // ESCAPE + getRootPane().getInputMap( JComponent.WHEN_IN_FOCUSED_WINDOW ) + .put( KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, 0 ), "ESCAPE" ); + getRootPane().getActionMap().put( "ESCAPE", cancelAction ); + // ENTER + getRootPane().getInputMap( JComponent.WHEN_IN_FOCUSED_WINDOW ) + .put( KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0 ), "ENTER" ); + getRootPane().getActionMap().put( "ENTER", okAction ); + + // window + setDefaultCloseOperation( DISPOSE_ON_CLOSE ); + pack(); + setLocationRelativeTo( parent ); + } +} diff --git a/src/jdrafting/gui/FileInfoDialog.java b/src/jdrafting/gui/FileInfoDialog.java new file mode 100644 index 0000000..77e013a --- /dev/null +++ b/src/jdrafting/gui/FileInfoDialog.java @@ -0,0 +1,135 @@ +package jdrafting.gui; + +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSpinner; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.KeyStroke; +import javax.swing.SpinnerNumberModel; + +@SuppressWarnings("serial") +public class FileInfoDialog extends JDialog +{ + public FileInfoDialog( Application parent ) + { + super( parent, getLocaleText( "exer_prop" ), true ); + + JPanel panel = new JPanel( new BorderLayout() ); + panel.setBorder( BorderFactory.createEmptyBorder( 6, 6, 6, 6 ) ); + getContentPane().add( panel ); + + Box box = Box.createVerticalBox(); + panel.add( box ); + + // title textfield + JLabel ltitle = new JLabel( getLocaleText( "title" ) ); + ltitle.setAlignmentX( LEFT_ALIGNMENT ); + box.add( ltitle ); + + final JTextField textTitle = new JTextField( parent.getExercise().getTitle() ); + textTitle.setAlignmentX( LEFT_ALIGNMENT ); + textTitle.setMaximumSize( new Dimension( + Integer.MAX_VALUE, textTitle.getPreferredSize().height ) ); + box.add( textTitle ); + + box.add( Box.createRigidArea( new Dimension( 0, 10 ) ) ); + + // description area + JLabel lDesc = new JLabel( getLocaleText( "exer_desc" ) ); + lDesc.setAlignmentX( LEFT_ALIGNMENT ); + box.add( lDesc ); + + final JTextArea textArea = new JTextArea( parent.getExercise().getDescription(), 8, 50 ); + JScrollPane scrollArea = new JScrollPane( textArea ); + scrollArea.setAlignmentX( LEFT_ALIGNMENT ); + box.add( scrollArea ); + + box.add( Box.createRigidArea( new Dimension( 0, 10 ) ) ); + + // start frame spinner + final JLabel lStartIndex = new JLabel( getLocaleText( "start_frame" ) ); + lStartIndex.setAlignmentX( LEFT_ALIGNMENT ); + box.add( lStartIndex ); + + final int minValue = 1, + maxValue = Math.max( 1, parent.getExercise().size() ), + value = Math.max( minValue, parent.getExercise().getStartIndex() ); + final JSpinner spinFrame = + new JSpinner( new SpinnerNumberModel( value, minValue, maxValue, 1 ) ); + spinFrame.getModel().addChangeListener( + e -> parent.getExercise().setStartIndex( (int) spinFrame.getValue() ) ); + spinFrame.setAlignmentX( LEFT_ALIGNMENT ); + spinFrame.setFont( new Font( Font.MONOSPACED, Font.BOLD, 20 ) ); + spinFrame.setMaximumSize( new Dimension( 100, spinFrame.getPreferredSize().height ) ); + box.add( spinFrame ); + + + box.add( Box.createRigidArea( new Dimension( 0, 10 ) ) ); + + JPanel btnPanel = new JPanel( new FlowLayout( FlowLayout.CENTER ) ); + btnPanel.setAlignmentX( LEFT_ALIGNMENT ); + btnPanel.setMaximumSize( new Dimension( + Integer.MAX_VALUE, textTitle.getPreferredSize().height ) ); + + box.add( btnPanel ); + + // ok button + JButton btnOk = new JButton( getLocaleText( "save_close" ) ); + btnPanel.add( btnOk ); + Action okAction = new AbstractAction() { + @Override + public void actionPerformed( ActionEvent e ) + { + parent.getExercise().setTitle( textTitle.getText() ); + parent.getExercise().setDescription( textArea.getText() ); + setVisible( false ); + dispose(); + } + }; + btnOk.addActionListener( okAction ); + // cancel button + JButton btnCancel =new JButton( getLocaleText( "cancel" ) ); + btnPanel.add( btnCancel ); + Action cancelAction = new AbstractAction() { + @Override + public void actionPerformed( ActionEvent e ) + { + setVisible( false ); + dispose(); + } + }; + btnCancel.addActionListener( cancelAction ); + + // ESCAPE + getRootPane().getInputMap( JComponent.WHEN_IN_FOCUSED_WINDOW ) + .put( KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, 0 ), "ESCAPE" ); + getRootPane().getActionMap().put( "ESCAPE", cancelAction ); + // ENTER + getRootPane().getInputMap( JComponent.WHEN_IN_FOCUSED_WINDOW ) + .put( KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0 ), "ENTER" ); + getRootPane().getActionMap().put( "ENTER", okAction ); + + // window + setDefaultCloseOperation( DISPOSE_ON_CLOSE ); + pack(); + setLocationRelativeTo( parent ); + } +} diff --git a/src/jdrafting/gui/JDCompoundEdit.java b/src/jdrafting/gui/JDCompoundEdit.java new file mode 100644 index 0000000..0fe1104 --- /dev/null +++ b/src/jdrafting/gui/JDCompoundEdit.java @@ -0,0 +1,39 @@ +package jdrafting.gui; + +import javax.swing.UIManager; +import javax.swing.undo.CompoundEdit; + +/** + * @author Miguel Alejandro Moreno Barrientos, (C)2021 + * @since 0.1.12 + */ +public class JDCompoundEdit extends CompoundEdit +{ + private static final long serialVersionUID = 1L; + + private String presentation = ""; + + public JDCompoundEdit() {} + public JDCompoundEdit( String presentation ) { this.presentation = presentation; } + + @Override + public boolean canRedo() { return true; } + @Override + public boolean canUndo() { return true; } + + @Override + public String getPresentationName() { return presentation; } + @Override + public String getRedoPresentationName() + { + return new StringBuilder( UIManager.getString("AbstractUndoableEdit.redoText") ) + .append( " " ).append( getPresentationName() ).toString(); + } + @Override + public String getUndoPresentationName() + { + return new StringBuilder( UIManager.getString("AbstractUndoableEdit.undoText") ) + .append( " " ).append( getPresentationName() ).toString(); + } + +} diff --git a/src/jdrafting/gui/JDUtils.java b/src/jdrafting/gui/JDUtils.java new file mode 100644 index 0000000..bd53c66 --- /dev/null +++ b/src/jdrafting/gui/JDUtils.java @@ -0,0 +1,373 @@ +package jdrafting.gui; + +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Point; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.Toolkit; +import java.awt.event.KeyEvent; +import java.awt.geom.Path2D; +import java.awt.geom.PathIterator; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.stream.Stream; + +import javax.swing.ImageIcon; + +import jdrafting.geom.JDMath; + +/** + * Helper methods for JDrafting + */ +final public class JDUtils +{ + /** + * Avoid instantiation + */ + private JDUtils() {} + + @Override + protected Object clone() throws CloneNotSupportedException + { + throw new CloneNotSupportedException(); + } + + /** + * Method like Kotlin elvis operator + * @param value value maybe null + * @param def value if null + * @return value if non null, or def value + */ + public static Object elvis( Object value, Object def ) + { + return value != null ? value : def; + } + + /** + * Specification of elvis for null or empty strings + * @param value string maybe null or empty + * @param def value if null + * @return value if non null or empty, or def value + */ + public static String elvis( String value, String def ) + { + return value == null || value.isEmpty() ? def : value; + } + + public static StringBuilder pathToString( final Shape shape ) + { + final PathIterator pit = shape.getPathIterator( null ); + final StringBuilder sb = new StringBuilder(); + final double[] current = new double[6]; + + while ( !pit.isDone() ) + { + sb.append( "{" ); + final int type = pit.currentSegment( current ); + switch( type ) + { + case PathIterator.SEG_MOVETO: + sb.append( "move to " + current[0] + ", " + current[1] ); + break; + case PathIterator.SEG_LINETO: + sb.append( "line to " + current[0] + ", " + current[1] ); + break; + case PathIterator.SEG_QUADTO: + sb.append( "quadratic to " + current[0] + ", " + current[1] + + ", " + current[2] + ", " + current[3] ); + break; + case PathIterator.SEG_CUBICTO: + sb.append( "cubic to " + current[0] + ", " + current[1] + + ", " + current[2] + ", " + current[3] + ", " + + current[4] + ", " + current[5] ); + break; + case PathIterator.SEG_CLOSE: + sb.append( "close" ); + break; + } + pit.next(); + sb.append( "}" ); + } + + return sb; + } + + public static Path2D removeUnnecessarySegments( final Shape shape ) + { + final Path2D.Double path = new Path2D.Double(); + final double[] coords = new double[6], + lastMove = new double[] {Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY}, + lastLine = new double[] {Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY}; + final PathIterator pit = shape.getPathIterator(null); + + while ( !pit.isDone() ) + { + switch( pit.currentSegment( coords ) ) + { + case PathIterator.SEG_LINETO: + if ( Math.hypot( lastLine[0] - coords[0], lastLine[1] - coords[1] ) < 1E-9 ) + break; + path.lineTo( coords[0], coords[1] ); + System.arraycopy( coords, 0, lastLine, 0, 2 ); + break; + case PathIterator.SEG_MOVETO: + path.moveTo( coords[0], coords[1] ); + System.arraycopy( coords, 0, lastMove, 0, 2 ); + break; + case PathIterator.SEG_QUADTO: + path.quadTo( coords[0], coords[1], coords[2], coords[3] ); + break; + case PathIterator.SEG_CUBICTO: + path.curveTo( coords[0], coords[1], coords[2], coords[3], coords[4], coords[5] ); + break; + case PathIterator.SEG_CLOSE: + if ( Math.hypot( lastLine[0] - lastMove[0], lastLine[1] - lastMove[1] ) < 1E-9 ) + break; + path.closePath(); + break; + } + pit.next(); + } + + return path; + } + + /** + * Remove close segment from a shape into a Path2D.Double + * @param closedShape shape with close segment + * @return open path + */ + public static Path2D openShape( final Shape closedShape ) + { + final Path2D.Double path = new Path2D.Double(); + final double[] coords = new double[6]; + final PathIterator pit = closedShape.getPathIterator(null); + + while ( !pit.isDone() ) + { + switch( pit.currentSegment( coords ) ) + { + case PathIterator.SEG_LINETO: + path.lineTo( coords[0], coords[1] ); + break; + case PathIterator.SEG_MOVETO: + path.moveTo( coords[0], coords[1] ); + break; + case PathIterator.SEG_QUADTO: + path.quadTo( coords[0], coords[1], coords[2], coords[3] ); + break; + case PathIterator.SEG_CUBICTO: + path.curveTo( coords[0], coords[1], coords[2], coords[3], coords[4], coords[5] ); + break; + case PathIterator.SEG_CLOSE: + return path; + } + pit.next(); + } + return path; + } + + /** + * Add final line start-end to a path obtained from a shape-PathIterator + * @param shape shape + * @param addClose add final close segment + * @return path closed with line (and a optional close) + */ + public static Path2D.Double closeShapeWithLine( final Shape shape, boolean addClose ) + { + final Path2D.Double path = new Path2D.Double(); + final PathIterator pit = shape.getPathIterator(null); + final double[] coords = new double[6], + init = new double[6]; + pit.currentSegment( init ); + + while ( !pit.isDone() ) + { + switch( pit.currentSegment( coords ) ) + { + case PathIterator.SEG_LINETO: + path.lineTo( coords[0], coords[1] ); + break; + case PathIterator.SEG_MOVETO: + path.moveTo( coords[0], coords[1] ); + break; + case PathIterator.SEG_QUADTO: + path.quadTo( coords[0], coords[1], coords[2], coords[3] ); + break; + case PathIterator.SEG_CUBICTO: + path.curveTo( coords[0], coords[1], coords[2], coords[3], coords[4], coords[5] ); + break; + } + pit.next(); + } + // join start-end + path.lineTo( init[0], init[1] ); + if ( addClose ) + path.closePath(); + return path; + } + + public static int smallIconSize = 16; + public static int largeIconSize = 24; + + /** + * Get screen size, multiplied by factor + * @param ratioX factor width (value 1 for screen width) + * @param ratioY factor height (value 1 for screen height) + * @return rounded scaled screen size + */ + public static Dimension getScreenSize( float ratioX, float ratioY ) + { + final Dimension ssize = Toolkit.getDefaultToolkit().getScreenSize(); + + return new Dimension( JDMath.nearInt( ssize.getWidth() * ratioX ), + JDMath.nearInt( ssize.getHeight() * ratioY ) ); + } + + /** + * Get a text value in current language + * @param key the key in the language file + * @return the translated expression + */ + public static String getLocaleText( String key ) + { + try + { + final ResourceBundle resource = ResourceBundle.getBundle( + "jdrafting.resources.language.language", Application.locale ); + /*if ( !locale.equals( resource.getLocale() ) ) // go to English + throw new MissingResourceException( "fallback", "", key );*/ + + return resource.getString( key ); + } + catch ( MissingResourceException e ) + { + // If all fails, default English + // (not needed if "language.properties" resource exists) + return ResourceBundle.getBundle( + "jdrafting.resources.language.language", Locale.ENGLISH ) + .getString( key ); + } + } + + /** + * Get mnemonic in current language + * @param key the key in the language file + * @return keycode for char + */ + public static int getLocaleMnemonic( String key ) + { + return KeyEvent.getExtendedKeyCodeForChar( getLocaleText( key ).charAt( 0 ) ); + } + + /** + * Scale an image from the image resource folder to a specified size + * @param name name of the file (from image resource folder) + * @param width new width + * @param height new height + * @return the scaled image + */ + public static ImageIcon getScaledIco( String name, int width, int height ) + { + return new ImageIcon( new ImageIcon( + Object.class + .getResource( "/jdrafting/resources/images/" + name ) ) + .getImage() + .getScaledInstance( width, height, Image.SCALE_SMOOTH ) ); + } + + /** + * Get small icons of the resource images. + * See {@link #getScaledIco(String, int, int)} + * @see #getScaledIco(String, int, int) + * @param name filename + * @return scaled image + */ + public static ImageIcon getSmallIcon( String name ) + { + return getScaledIco( name, smallIconSize, smallIconSize ); + } + + /** + * Get large icons of the resource images. + * See {@link #getScaledIco(String, int, int)} + * @see #getScaledIco(String, int, int) + * @param name filename + * @return scaled image + */ + public static ImageIcon getLargeIcon( String name ) + { + return getScaledIco( name, largeIconSize, largeIconSize ); + } + + /** + * Load a custom mouse cursor by filename + * @param file filename + * @return a mouse cursor + */ + public static Cursor getCustomCursor( String file ) + { + return Toolkit.getDefaultToolkit().createCustomCursor( + getScaledIco( "cursors/" + file, 32, 32 ).getImage(), + new Point( 0, 0 ), "custom cursor" ); + } + + /** + * Converts a name to a CamelCase string only with alphanumeric characters + * @param text a name + * @return CamelCase representation + */ + public static String camelCase( String text ) + { + String[] words = text.split( "[^\\w]+" ); // split into words + words = Stream.of( words ) + .map( s -> Character.toUpperCase( s.charAt( 0 ) ) + s.substring( 1 ) ) + .toArray( String[]::new ); // to versals + + return String.join( "", words ); + } + + /** + * Sets preferred, maximum, minimum size to a fixed size + * @param c component to fix + * @param w width + * @param h height + * @return the same component c + */ + public static Component fixSize( Component c, int w, int h ) + { + c.setPreferredSize( new Dimension( w, h ) ); + c.setMaximumSize( c.getPreferredSize() ); + c.setMinimumSize( c.getPreferredSize() ); + + return c; + } + + /** + * Improve quality render hints + * @param g2 graphics + */ + public static void setHighQualityRender( Graphics2D g2 ) + { + g2.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON ); + g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON ); + g2.setRenderingHint( RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY ); + g2.setRenderingHint( RenderingHints.KEY_ALPHA_INTERPOLATION, + RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY ); + g2.setRenderingHint( RenderingHints.KEY_COLOR_RENDERING, + RenderingHints.VALUE_COLOR_RENDER_QUALITY ); + g2.setRenderingHint( RenderingHints.KEY_DITHERING, + RenderingHints.VALUE_DITHER_ENABLE ); + g2.setRenderingHint( RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + } + +} diff --git a/src/jdrafting/gui/JDraftingArgs.java b/src/jdrafting/gui/JDraftingArgs.java new file mode 100644 index 0000000..797266d --- /dev/null +++ b/src/jdrafting/gui/JDraftingArgs.java @@ -0,0 +1,309 @@ +package jdrafting.gui; + +import static java.lang.System.out; + +import java.awt.Color; +import java.util.Locale; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.swing.JFrame; +import javax.swing.UIManager; +import javax.swing.UIManager.LookAndFeelInfo; + +import cla.CLAParser; +import cla.ParamExecutor; +import cla.Parameter; + +/** + * Command line parser + * (uses CLA; https://github.com/miguelalejandromorenobarrientos/CLA ) + * Note: Some parameter executors need an instance of Application and the + * others need to modify static fields before start GUI, these ones must + * receive null as app + */ +public class JDraftingArgs extends CLAParser +{ + private Application app; + + public JDraftingArgs( Application a ) + { + app = a; + + // parameter -help + addDefaultHelpParameter( + out, + "\n" + Application.APPNAME + " console args description:\n", + "help", "-" ); + ParamExecutor defaultExecutor = getParameter( "help" ).getExecutor(); + getParameter( "help" ).setExecutor( (param,values) -> { + defaultExecutor.execute( param, values ); + System.exit( 0 ); + }); + // parameter -lang + addParameter( new Parameter( + "lang", + "-", "", + "Select language" + + " (must exist 'language_??.properties' on resources folder)." + + " Usage: -lang language", + false, + 1, 1, + Stream.of( Locale.getAvailableLocales() ) + .map( Locale::toString ) + .collect( Collectors.toList() ), + (param,values) -> { + if ( app != null ) return; + Application.locale = new Locale( values.get( 0 ) ); + Locale.setDefault( Application.locale ); + out.println( "lang set to " + Application.locale.getDisplayName() ); + }, + 1 ) ); + // parameter -lookfeel + addParameter( new Parameter( + "lookfeel", + "-", "", + "Select look and feel. Usage: -lookfeel installed_look&feel", + false, + 1, 1, + Stream.of( UIManager.getInstalledLookAndFeels() ) + .map( LookAndFeelInfo::getClassName ) + .collect( Collectors.toList() ), + (param,values) -> { + if ( app != null ) return; + Application.lookAndFeelClassName = values.get( 0 ); + out.println( "L&F set to " + Application.lookAndFeelClassName ); + }, + 2 ) ); + // parameter -size + addParameter( new Parameter( + "size", + "-", "", + "Initial size. Usage: -size width height / size [iconified]", + false, + 1, 2, + null, + (param,values) -> { + if ( app == null ) return; + int n = values.size(); + if ( n == 1 ) + { + String value = values.get( 0 ); + if ( value.equals( "iconified" ) ) + app.setExtendedState( JFrame.ICONIFIED ); + else if ( value.equals( "normal" ) ) + app.setExtendedState( JFrame.NORMAL ); + else if ( value.equals( "horiz" ) ) + app.setExtendedState( JFrame.MAXIMIZED_HORIZ ); + else if ( value.equals( "vert" ) ) + app.setExtendedState( JFrame.MAXIMIZED_VERT ); + } + else + { + app.setExtendedState( JFrame.NORMAL ); + int w = Integer.parseInt( values.get( 0 ) ); + int h = Integer.parseInt( values.get( 1 ) ); + app.setSize( w, h ); + out.printf( "size set to %sx%s\n", w, h ); + } + }, + 5 ) ); + // parameter -canvascolor + addParameter( new Parameter( + "canvascolor", + "-", "", + "Initial canvas color. " + + "Usage: -canvascolor html_color / -canvascolor red green blue", + false, + 1, 3, + null, + (param,values) -> { + if ( app == null ) return; + int n = values.size(); + Color c; + if ( n == 1 ) // html format + c = Color.decode( values.get( 0 ) ); + else // RGB + { + int r = Integer.parseInt( values.get( 0 ) ); + int g = Integer.parseInt( values.get( 1 ) ); + int b = 0; + if ( n == 3 ) + b = Integer.parseInt( values.get( 2 ) ); + c = new Color( r, g, b ); + } + app.setBackColor( c ); + out.println( "Canvas color set to " + c ); + }, + 10 ) ); + // parameter -color + addParameter( new Parameter( + "color", + "-", "", + "Initial color. " + + "Usage: -canvascolor htmlColor / -canvascolor red green blue", + false, + 1, 3, + null, + (param,values) -> { + if ( app == null ) return; + int n = values.size(); + Color c; + if ( n == 1 ) // html format + c = Color.decode( values.get( 0 ) ); + else // RGB + { + int r = Integer.parseInt( values.get( 0 ) ); + int g = Integer.parseInt( values.get( 1 ) ); + int b = 0; + if ( n == 3 ) + b = Integer.parseInt( values.get( 2 ) ); + c = new Color( r, g, b ); + } + app.setColor( c ); + out.println( "Color set to " + c ); + }, + 20 ) ); + // parameter -pointcolor + addParameter( new Parameter( + "pointcolor", + "-", "", + "Initial point color. " + + "Usage: -canvascolor htmlColor / -canvascolor red green blue", + false, + 1, 3, + null, + (param,values) -> { + if ( app == null ) return; + int n = values.size(); + Color c; + if ( n == 1 ) // html format + c = Color.decode( values.get( 0 ) ); + else // RGB + { + int r = Integer.parseInt( values.get( 0 ) ); + int g = Integer.parseInt( values.get( 1 ) ); + int b = 0; + if ( n == 3 ) + b = Integer.parseInt( values.get( 2 ) ); + c = new Color( r, g, b ); + } + app.setPointColor( c ); + out.println( "Point color set to " + c ); + }, + 30 ) ); + // parameter -toolcolor + addParameter( new Parameter( + "toolcolor", + "-", "", + "Tool color. " + + "Usage: -canvascolor htmlColor / -canvascolor red green blue", + false, + 1, 3, + null, + (param,values) -> { + if ( app != null ) return; + int n = values.size(); + Color c; + if ( n == 1 ) // html format + c = Color.decode( values.get( 0 ) ); + else // RGB + { + int r = Integer.parseInt( values.get( 0 ) ); + int g = Integer.parseInt( values.get( 1 ) ); + int b = 0; + if ( n == 3 ) + b = Integer.parseInt( values.get( 2 ) ); + c = new Color( r, g, b ); + } + Application.toolMainColor = c; + out.println( "Tool main color set to " + c ); + }, + 30 ) ); + // parameter -flatness + addParameter( new Parameter( + "flatness", + "-", "", + "Accuracy in curves. Usage: -flatness value", + false, + 1, 1, + null, + (param,values) -> { + if ( app == null ) return; + double flatness = Double.parseDouble( values.get( 0 ) ); + app.setFlatnessValue( flatness ); + out.println( "Flatness value set to " + flatness ); + }, + 150 ) ); + // parameter -angle + addParameter( new Parameter( + "angle", + "-", "", + "Tool angle start value. Usage: -angle start_angle", + false, + 1, 1, + null, + (param,values) -> { + if ( app == null ) return; + double angle = Double.parseDouble( values.get( 0 ) ); + app.setAngle( angle ); + out.println( "Angle set to " + angle ); + }, + 200 ) ); + // parameter -iconsize + addParameter( new Parameter( + "iconsize", + "-", "", + "Icons size (pixels by side). Usage: -iconsize smallsize largesize", + false, + 1, 2, + null, + (param,values) -> { + if ( app != null ) return; + JDUtils.smallIconSize = Integer.parseInt( values.get( 0 ) ); + if ( values.size() == 2 ) + JDUtils.largeIconSize = Integer.parseInt( values.get( 1 ) ); + }, + 300 ) ); + // parameter -file + addParameter( new Parameter( + "file", + "-", "", + "Load filename. Usage: -file filename", + false, + 1, 1, + null ) ); + // parameter -jme + addParameter( new Parameter( + "jme", + ":)", "", + "enable jme", + false, + 1, 1, + Stream.of( new String[] { "enabled", "disabled" } ) + .collect( Collectors.toList() ), + (param,values) -> { + if ( app != null ) return; + Application.jmeEnabled = values.get( 0 ).equals( "enabled" ); + out.println( "Easter egg jme loaded" ); + }, + 400 ) ); + // parameter -version + addParameter( new Parameter( + "version", + "-", "", + "app version", + false, + 0, 0, + null, + (param,values) -> { + if ( app != null ) return; + out.printf( "%s v%s, @%s\n", Application.APPNAME, + Application.VERSION, Application.COPYLEFT ); + System.exit( 0 ); + }, + Integer.MIN_VALUE + 1 ) ); + } + + public void setApp( Application app ) { this.app = app; } +} diff --git a/src/jdrafting/gui/Launcher.java b/src/jdrafting/gui/Launcher.java new file mode 100644 index 0000000..3a4b959 --- /dev/null +++ b/src/jdrafting/gui/Launcher.java @@ -0,0 +1,51 @@ +package jdrafting.gui; + +import java.io.File; +import java.util.NoSuchElementException; + +import javax.swing.SwingUtilities; + +import cla.ParsedParameterMap; + +public class Launcher +{ + /** + * Launch GUI + * @param args %1 filename to load + */ + public static void main( String[] args ) + { + SwingUtilities.invokeLater( () -> { + try + { + // parse and execute parameters before app instantiation + // (some parameters like lang, lookfeel need to be executed + // before app intantiation) + final JDraftingArgs argsParser = new JDraftingArgs( null ); + final ParsedParameterMap parsedMap = argsParser.parseAndExecute( args ); + + // application instance + final Application app = new Application(); + + // parse and execute parameters after app instantiation + argsParser.setApp( app ); + argsParser.execute( parsedMap ); + + // launch app + app.setVisible( true ); + + // load file from console + // (file load must be executed with a visible app) + if ( parsedMap.containsParam( "file" ) ) + app.openFile( new File( parsedMap.getValues( "file" )[0] ) ); + } + catch ( NoSuchElementException e ) + { + System.out.printf( + "Launch error (%s: %s)\n-help for parameter info", + e.getClass().getSimpleName(), e.getMessage() ); + System.exit( -1 ); + } + } ); + } +} diff --git a/src/jdrafting/gui/LineStyleComboBox.java b/src/jdrafting/gui/LineStyleComboBox.java new file mode 100644 index 0000000..d419e85 --- /dev/null +++ b/src/jdrafting/gui/LineStyleComboBox.java @@ -0,0 +1,95 @@ +package jdrafting.gui; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Arrays; + +import javax.swing.JComboBox; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.ListCellRenderer; + +import jdrafting.geom.JDStrokes; + +@SuppressWarnings("serial") +public class LineStyleComboBox extends JComboBox +{ + private Application app; + + public LineStyleComboBox( Application app ) + { + super( Arrays.stream( JDStrokes.values() ) + .map( v -> v.getStroke() ) + .toArray( BasicStroke[]::new ) ); + this.app = app; + + setBackground( new Color( 200, 200, 255 ) ); + + addActionListener( new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) + { + if ( e.getSource() instanceof JComboBox ) + { + JComboBox combo = (JComboBox) e.getSource(); + app.setStroke( JDStrokes.getStroke( + (BasicStroke) combo.getSelectedItem(), + app.getStroke().getLineWidth() ) ); + } + } + } ); + } + + @Override + public ListCellRenderer getRenderer() + { + return new LineRenderer(); + } + + + private class LineRenderer extends JPanel implements + ListCellRenderer + { + private BasicStroke stroke; + + LineRenderer() + { + setBackground( new Color( 222, 222, 255 ) ); + } + + @Override + public Component getListCellRendererComponent( + JList list, BasicStroke value, int index, + boolean isSelected, boolean cellHasFocus ) + { + stroke = value; + + return this; // render + } + + @Override + protected void paintComponent(Graphics g) + { + super.paintComponent(g); + + Graphics2D g2 = (Graphics2D) g; + g2.setColor( app.getColor() ); + g2.setStroke( JDStrokes.getStroke( stroke, + Math.min( app.getStroke().getLineWidth(), 12 ) ) ); + g2.drawLine( 8, getHeight() / 2, + (int) getPreferredSize().getWidth() - 9, getHeight() / 2 ); + } + + @Override + public Dimension getPreferredSize() + { + return LineStyleComboBox.this.getSize(); + } + } +} diff --git a/src/jdrafting/gui/ShapeList.java b/src/jdrafting/gui/ShapeList.java new file mode 100644 index 0000000..88ac109 --- /dev/null +++ b/src/jdrafting/gui/ShapeList.java @@ -0,0 +1,203 @@ +package jdrafting.gui; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.util.HashSet; + +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.DefaultListModel; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.ListCellRenderer; +import javax.swing.ListSelectionModel; +import javax.swing.border.BevelBorder; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import jdrafting.geom.JDraftingShape; + +@SuppressWarnings("serial") +public class ShapeList extends JList +{ + private Application app; + + public ShapeList( Application app ) + { + super( new DefaultListModel() ); + + this.app = app; + + setSelectionMode( ListSelectionModel.MULTIPLE_INTERVAL_SELECTION ); + + // listeners + addMouseListener( new ShapeListMouseAdapter() ); + getSelectionModel().addListSelectionListener( + new ShapeListSelectionListener() ); + } + + @Override + public ListCellRenderer getCellRenderer() + { + return new ShapeListRenderer(); + } + + @Override + public DefaultListModel getModel() + { + return (DefaultListModel) super.getModel(); + } + + /** + * MouseAdapter + */ + class ShapeListMouseAdapter extends MouseAdapter + { + @Override + public void mousePressed( MouseEvent e ) + { + if ( e.getClickCount() > 1 ) + { + // get selected shape + final int index = ShapeList.this.getSelectionModel().getLeadSelectionIndex(); + final JDraftingShape jdshape = ShapeList.this.getModel().get( index ); + // edit shape + new EditShapeDialog( app, jdshape ).setVisible( true ); + e.consume(); + } + if ( e.getButton() == MouseEvent.BUTTON3 ) + { + // TODO PopupMenu? + e.consume(); + } + } + } + + /** + * ListSelectionListener + */ + class ShapeListSelectionListener implements ListSelectionListener + { + @Override + public void valueChanged( ListSelectionEvent e ) + { + final ListSelectionModel lsm = (ListSelectionModel) e.getSource(); + final int first = lsm.getMinSelectionIndex(), + last = lsm.getMaxSelectionIndex(); + + app.setSelectedShapes( new HashSet<>() ); + + for ( int index = first; index <= last; index++ ) + if ( lsm.isSelectedIndex( index ) ) + app.getSelectedShapes().add( getModel().get( index ) ); + + // TODO how to always force valueChanged? + + app.getCanvas().repaint(); + app.scrollList.repaint(); + } + } + + /** + * ListCellRenderer + */ + class ShapeListRenderer extends JPanel implements ListCellRenderer + { + @Override + public Component getListCellRendererComponent( + JList list, JDraftingShape value, + int index, boolean isSelected, boolean cellHasFocus ) + { + removeAll(); + setLayout( new BoxLayout( this, BoxLayout.LINE_AXIS ) ); + setOpaque( true ); + setBorder( BorderFactory.createBevelBorder( BevelBorder.RAISED ) ); + // index + JLabel shapeIndex = new JLabel( index + 1 + ":" ); + add( shapeIndex ); + shapeIndex.setFont( new Font( Font.SANS_SERIF, Font.ITALIC, 14 ) ); + shapeIndex.setForeground( Color.BLACK ); + // miniature + add( Box.createHorizontalStrut( 2 ) ); + JPanel mini = new JPanel() { + @Override + protected void paintComponent( Graphics g ) + { + setBackground( app.getBackColor() ); + super.paintComponent(g); + + Graphics2D g2 = (Graphics2D) g; + + // High quality render + JDUtils.setHighQualityRender( g2 ); + + Viewport viewDest = new Viewport( 0, getWidth() - 1, + 0, getHeight() - 1 ); + Rectangle2D bounds = CanvasPanel.getExerciseBounds( + app.getExercise(), + new Viewport( app.getExercise().getBounds() ), + viewDest ); + + double size = + Math.max( bounds.getWidth(), bounds.getHeight() ); + bounds = new Rectangle2D.Double( + bounds.getCenterX() - size / 2., + bounds.getCenterY() - size / 2., + size, size ); + AffineTransform transform = CanvasPanel.getTransform( + new Viewport( bounds ), viewDest ); + HashSet selected = new HashSet<>(); + selected.add( value ); + CanvasPanel.drawExercise( g2, transform, app.getExercise(), + selected, false ); + } + }; + add( mini ); + mini.setPreferredSize( new Dimension( 64, 64 ) ); + mini.setMaximumSize( mini.getPreferredSize() ); + mini.setMinimumSize( mini.getPreferredSize() ); + // name + add( Box.createHorizontalStrut( 4 ) ); + JLabel shapeName = new JLabel( value.getName() != null && value.getName().length() > 0 + ? value.getName() + : "", JLabel.LEFT ); + add( shapeName ); + shapeName.setFont( new Font( Font.SANS_SERIF, Font.BOLD, 20 ) ); + shapeName.setForeground( Color.BLUE ); + // description + add( Box.createHorizontalStrut( 4 ) ); + JLabel shapeDesc = new JLabel( "", JLabel.LEFT ); + add( shapeDesc ); + if ( value.getDescription().length() > 0 ) + { + shapeDesc.setText( "" + String.join( "
", + value.getDescription().split( "\n" ) ) + "" ); + setToolTipText( shapeDesc.getText() ); + } + + shapeDesc.setFont( new Font( Font.SANS_SERIF, Font.PLAIN, 12 ) ); + shapeDesc.setForeground( Color.GRAY ); + + // background color + if ( app.getSelectedShapes().contains( value ) ) // selected + setBackground( new Color( 255, 127, 127 ) ); + else if ( !app.getExercise().isIndexAtEnd() // visible + && index >= app.getExercise().getFrameIndex() ) + setBackground( Color.LIGHT_GRAY ); + else + setBackground( ShapeList.this.getBackground() ); + + return this; + } + } +} diff --git a/src/jdrafting/gui/Toast.java b/src/jdrafting/gui/Toast.java new file mode 100644 index 0000000..53a44a2 --- /dev/null +++ b/src/jdrafting/gui/Toast.java @@ -0,0 +1,198 @@ +// This file is part of Toast. +// +// Toast 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. +// +// Toast 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 Toast. If not, see . +package jdrafting.gui; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Point; + +import javax.swing.BorderFactory; +import javax.swing.JLabel; +import javax.swing.JWindow; +import javax.swing.Timer; + +/** + * Customizable Toast for Swing.
+ *
+ * Toast doesn't block EDT (Event Dispatch Thread). + * + * @author Miguel Alejandro Moreno Barrientos, (C) 2019-2020,2021 + * @version 0.1.11.1 (modified for JDrafting) + */ +public class Toast extends JWindow +{ + private static final long serialVersionUID = 1L; + + public static final int HALF_SECOND = 500, + ONE_SECOND = 1000, + TWO_SECONDS = 2000, + FOUR_SECONDS = 4000; + + private int time; // time until start to disappear + + private Timer closingTimer; + + /** label to show */ + protected JLabel toastLabel; + /** message */ + protected String msg; + /** toast location (when developer gives the coordinates) */ + protected Point point = null; + /** max message length shown */ + protected int maxLength = 200; + + /** + * Create toast x-centered and y-south + * + * @param msg message to show + * @param time time for hiding (milliseconds) + */ + public Toast( String msg, int time ) + { + this.msg = msg; + this.time = time; + + setAlwaysOnTop( true ); + + // initialize JLabel toast + toastLabel = new JLabel( cutString( msg, maxLength, "\u2026" ) ); + toastLabel.setOpaque(true); + toastLabel.setBorder( BorderFactory.createEmptyBorder(10, 10, 10, 10) ); + toastLabel.setFont( new Font( Font.SANS_SERIF, Font.BOLD, 18 ) ); + toastLabel.setForeground( Color.WHITE ); + toastLabel.setBackground( Color.BLACK ); + add( toastLabel ); + } + + /** + * Create toast in point (left-upper corner) + * + * @param msg message to show + * @param point screen position (left-upper corner) + * @param time time for hiding (milliseconds) + */ + public Toast( String msg, Point point, int time ) + { + this( msg, time ); + + this.point = point; + } + + /** + * Returns inner label.
+ * Color, Font, Background can be modified + * (other changes could cause malfunction) + * + * @return inner label + */ + public JLabel getToastLabel() { return toastLabel; } + + /** + * Max msg length + * + * @return max string length + */ + public int getMaxLength() { return maxLength; } + /** + * Sets max msg length (too large string can overflow screen) + * + * @param maxLength {@code >=0} + * @return the toast + */ + public Toast setMaxLength( int maxLength ) + { + this.maxLength = maxLength; + toastLabel.setText( cutString( msg, maxLength, "\u2026" ) ); + return this; + } + + /** + * @return toast message + */ + public String getMsg() { return msg; } + + /** + * @return time for hiding + */ + public int getTime() { return time; } + + public Timer getClosingTimer() { return closingTimer; } + + /** + * Show toast. Can be called several times + * + * @return the toast + */ + public Toast showToast() + { + pack(); + + // initial opacity + setOpacity(0.8f); + + // show toast + setVisible(true); + + // location + if ( point == null ) // location x-centered and y-south + { + setLocationRelativeTo( null ); + setLocation( getLocation().x, + getLocation().y + Math.round( + getToolkit().getScreenSize().height * 0.33f ) ); + } + else + setLocation( point ); + + // closing toast + closingTimer = new Timer( time, e -> { + final Timer disappearTimer = new Timer( 20, evt -> { + // hide transition + if ( getOpacity() > 0.05f ) + setOpacity( getOpacity() - 0.02f ); + // stop timer and close toast + else + { + dispose(); + setVisible( false ); + ((Timer) evt.getSource()).stop(); + } + }); // disappearTimer + disappearTimer.start(); + }); // closingTimer + closingTimer.setRepeats(false); + closingTimer.start(); + + return this; + } + + /** + * Cut string from limit (not including HTML tags) + * @param s message to cut + * @param limit max length of the original string (must be >= 0) + * @param sufix string at the end (as \u2026) + * @return cut string + */ + protected static String cutString( String s, int limit, String sufix ) + { + int lengthHTML = s.length(), + lengthNoHTML = s.replaceAll("\\<[^>]*>","").length(); // not perfect way + + return lengthNoHTML <= limit || s == null + ? s + : s.substring(0, limit + lengthHTML - lengthNoHTML - "".length() - 1) + sufix; + } + +} diff --git a/src/jdrafting/gui/ToastCanvasStep.java b/src/jdrafting/gui/ToastCanvasStep.java new file mode 100644 index 0000000..79457eb --- /dev/null +++ b/src/jdrafting/gui/ToastCanvasStep.java @@ -0,0 +1,52 @@ +package jdrafting.gui; + +import java.awt.Point; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.BorderFactory; + +import jdrafting.geom.JDraftingShape; + +/** + * Toast in canvas when move steps in exercise, showing current shape name and description + * @since 0.1.10.1 + */ +public class ToastCanvasStep extends Toast +{ + private static final long serialVersionUID = 1L; + + private static final int TIME = 5000; // ms + protected int maxLength = 1000; + + public ToastCanvasStep( JDraftingShape shape, int index, Point location ) + { + super( shape.getDescription().isEmpty() + ? " " + : "" + + String.join( "
", shape.getDescription().split( "\n" ) ) + + "", + new Point( location.x + 20, location.y + 15 ), + TIME ); + + // border and title + getToastLabel().setBorder( BorderFactory.createTitledBorder( + String.format( "%s: " + + "%s", + index, shape.getName() ) ) ); + + // pause toast when mouse enter and restart at exit + addMouseListener( new MouseAdapter() { + @Override + public void mouseEntered( MouseEvent e ) + { + ToastCanvasStep.this.getClosingTimer().stop(); + } + @Override + public void mouseExited( MouseEvent e ) + { + ToastCanvasStep.this.getClosingTimer().restart(); + } + }); + } +} diff --git a/src/jdrafting/gui/UndoRedoHistoryList.java b/src/jdrafting/gui/UndoRedoHistoryList.java new file mode 100644 index 0000000..a2d4dd9 --- /dev/null +++ b/src/jdrafting/gui/UndoRedoHistoryList.java @@ -0,0 +1,133 @@ +package jdrafting.gui; + +import java.awt.Color; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.HashSet; + +import javax.swing.BorderFactory; +import javax.swing.DefaultListModel; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.ListCellRenderer; +import javax.swing.ListSelectionModel; +import javax.swing.undo.UndoableEdit; + +import jdrafting.gui.Application.ExtendedUndoManager; + +public class UndoRedoHistoryList extends JList +{ + private static final long serialVersionUID = 1L; + + final Application app; + + public UndoRedoHistoryList( final Application app ) + { + super( new DefaultListModel() ); + + this.app = app; + + setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); + + update(); + + addKeyListener( new KeyAdapter() { + @Override + public void keyReleased( KeyEvent e ) + { + if ( e.getKeyCode() == KeyEvent.VK_ENTER ) + action(); + } + } ); + + addMouseListener( new MouseAdapter() { + public void mouseClicked( MouseEvent e) + { + if ( e.getClickCount() == 2 ) + action(); + }; + }); + } + + private void action() + { + final UndoableEdit edit = getSelectedValue(); + if ( edit == null ) + return; + + final ExtendedUndoManager um = (ExtendedUndoManager) app.undoManager; + + final int idxSelected = um.getEdits().indexOf( edit ), + idxRedone = um.editToBeRedone() == null + ? um.size() + : um.getEdits().indexOf( um.editToBeRedone() ); + + if ( idxSelected < idxRedone ) + for ( int i = idxSelected; i < idxRedone; i++, um.undo() ); + else + for ( int i = idxRedone; i <= idxSelected; i++, um.redo() ); + + if ( app.undoRedoWindow != null ) + app.undoRedoWindow.dispose(); + + app.setSelectedShapes( new HashSet<>() ); + + app.canvas.repaint(); + } + + @Override + public DefaultListModel getModel() + { + return (DefaultListModel) super.getModel(); + } + + private void update() + { + getModel().removeAllElements(); + + final ExtendedUndoManager undoManager = (ExtendedUndoManager) app.undoManager; + + for ( final UndoableEdit edit : undoManager.getEdits() ) + getModel().addElement( edit ); + } + + @Override + public ListCellRenderer getCellRenderer() + { + return new ListCellRenderer() { + @Override + public JLabel getListCellRendererComponent( JList list, + UndoableEdit value, + int index, + boolean isSelected, + boolean cellHasFocus ) + { + final ExtendedUndoManager um = (ExtendedUndoManager) app.undoManager; + final int idxRedone = um.editToBeRedone() == null + ? um.size() + : um.getEdits().indexOf( um.editToBeRedone() ); + + final JLabel lbl = new JLabel( String.format( "%s", + index < idxRedone + ? value.getUndoPresentationName() + : value.getRedoPresentationName() ) ); + lbl.setForeground( index < idxRedone + ? new Color( 127, 0, 0 ) // red for undo + : new Color( 0, 127, 0 ) ); // green for redo + lbl.setBorder( BorderFactory.createEmptyBorder( 2, 3, 2, 3 ) ); + + if ( um.editToBeRedone() == value ) + lbl.setBackground( Color.LIGHT_GRAY ); + + lbl.setOpaque( true ); + if ( isSelected ) + lbl.setBackground( Color.CYAN ); + + return lbl; + } + }; + } + +} diff --git a/src/jdrafting/gui/Viewport.java b/src/jdrafting/gui/Viewport.java new file mode 100644 index 0000000..b858c91 --- /dev/null +++ b/src/jdrafting/gui/Viewport.java @@ -0,0 +1,127 @@ +package jdrafting.gui; + +import static java.lang.String.format; + +import java.awt.geom.Rectangle2D; + +/** + * Logic viewport + * @author Miguel Alejandro Moreno Barrientos + * @since 0.1.0 + */ +public class Viewport +{ + private double xmin, ymin, xmax, ymax; + + /** + * Viewport from x extremes and y extremes + * @param xmin min x + * @param xmax max x + * @param ymin min y + * @param ymax max y + */ + public Viewport( double xmin, double xmax, double ymin, double ymax ) + { + this.xmin = xmin; + this.xmax = xmax; + this.ymin = ymin; + this.ymax = ymax; + } + + /** + * Default viewport [-1000,1000]x[-1000,1000] + */ + public Viewport() + { + this( -1000., 1000., -1000., 1000. ); + } + + /** + * Viewport from Rectangle2D + * @param r rectangle + */ + public Viewport( Rectangle2D r ) + { + this( r.getMinX(), r.getMaxX(), r.getMinY(), r.getMaxY() ); + } + + public double getMinX() { return xmin; } + public double getMaxX() { return xmax; } + public double getMinY() { return ymin; } + public double getMaxY() { return ymax; } + + public double getWidth() { return getMaxX() - getMinX(); } + public double getHeight() { return getMaxY() - getMinY(); } + public double getCenterX() { return ( getMinX() + getMaxX() ) / 2; } + public double getCenterY() { return ( getMinY() + getMaxY() ) / 2; } + + /** + * Zoom over viewport in {@code (x,y)} point + * @param x coord x + * @param y coord y + * @param factor zoom factor; >1 for zoom out, <1 for zoom in + * @return this viewport zoomed + */ + public Viewport zoom( double x, double y, double factor ) + { + double oldwidth = getWidth(); + double oldheight = getHeight(); + xmin = x + ( xmin - x ) * factor; + ymin = y + ( ymin - y ) * factor; + xmax = xmin + oldwidth * factor; + ymax = ymin + oldheight * factor; + + return this; + } + + /** + * Translate viewport + * @param dx delta x + * @param dy delta y + * @return this viewport translated + */ + public Viewport move( double dx, double dy ) + { + xmin += dx; + xmax += dx; + ymin += dy; + ymax += dy; + + return this; + } + + /** + * Coordinates at this viewport from coordinates in other viewport + * @param x old x + * @param y old y + * @param other old viewport + * @return [x,y] at this viewport + */ + public double[] toThisViewport( double x, double y, Viewport other ) + { + double nx = getMinX() + ( ( x - other.getMinX() ) / other.getWidth() ) + * getWidth(); + double ny = getMinY() + ( ( y - other.getMinY() ) / other.getHeight() ) + * getHeight(); + + return new double[] { nx, ny }; + } + + /** + * Get viewport as java.awt.geom.Rectangle2D (Double) + * @return rectangle + */ + public Rectangle2D getAsRectangle() + { + return new Rectangle2D.Double( getMinX(), getMaxY(), getWidth(), getHeight() ); + } + + @Override + public String toString() + { + return format( "[xmin:%s,xmax:%s,ymin:%s,ymax:%s,width:%s,height:%s]", + getMinX(), getMaxX(), getMinY(), getMaxY(), + getWidth(), getHeight() ); + } + +} diff --git a/src/jdrafting/gui/controller/actions/AboutAction.java b/src/jdrafting/gui/controller/actions/AboutAction.java new file mode 100644 index 0000000..1f06ff1 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/AboutAction.java @@ -0,0 +1,103 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.Desktop; +import java.awt.event.ActionEvent; +import java.io.IOException; +import java.net.URISyntaxException; + +import javax.swing.AbstractAction; +import javax.swing.JEditorPane; +import javax.swing.JOptionPane; +import javax.swing.KeyStroke; +import javax.swing.event.HyperlinkEvent; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; + +@SuppressWarnings("serial") +public class AboutAction extends AbstractAction +{ + private Application app; + + public AboutAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "about" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_about" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( "F1" ) ); + putValue( SMALL_ICON, getSmallIcon( "jdrafting.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "jdrafting.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + final JEditorPane ep = new JEditorPane( "text/html", "" + + "

" + + Application.APPNAME + + "
" + + "v" + + Application.VERSION + + "
" + + "Author " + + Application.AUTHOR + + "

" + + "

" + + Application.APPNAME + " " + + getLocaleText( "app_des" ) + + "

" + + "

" + + "GPLv3 license " + + "" + + "http://www.gnu.org/licenses/gpl-3.0.html" + + "" + + "

" + + "

" + + "
SVG export: " + + "" + + "Apache Batik" + + "

" + + "

" + + "Command line parser: " + + "" + + "CLA" + + "

" + + "

" + + "Toast in canvas for step description: " + + "" + + "Toast" + + "

" + + "

" + + "Math parser for functions: " + + "" + + "JME parser" + + "

" + + "

" + + "
(C)" + Application.COPYLEFT + + "

" + + "" ); + ep.setEditable( false ); + ep.setOpaque( false ); + ep.getCaret().deinstall( ep ); // non-selectable + ep.addHyperlinkListener( ev -> { + if ( ev.getEventType().equals( HyperlinkEvent.EventType.ACTIVATED ) + && Desktop.isDesktopSupported() + && Desktop.getDesktop().isSupported( Desktop.Action.BROWSE ) ) + { + try { Desktop.getDesktop().browse( ev.getURL().toURI() ); } + catch ( URISyntaxException | IOException ignored ) {} + } + }); + JOptionPane.showMessageDialog( app, ep, "About " + Application.APPNAME, + JOptionPane.PLAIN_MESSAGE, + JDUtils.getScaledIco( "jdrafting.png", 100, 100 ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/AngleAction.java b/src/jdrafting/gui/controller/actions/AngleAction.java new file mode 100644 index 0000000..4c993eb --- /dev/null +++ b/src/jdrafting/gui/controller/actions/AngleAction.java @@ -0,0 +1,41 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.AngleListener; + +@SuppressWarnings("serial") +public class AngleAction extends AbstractAction +{ + private Application app; + + public AngleAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "angle" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "angle_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_angle" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_5, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "angle.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "angle.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( new AngleListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/ArcAction.java b/src/jdrafting/gui/controller/actions/ArcAction.java new file mode 100644 index 0000000..242285d --- /dev/null +++ b/src/jdrafting/gui/controller/actions/ArcAction.java @@ -0,0 +1,41 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.ArcListener; + +@SuppressWarnings("serial") +public class ArcAction extends AbstractAction +{ + private Application app; + + public ArcAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "arc" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "arc_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_arc" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_3, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "arc.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "arc.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( new ArcListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/AreaIntersectionAction.java b/src/jdrafting/gui/controller/actions/AreaIntersectionAction.java new file mode 100644 index 0000000..21577cb --- /dev/null +++ b/src/jdrafting/gui/controller/actions/AreaIntersectionAction.java @@ -0,0 +1,111 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.Shape; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.geom.Area; +import java.util.HashSet; +import java.util.stream.Collectors; + +import javax.swing.AbstractAction; +import javax.swing.JOptionPane; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +import static jdrafting.gui.JDUtils.elvis; + +/** + * Shape from area intersections + * @author Miguel Alejandro Moreno Barrientos, (C)2021 + * @since 0.1.11.3 + */ +@SuppressWarnings("serial") +public class AreaIntersectionAction extends AbstractAction +{ + private Application app; + + public AreaIntersectionAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "area_intersection" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "area_intersection_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_fusion" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( + KeyEvent.VK_4, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "area_intersection.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "area_intersection.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + // check two or more selected shapes + if ( app.getSelectedShapes().size() < 2 ) + { + JOptionPane.showMessageDialog( app, getLocaleText( "sel_2_error" ), + getLocaleText( "area_intersection" ), + JOptionPane.ERROR_MESSAGE ); + return; + } + + // shape enum for description + final String descHtml = "{" + String.join( ",", app.getSelectedShapes() + .parallelStream() + .map( jdshape -> "[" + elvis( jdshape.getName(), "?" ) + + "]" ) + .collect( Collectors.toList() ) ) + "}"; + + //////////////////////////// TRANSACTION //////////////////////////// + final JDCompoundEdit transaction = new JDCompoundEdit( + getLocaleText( "area_intersection" ) + " " + descHtml ); + + // create intersection area + final Area intersectionArea = app.getSelectedShapes().parallelStream() + .map( jds -> new Area( jds.getShape() ) ) + .reduce( (area1,area2) -> { area1.intersect( area2 ); + return area1; } ) + .get(); + + + // check for empty intersection + if ( intersectionArea.isEmpty() ) + { + JOptionPane.showMessageDialog( app, getLocaleText( "empty_intersection_error" ), + getLocaleText( "area_intersection" ), + JOptionPane.ERROR_MESSAGE ); + return; + } + + // final shape + final Shape result = JDUtils.removeUnnecessarySegments( intersectionArea.isSingular() + ? JDUtils.closeShapeWithLine( intersectionArea, true ) + : intersectionArea ); + + // add new shape to exercise + app.addShapeFromIterator( result.getPathIterator( null ), "", + getLocaleText( "new_intersection" ) + " " + descHtml, app.getColor(), + null, app.getStroke(), transaction ); + + // post transaction + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + app.setSelectedShapes( new HashSet<>() ); + + // refresh + app.getCanvas().repaint(); + app.scrollList.repaint(); + } +} diff --git a/src/jdrafting/gui/controller/actions/AreaSubstractAction.java b/src/jdrafting/gui/controller/actions/AreaSubstractAction.java new file mode 100644 index 0000000..f5373bd --- /dev/null +++ b/src/jdrafting/gui/controller/actions/AreaSubstractAction.java @@ -0,0 +1,46 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.AreaSubstractListener; + +/** + * Shape from area substraction + * @author Miguel Alejandro Moreno Barrientos, (C)2021 + * @since 0.1.11.3 + */ +@SuppressWarnings("serial") +public class AreaSubstractAction extends AbstractAction +{ + private Application app; + + public AreaSubstractAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "area_substract" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "area_substract_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_fusion" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( + KeyEvent.VK_6, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "area_substract.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "area_substract.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + app.getCanvas().setCanvasListener( new AreaSubstractListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/AreaSymmetricDifferenceAction.java b/src/jdrafting/gui/controller/actions/AreaSymmetricDifferenceAction.java new file mode 100644 index 0000000..8a07620 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/AreaSymmetricDifferenceAction.java @@ -0,0 +1,109 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.elvis; +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.Shape; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.geom.Area; +import java.util.HashSet; +import java.util.stream.Collectors; + +import javax.swing.AbstractAction; +import javax.swing.JOptionPane; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +/** + * Shape from symmetric difference + * @author Miguel Alejandro Moreno Barrientos, (C)2021 + * @since 0.1.12 + */ +@SuppressWarnings("serial") +public class AreaSymmetricDifferenceAction extends AbstractAction +{ + private Application app; + + public AreaSymmetricDifferenceAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "area_sym_diff" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "area_sym_diff_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_sym_diff" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( + KeyEvent.VK_7, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "area_symmetric_substract.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "area_symmetric_substract.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + // check two or more selected shapes + if ( app.getSelectedShapes().size() < 2 ) + { + JOptionPane.showMessageDialog( app, getLocaleText( "sel_2_error" ), + getLocaleText( "area_sym_diff" ), + JOptionPane.ERROR_MESSAGE ); + return; + } + + // shape enum for description + final String descHtml = "{" + String.join( ",", app.getSelectedShapes() + .parallelStream() + .map( jdshape -> "[" + + elvis( jdshape.getName(), "?" ) + + "]" ) + .collect( Collectors.toList() ) ) + "}"; + + // create intersection area + final Area symdiffArea = app.getSelectedShapes().parallelStream() + .map( jds -> new Area( jds.getShape() ) ) + .reduce( (area1,area2) -> { area1.exclusiveOr( area2 ); + return area1; } ) + .get(); + + // check for empty area + if ( symdiffArea.isEmpty() ) + { + JOptionPane.showMessageDialog( app, getLocaleText( "empty_sym_diff_error" ), + getLocaleText( "area_sym_diff" ), + JOptionPane.ERROR_MESSAGE ); + return; + } + + // final shape + final Shape result = JDUtils.removeUnnecessarySegments( symdiffArea.isSingular() + ? JDUtils.closeShapeWithLine( symdiffArea, true ) + : symdiffArea ); + + //////////////////////////// TRANSACTION //////////////////////////// + final JDCompoundEdit transaction = new JDCompoundEdit( + getLocaleText( "area_sym_diff" ) + " " + descHtml ); + + // add new shape to exercise + app.addShapeFromIterator( result.getPathIterator( null ), "", + getLocaleText( "new_sym_diff" ) + " " + descHtml, app.getColor(), + null, app.getStroke(), transaction ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + app.setSelectedShapes( new HashSet<>() ); + + // refresh + app.getCanvas().repaint(); + app.scrollList.repaint(); + } +} diff --git a/src/jdrafting/gui/controller/actions/AreaUnionAction.java b/src/jdrafting/gui/controller/actions/AreaUnionAction.java new file mode 100644 index 0000000..fb81746 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/AreaUnionAction.java @@ -0,0 +1,108 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.elvis; +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.Shape; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.geom.Area; +import java.util.HashSet; +import java.util.stream.Collectors; + +import javax.swing.AbstractAction; +import javax.swing.JOptionPane; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +/** + * Shape from area union + * @author Miguel Alejandro Moreno Barrientos, (C)2021 + * @since 0.1.11.3 + */ +@SuppressWarnings("serial") +public class AreaUnionAction extends AbstractAction +{ + private Application app; + + public AreaUnionAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "area_union" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "area_union_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_fusion" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( + KeyEvent.VK_5, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "area_union.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "area_union.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + // check two or more selected shapes + if ( app.getSelectedShapes().size() < 2 ) + { + JOptionPane.showMessageDialog( app, getLocaleText( "sel_2_error" ), + getLocaleText( "area_union" ), + JOptionPane.ERROR_MESSAGE ); + return; + } + + // shape enum for description + final String descHtml = "{" + String.join( ",", app.getSelectedShapes() + .parallelStream() + .map( jdshape -> "[" + elvis(jdshape.getName(), "?") + + "]" ) + .collect( Collectors.toList() ) ) + "}"; + + // create intersection area + final Area unionArea = app.getSelectedShapes().parallelStream() + .map( jds -> new Area( jds.getShape() ) ) + .reduce( (area1,area2) -> { area1.add( area2 ); + return area1; } ) + .get(); + + // check for empty intersection + if ( unionArea.isEmpty() ) + { + JOptionPane.showMessageDialog( app, getLocaleText( "empty_union_error" ), + getLocaleText( "area_union" ), + JOptionPane.ERROR_MESSAGE ); + return; + } + + // final shape + final Shape result = JDUtils.removeUnnecessarySegments( unionArea.isSingular() + ? JDUtils.closeShapeWithLine( unionArea, true ) + : unionArea ); + + //////////////////////////// TRANSACTION //////////////////////////// + final JDCompoundEdit transaction = new JDCompoundEdit( + getLocaleText( "area_union" ) + " " + descHtml ); + + // add new shape to exercise + app.addShapeFromIterator( result.getPathIterator( null ), "", + getLocaleText( "new_union" ) + " " + descHtml, app.getColor(), + null, app.getStroke(), transaction ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + app.setSelectedShapes( new HashSet<>() ); + + // refresh + app.getCanvas().repaint(); + app.scrollList.repaint(); + } +} diff --git a/src/jdrafting/gui/controller/actions/ArrowAction.java b/src/jdrafting/gui/controller/actions/ArrowAction.java new file mode 100644 index 0000000..3ea555a --- /dev/null +++ b/src/jdrafting/gui/controller/actions/ArrowAction.java @@ -0,0 +1,42 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.ArrowListener; + +@SuppressWarnings("serial") +public class ArrowAction extends AbstractAction +{ + private Application app; + + public ArrowAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "arrow" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "arrow_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_arrow" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_6, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "arrow.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "arrow.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( + new ArrowListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/AxialSymmetryAction.java b/src/jdrafting/gui/controller/actions/AxialSymmetryAction.java new file mode 100644 index 0000000..a894c58 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/AxialSymmetryAction.java @@ -0,0 +1,49 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.JOptionPane; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.AxialSymmetryListener; + +@SuppressWarnings("serial") +public class AxialSymmetryAction extends AbstractAction +{ + private Application app; + + public AxialSymmetryAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "axial_sym" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "axial_sym_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_sym_axial" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( + KeyEvent.VK_A, InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "axial_symmetry.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "axial_symmetry.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + if ( app.getSelectedShapes().size() > 0 ) + app.getCanvas().setCanvasListener( + new AxialSymmetryListener( app.getCanvas() ) ); + else + JOptionPane.showMessageDialog( app, + getLocaleText( "selected_shapes_msg" ), + getLocaleText( "axial_sym" ) + " error", + JOptionPane.ERROR_MESSAGE ); + } +} diff --git a/src/jdrafting/gui/controller/actions/BackwardAction.java b/src/jdrafting/gui/controller/actions/BackwardAction.java new file mode 100644 index 0000000..236c29c --- /dev/null +++ b/src/jdrafting/gui/controller/actions/BackwardAction.java @@ -0,0 +1,64 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.Exercise; +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.ToastCanvasStep; + +@SuppressWarnings("serial") +public class BackwardAction extends AbstractAction +{ + private Application app; + + public BackwardAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "backward" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "backward_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_backward" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_LEFT, InputEvent.ALT_DOWN_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "backward.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "backward.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + final Exercise exercise = app.getExercise(); + if ( exercise.getFrameIndex() > 1 ) + { + // update frame + exercise.setFrameIndex( exercise.getFrameIndex() - 1 ); + + // create step description toast + final JDraftingShape shape = app.shapeList.getModel().get(exercise.getFrameIndex() - 1); + if ( app.currentToast != null ) + { + if ( app.currentToast.getClosingTimer() != null ) + app.currentToast.getClosingTimer().stop(); + app.currentToast.dispose(); + } + app.currentToast = new ToastCanvasStep( shape, exercise.getFrameIndex(), + app.canvas.getLocationOnScreen() ) + .showToast(); + + // refresh + app.getCanvas().repaint(); + app.scrollList.repaint(); + } + } +} diff --git a/src/jdrafting/gui/controller/actions/BisectrixAction.java b/src/jdrafting/gui/controller/actions/BisectrixAction.java new file mode 100644 index 0000000..fdaed5c --- /dev/null +++ b/src/jdrafting/gui/controller/actions/BisectrixAction.java @@ -0,0 +1,41 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.BisectrixListener; + +@SuppressWarnings("serial") +public class BisectrixAction extends AbstractAction +{ + private Application app; + + public BisectrixAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "bisectrix" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "bisectrix_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_bisectrix" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_4, InputEvent.ALT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "bisectrix.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "bisectrix.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( new BisectrixListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/BoundsAction.java b/src/jdrafting/gui/controller/actions/BoundsAction.java new file mode 100644 index 0000000..7d78579 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/BoundsAction.java @@ -0,0 +1,41 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.BoundsListener; + +@SuppressWarnings("serial") +public class BoundsAction extends AbstractAction +{ + private Application app; + + public BoundsAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "bounds" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "bounds_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_bounds" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( + KeyEvent.VK_1, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "bounds.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "bounds.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( new BoundsListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/CanvasColorAction.java b/src/jdrafting/gui/controller/actions/CanvasColorAction.java new file mode 100644 index 0000000..7630c24 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/CanvasColorAction.java @@ -0,0 +1,51 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.JColorChooser; +import javax.swing.JDialog; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; + +@SuppressWarnings("serial") +public class CanvasColorAction extends AbstractAction +{ + private Application app; + private JDialog colorChooser; + private JColorChooser jcc; + + public CanvasColorAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "background_color" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "background_color_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_canvas_col" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_B, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "backcolor.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "backcolor.png" ) ); + + jcc = new JColorChooser(); + colorChooser = JColorChooser.createDialog( + app, getLocaleText( "background_color_des" ), true, jcc, + (evt) -> app.setBackColor( jcc.getColor() ), // ok + null ); // cancel + } + + @Override + public void actionPerformed( ActionEvent e ) + { + jcc.setColor( app.getBackColor() ); + colorChooser.setVisible( true ); + } +} diff --git a/src/jdrafting/gui/controller/actions/CapableArcAction.java b/src/jdrafting/gui/controller/actions/CapableArcAction.java new file mode 100644 index 0000000..bab3d1f --- /dev/null +++ b/src/jdrafting/gui/controller/actions/CapableArcAction.java @@ -0,0 +1,42 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.CapableArcListener; + +@SuppressWarnings("serial") +public class CapableArcAction extends AbstractAction +{ + private Application app; + + public CapableArcAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "capable_arc" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "capable_arc_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_cap_arc" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_5, InputEvent.ALT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "capable_arc.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "capable_arc.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( + new CapableArcListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/CentralSymmetryAction.java b/src/jdrafting/gui/controller/actions/CentralSymmetryAction.java new file mode 100644 index 0000000..c3183a3 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/CentralSymmetryAction.java @@ -0,0 +1,50 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.JOptionPane; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.CentralSymmetryListener; + +@SuppressWarnings("serial") +public class CentralSymmetryAction extends AbstractAction +{ + private Application app; + + public CentralSymmetryAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "central_sym" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "central_sym_des" ) ); + putValue( MNEMONIC_KEY, + JDUtils.getLocaleMnemonic( "mne_sym_central" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( + KeyEvent.VK_C, InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "central_symmetry.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "central_symmetry.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + if ( app.getSelectedShapes().size() > 0 ) + app.getCanvas().setCanvasListener( + new CentralSymmetryListener( app.getCanvas() ) ); + else + JOptionPane.showMessageDialog( app, + getLocaleText( "selected_shapes_msg" ), + getLocaleText( "central_sym" ) + " error", + JOptionPane.ERROR_MESSAGE ); + } +} diff --git a/src/jdrafting/gui/controller/actions/CircumferenceAction.java b/src/jdrafting/gui/controller/actions/CircumferenceAction.java new file mode 100644 index 0000000..7d0463b --- /dev/null +++ b/src/jdrafting/gui/controller/actions/CircumferenceAction.java @@ -0,0 +1,42 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.CircumferenceListener; + +@SuppressWarnings("serial") +public class CircumferenceAction extends AbstractAction +{ + private Application app; + + public CircumferenceAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "circumference" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "circumference_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_circ" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_4, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "circumference.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "circumference.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( + new CircumferenceListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/CopySelectedAction.java b/src/jdrafting/gui/controller/actions/CopySelectedAction.java new file mode 100644 index 0000000..07681aa --- /dev/null +++ b/src/jdrafting/gui/controller/actions/CopySelectedAction.java @@ -0,0 +1,64 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.Toast; + +@SuppressWarnings("serial") +public class CopySelectedAction extends AbstractAction +{ + private Application app; + + public CopySelectedAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "copy" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "copy_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_copy" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_C, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "copy.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "copy.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + if ( !app.getSelectedShapes().isEmpty() ) + { + app.setInnerClipboard( app.getSelectedShapes() + .parallelStream() + .sorted( (jds1,jds2) -> Integer.compare( + app.getExercise().indexOf(jds1), + app.getExercise().indexOf(jds2) ) ) + .toArray( JDraftingShape[]::new ) ); + + new Toast( String.format( "%d %s", + app.getInnerClipboard().length, + getLocaleText( "toast_copy" ) ), + Toast.ONE_SECOND ).showToast(); + } + else + { + final Toast toast = new Toast( String.format( + "%s", + getLocaleText( "toast_no_copy" ) ), + Toast.ONE_SECOND ); + toast.getToastLabel().setBackground( new Color( 255, 75, 75 ) ); + toast.showToast(); + } + } +} diff --git a/src/jdrafting/gui/controller/actions/DeleteSelectedAction.java b/src/jdrafting/gui/controller/actions/DeleteSelectedAction.java new file mode 100644 index 0000000..c99c4ba --- /dev/null +++ b/src/jdrafting/gui/controller/actions/DeleteSelectedAction.java @@ -0,0 +1,62 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.util.HashSet; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +@SuppressWarnings("serial") +public class DeleteSelectedAction extends AbstractAction +{ + private Application app; + + public DeleteSelectedAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "delete" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "delete_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_delete" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( "DELETE" ) ); + putValue( SMALL_ICON, getSmallIcon( "delete_all.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "delete_all.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + //////////////////////////// TRANSACTION //////////////////////////// + final JDCompoundEdit transaction = new JDCompoundEdit() { + @Override + public String getPresentationName() + { + return getLocaleText( "delete" ) + " (" + edits.size() + " shapes)"; + } + }; + + app.getSelectedShapes() + .stream() + .forEach( jdshape -> { + if ( app.shapeList.getModel().contains( jdshape ) ) + app.removeShape( jdshape, transaction ); + }); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + app.setSelectedShapes( new HashSet<>() ); + + app.getCanvas().repaint(); + app.scrollList.repaint(); + } +} diff --git a/src/jdrafting/gui/controller/actions/DivisionPointsAction.java b/src/jdrafting/gui/controller/actions/DivisionPointsAction.java new file mode 100644 index 0000000..3c17bde --- /dev/null +++ b/src/jdrafting/gui/controller/actions/DivisionPointsAction.java @@ -0,0 +1,41 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.DivisionPointsListener; + +@SuppressWarnings("serial") +public class DivisionPointsAction extends AbstractAction +{ + private Application app; + + public DivisionPointsAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "divisions" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "divisions_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_divisions" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_0, InputEvent.ALT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "divisions.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "divisions.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + app.getCanvas().setCanvasListener( new DivisionPointsListener( app.getCanvas() ) ); + } + +} diff --git a/src/jdrafting/gui/controller/actions/EllipseAction.java b/src/jdrafting/gui/controller/actions/EllipseAction.java new file mode 100644 index 0000000..83797d1 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/EllipseAction.java @@ -0,0 +1,42 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.EllipseListener; + +@SuppressWarnings("serial") +public class EllipseAction extends AbstractAction +{ + private Application app; + + public EllipseAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "ellipse" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "ellipse_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_ellipse" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_5, InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "ellipse.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "ellipse.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( + new EllipseListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/EndAction.java b/src/jdrafting/gui/controller/actions/EndAction.java new file mode 100644 index 0000000..2fabf43 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/EndAction.java @@ -0,0 +1,42 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; + +@SuppressWarnings("serial") +public class EndAction extends AbstractAction +{ + private Application app; + + public EndAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "end" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "end_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_end" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_UP, InputEvent.ALT_DOWN_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "end.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "end.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + app.getExercise().setFrameAtEnd(); + app.getCanvas().repaint(); + app.scrollList.repaint(); + } +} diff --git a/src/jdrafting/gui/controller/actions/ExerciseMetadataAction.java b/src/jdrafting/gui/controller/actions/ExerciseMetadataAction.java new file mode 100644 index 0000000..e4bc849 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/ExerciseMetadataAction.java @@ -0,0 +1,41 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.FileInfoDialog; +import jdrafting.gui.JDUtils; + +@SuppressWarnings("serial") +public class ExerciseMetadataAction extends AbstractAction +{ + private Application app; + + public ExerciseMetadataAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "fileinfo" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "fileinfo_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_ex_metadata" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_I, InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "fileinfo.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "fileinfo.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + new FileInfoDialog( app ).setVisible( true ); + app.setAppTitle(); + } +} diff --git a/src/jdrafting/gui/controller/actions/ExitAction.java b/src/jdrafting/gui/controller/actions/ExitAction.java new file mode 100644 index 0000000..df4cf39 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/ExitAction.java @@ -0,0 +1,40 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; + +@SuppressWarnings("serial") +public class ExitAction extends AbstractAction +{ + private Application app; + + public ExitAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "exit" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "exit_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_exit" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_Q, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "exit.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "exit.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + app.exit(); + } +} diff --git a/src/jdrafting/gui/controller/actions/ExtremesAction.java b/src/jdrafting/gui/controller/actions/ExtremesAction.java new file mode 100644 index 0000000..bb53ccc --- /dev/null +++ b/src/jdrafting/gui/controller/actions/ExtremesAction.java @@ -0,0 +1,42 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.ExtremesListener; + +@SuppressWarnings("serial") +public class ExtremesAction extends AbstractAction +{ + private Application app; + + public ExtremesAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "extremes" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "extremes_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_extremes" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_9, InputEvent.ALT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "extremes.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "extremes.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + app.getCanvas().setCanvasListener( new ExtremesListener( app.getCanvas() ) ); + } + +} diff --git a/src/jdrafting/gui/controller/actions/EyedropperAction.java b/src/jdrafting/gui/controller/actions/EyedropperAction.java new file mode 100644 index 0000000..efb53b8 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/EyedropperAction.java @@ -0,0 +1,42 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.EyedropperListener; + +@SuppressWarnings("serial") +public class EyedropperAction extends AbstractAction +{ + private Application app; + + public EyedropperAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "eyedropper" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "eyedropper_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_eyedropper" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_E, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "eyedropper.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "eyedropper.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( + new EyedropperListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/ForwardAction.java b/src/jdrafting/gui/controller/actions/ForwardAction.java new file mode 100644 index 0000000..b897a98 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/ForwardAction.java @@ -0,0 +1,64 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.Exercise; +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.ToastCanvasStep; + +@SuppressWarnings("serial") +public class ForwardAction extends AbstractAction +{ + private Application app; + + public ForwardAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "forward" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "forward_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_forward" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_RIGHT, InputEvent.ALT_DOWN_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "forward.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "forward.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + final Exercise exercise = app.getExercise(); + if ( !exercise.isIndexAtEnd() ) + { + // update frame + exercise.setFrameIndex( exercise.getFrameIndex() + 1 ); + + // create step description toast + final JDraftingShape shape = app.shapeList.getModel().get(exercise.getFrameIndex() - 1); + if ( app.currentToast != null ) + { + if ( app.currentToast.getClosingTimer() != null ) + app.currentToast.getClosingTimer().stop(); + app.currentToast.dispose(); + } + + app.currentToast = new ToastCanvasStep( shape, exercise.getFrameIndex(), + app.canvas.getLocationOnScreen() ).showToast(); + + // refresh + app.getCanvas().repaint(); + app.scrollList.repaint(); + } + } +} diff --git a/src/jdrafting/gui/controller/actions/FragmentAction.java b/src/jdrafting/gui/controller/actions/FragmentAction.java new file mode 100644 index 0000000..637ad8c --- /dev/null +++ b/src/jdrafting/gui/controller/actions/FragmentAction.java @@ -0,0 +1,41 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.FragmentListener; + +@SuppressWarnings("serial") +public class FragmentAction extends AbstractAction +{ + private Application app; + + public FragmentAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "fragment" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "fragment_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_fragment" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( + KeyEvent.VK_2, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "hammer.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "hammer.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + app.getCanvas().setCanvasListener( new FragmentListener( app.getCanvas() ) ); + } +} \ No newline at end of file diff --git a/src/jdrafting/gui/controller/actions/FreeHandAction.java b/src/jdrafting/gui/controller/actions/FreeHandAction.java new file mode 100644 index 0000000..49d1f42 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/FreeHandAction.java @@ -0,0 +1,42 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.FreeHandListener; + +@SuppressWarnings("serial") +public class FreeHandAction extends AbstractAction +{ + private Application app; + + public FreeHandAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "free_hand" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "free_hand_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_free" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_9, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "free_hand.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "free_hand.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( + new FreeHandListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/FusionAction.java b/src/jdrafting/gui/controller/actions/FusionAction.java new file mode 100644 index 0000000..321016f --- /dev/null +++ b/src/jdrafting/gui/controller/actions/FusionAction.java @@ -0,0 +1,101 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.elvis; +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.geom.Path2D; +import java.util.HashSet; +import java.util.stream.Collectors; + +import javax.swing.AbstractAction; +import javax.swing.JOptionPane; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +/** + * Fusion the PathIterator o several shapes on one shape + * @author Miguel Alejandro Moreno Barrientos, (C)?-2021 + * @version 0.1.12 + */ +@SuppressWarnings("serial") +public class FusionAction extends AbstractAction +{ + private Application app; + + public FusionAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "fusion" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "fusion_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_fusion" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( + KeyEvent.VK_3, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "fusion.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "fusion.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + // check two or more selected shapes + if ( app.getSelectedShapes().size() < 2 ) + { + JOptionPane.showMessageDialog( app, getLocaleText( "sel_2_error" ), + getLocaleText( "fusion" ), + JOptionPane.ERROR_MESSAGE ); + return; + } + + final String descHtml = "{" + String.join( ",", app.getSelectedShapes() + .parallelStream() + .map( jdshape -> "[" + + elvis( jdshape.getName(), "?" ) + + "]" ) + .collect( Collectors.toList() ) ) + "}"; + + //////////////////////////// TRANSACTION //////////////////////////// + final JDCompoundEdit transaction = new JDCompoundEdit( getLocaleText( "fusion" ) + + " " + descHtml + " (" + app.getSelectedShapes().size() + " shapes)" ); + + // remove shapes from exercise and create merged path + final Path2D path = new Path2D.Double(); + final boolean connect = + ( e.getModifiers() & ActionEvent.SHIFT_MASK ) == ActionEvent.SHIFT_MASK; + + app.getSelectedShapes() + .stream() + .sorted( (jds1,jds2) -> Integer.compare( app.getExercise().getShapes().indexOf(jds1), + app.getExercise().getShapes().indexOf(jds2) ) ) // improve filling + .forEach( jdshape -> { + app.removeShape( jdshape, transaction ); + path.append( jdshape.getShape().getPathIterator( null ), connect ); + }); + + // add new shape to exercise + app.addShapeFromIterator( path.getPathIterator( null ), "", + getLocaleText( "new_fusion" ) + " " + descHtml, app.getColor(), null, + app.getStroke(), transaction ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + app.setSelectedShapes( new HashSet<>() ); + + // refresh + app.getCanvas().repaint(); + app.scrollList.repaint(); + } + +} diff --git a/src/jdrafting/gui/controller/actions/HomothetyAction.java b/src/jdrafting/gui/controller/actions/HomothetyAction.java new file mode 100644 index 0000000..c937392 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/HomothetyAction.java @@ -0,0 +1,69 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.JOptionPane; +import javax.swing.JSpinner; +import javax.swing.KeyStroke; +import javax.swing.SpinnerNumberModel; + +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.HomothetyListener; + +@SuppressWarnings("serial") +public class HomothetyAction extends AbstractAction +{ + private Application app; + private double factor = 2.; + + public HomothetyAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "homothety" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "homothety_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_homo" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_H, InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "homothety.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "homothety.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + final CanvasPanel canvas = app.getCanvas(); + + if ( app.getSelectedShapes().size() > 0 ) + { + // dialog for homothety factor + final JSpinner spinFactor = new JSpinner( new SpinnerNumberModel( + factor, 0.1, Double.POSITIVE_INFINITY, 0.1 ) ); + spinFactor.addChangeListener( evt -> + factor = (double) ( (JSpinner) evt.getSource() ).getValue() ); + + final int option = JOptionPane.showOptionDialog( app, spinFactor, + getLocaleText( "homo_dlg" ), + JOptionPane.OK_CANCEL_OPTION, + JOptionPane.PLAIN_MESSAGE, + getLargeIcon( "homothety.png" ), + null, null ); + if ( option != JOptionPane.OK_OPTION ) return; + + canvas.setCanvasListener( new HomothetyListener( canvas, factor ) ); + } + else + JOptionPane.showMessageDialog( app, + getLocaleText( "selected_shapes_msg" ), + getLocaleText( "homothety" ) + " error", + JOptionPane.ERROR_MESSAGE ); + } +} diff --git a/src/jdrafting/gui/controller/actions/HyperbolaAction.java b/src/jdrafting/gui/controller/actions/HyperbolaAction.java new file mode 100644 index 0000000..ef5cafa --- /dev/null +++ b/src/jdrafting/gui/controller/actions/HyperbolaAction.java @@ -0,0 +1,42 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.HyperbolaListener; + +@SuppressWarnings("serial") +public class HyperbolaAction extends AbstractAction +{ + private Application app; + + public HyperbolaAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "hyperbola" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "hyperbola_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_hyperbola" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_7, InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "hyperbola.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "hyperbola.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( + new HyperbolaListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/IntersectionsAction.java b/src/jdrafting/gui/controller/actions/IntersectionsAction.java new file mode 100644 index 0000000..ab83850 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/IntersectionsAction.java @@ -0,0 +1,144 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.elvis; +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.geom.Point2D; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +import javax.swing.AbstractAction; +import javax.swing.JOptionPane; +import javax.swing.KeyStroke; + +import jdrafting.geom.JDPoint; +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +/** + * Get intersections between selected shapes + */ +@SuppressWarnings("serial") +public class IntersectionsAction extends AbstractAction +{ + private Application app; + + public IntersectionsAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "inter" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "inter_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_intersection" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( + KeyEvent.VK_0, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "intersection.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "intersection.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + // error if less than two shapes + if ( app.getSelectedShapes().size() < 2 ) + { + JOptionPane.showMessageDialog( app, + getLocaleText( "inter_msg" ), + getLocaleText( "inter_dlg" ), + JOptionPane.ERROR_MESSAGE ); + return; + } + + // set joins style + final Color color = ( e.getModifiers() & ActionEvent.SHIFT_MASK ) == ActionEvent.SHIFT_MASK + ? app.getColor() + : app.getPointColor(); + final BasicStroke stroke = ( e.getModifiers() & ActionEvent.SHIFT_MASK ) + == ActionEvent.SHIFT_MASK + ? app.getStroke() + : app.getPointStroke(); + + // get pair to pair intersections + final JDraftingShape[] arrayShapes = app.getSelectedShapes() + .stream().toArray( JDraftingShape[]::new ); + final List joins = new LinkedList<>(); + + for ( int i = 0; i < arrayShapes.length - 1; i++ ) + for ( int j = i + 1; j < arrayShapes.length; j++ ) + for ( final Point2D join : CanvasPanel.intersectionPoints( arrayShapes[i], + arrayShapes[j] ) ) + { + final POJOJoin pojojoin = new POJOJoin(); + pojojoin.join = join; + pojojoin.shape1 = arrayShapes[i]; + pojojoin.shape2 = arrayShapes[j]; + joins.add( pojojoin ); + } + + // no joins message + if ( joins.isEmpty() ) + { + JOptionPane.showMessageDialog( app, + getLocaleText( "inter_error" ), + getLocaleText( "inter" ), + JOptionPane.ERROR_MESSAGE ); + return; + } + + final String descHtml = "{" + String.join( ",", app.getSelectedShapes() + .parallelStream() + .map( jdshape -> "[" + + elvis( jdshape.getName(), "?" ) + + "]" ) + .collect( Collectors.toList() ) ) + "}"; + + //////////////////////////// TRANSACTION //////////////////////////// + final JDCompoundEdit transaction = new JDCompoundEdit() { + @Override + public String getPresentationName() + { + return getLocaleText( "inter" ) + " " + descHtml + + " (" + edits.size() + " points)"; + } + }; + + // add instersections to exercise + for ( final POJOJoin pojojoin : joins ) + { + app.addShapeFromIterator( new JDPoint( pojojoin.join ).getPathIterator( null ), "", + String.format( "%s {[%s],[%s]}", + getLocaleText( "new_join" ), + Application.HTML_SHAPE_NAMES_COL, + elvis( pojojoin.shape1.getName(), "?" ), + elvis( pojojoin.shape2.getName(), "?" ) ), + color, null, stroke, transaction ); + } + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + // refresh + app.getCanvas().repaint(); + app.scrollList.repaint(); + } + + private class POJOJoin + { + JDraftingShape shape1, shape2; + Point2D join; + } +} diff --git a/src/jdrafting/gui/controller/actions/InvertSelectionAction.java b/src/jdrafting/gui/controller/actions/InvertSelectionAction.java new file mode 100644 index 0000000..8460143 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/InvertSelectionAction.java @@ -0,0 +1,47 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.util.stream.Collectors; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; + +@SuppressWarnings("serial") +public class InvertSelectionAction extends AbstractAction +{ + private Application app; + + public InvertSelectionAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "invert" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "invert_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_invert_sel" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_G, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "invert.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "invert.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.setSelectedShapes( app.getExercise().getShapes() + .stream() + .filter( jdshape -> !app.getSelectedShapes().contains( jdshape ) ) + .collect( Collectors.toSet() ) ); + + app.scrollList.repaint(); + app.getCanvas().repaint(); + } +} diff --git a/src/jdrafting/gui/controller/actions/LookFeelAction.java b/src/jdrafting/gui/controller/actions/LookFeelAction.java new file mode 100644 index 0000000..cc9b08b --- /dev/null +++ b/src/jdrafting/gui/controller/actions/LookFeelAction.java @@ -0,0 +1,43 @@ +package jdrafting.gui.controller.actions; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.JMenu; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UIManager.LookAndFeelInfo; + +import jdrafting.gui.Application; + +@SuppressWarnings("serial") +public class LookFeelAction extends AbstractAction +{ + private JMenu menu; + private Application app; + + public LookFeelAction( Application app, JMenu menu ) + { + this.app = app; + this.menu = menu; + actionPerformed( null ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + try + { + LookAndFeelInfo[] list = UIManager.getInstalledLookAndFeels(); + + int i; + for ( i = 0; i < menu.getItemCount(); i++ ) + if ( menu.getItem( i ).isSelected() ) + break; + + UIManager.setLookAndFeel( list[i].getClassName() ); + SwingUtilities.updateComponentTreeUI( app ); + } + catch ( Exception ex ) {} + } +} diff --git a/src/jdrafting/gui/controller/actions/MathFunctionAction.java b/src/jdrafting/gui/controller/actions/MathFunctionAction.java new file mode 100644 index 0000000..5731172 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/MathFunctionAction.java @@ -0,0 +1,178 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.Desktop; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JEditorPane; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JTextField; +import javax.swing.KeyStroke; +import javax.swing.event.HyperlinkEvent; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.MathFunctionListener; +import jme.Expresion; +import jme.excepciones.ExpresionException; +import jme.terminales.RealDoble; +import static jdrafting.gui.JDUtils.getLocaleText; + +@SuppressWarnings("serial") +public class MathFunctionAction extends AbstractAction +{ + private Application app; + private Map jmeParams; + + public MathFunctionAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "func" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "func_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_jme" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_J, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "jme.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "jme.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + if ( !Application.jmeEnabled ) + return; + + JMEDialog dialog = new JMEDialog(); + jmeParams = null; + dialog.setVisible( true ); + + if ( jmeParams != null ) + app.getCanvas().setCanvasListener( + new MathFunctionListener( app.getCanvas(), jmeParams ) ); + } + + private class JMEDialog extends JDialog + { + private JMEDialog() + { + super( app, getLocaleText( "jme_dlg" ), true ); + setLayout( new GridLayout( 6, 2, 4, 4 ) ); + add( new JLabel() ); + JEditorPane ep = new JEditorPane( "text/html", + "" + + getLocaleText( "jme_doc" ) + + "" ); + ep.setEditable( false ); + ep.getCaret().deinstall( ep ); // non-selectable + ep.addHyperlinkListener( ev -> { + if ( ev.getEventType().equals( HyperlinkEvent.EventType.ACTIVATED ) + && Desktop.isDesktopSupported() + && Desktop.getDesktop().isSupported( Desktop.Action.BROWSE ) ) + { + try { Desktop.getDesktop().browse( ev.getURL().toURI() ); } + catch ( URISyntaxException | IOException ex ) {} + } + }); + add( ep ); + add( new JLabel( "JME " + + getLocaleText( "jme_examples" ) + + " -> f(x):=cos(x) " + + "|| f(t):=[cos(t),3*sin(t)]", + JLabel.RIGHT ) ); + JTextField tfExpression = new JTextField( + jmeParams != null + ? ( (Expresion) jmeParams.get( "expression" ) ).entrada() + : "" ); + add( tfExpression ); + add( new JLabel( getLocaleText( "jme_min" ), JLabel.RIGHT ) ); + JTextField tfminX = new JTextField( jmeParams != null + ? jmeParams.get( "xmin" ).toString() + : "-10" ); + add( tfminX ); + add( new JLabel( getLocaleText( "jme_max" ), JLabel.RIGHT ) ); + JTextField tfmaxX = new JTextField( jmeParams != null + ? jmeParams.get( "xmax" ).toString() + : "10" ); + add( tfmaxX ); + add( new JLabel( getLocaleText( "jme_intervals" ), JLabel.RIGHT ) ); + JTextField tfIntervals = new JTextField( + jmeParams != null + ? jmeParams.get( "intervals" ).toString() + : String.valueOf( (int) app.getFlatnessValue() ) ); + add( tfIntervals ); + JButton btnOk = new JButton( "Ok" ); + add( btnOk ); + Action okAction = new AbstractAction() { + @Override + public void actionPerformed( ActionEvent e ) + { + Map params = new HashMap<>(); + try + { + params.put( "expression", + new Expresion( tfExpression.getText() ) ); + params.put( "xmin", ( (RealDoble) Expresion.evaluar( + tfminX.getText() ) ).doble() ); + params.put( "xmax", ( (RealDoble) Expresion.evaluar( + tfmaxX.getText() ) ).doble() ); + params.put( "intervals", + (int) ( (RealDoble) Expresion.evaluar( + tfIntervals.getText() ) ).longint() ); + jmeParams = params; + } + catch ( ClassCastException | ExpresionException ex ) + { + JOptionPane.showMessageDialog( JMEDialog.this, ex, + "jme error", JOptionPane.ERROR_MESSAGE ); + return; + } + dispose(); + } + }; + btnOk.addActionListener( okAction ); + JButton btnCancel = new JButton( "Cancel" ); + add( btnCancel ); + Action cancelAction = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) + { + dispose(); + } + }; + btnCancel.addActionListener( cancelAction ); + + // ENTER + getRootPane().getInputMap( JComponent.WHEN_IN_FOCUSED_WINDOW ) + .put( KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0 ), "INTRO_KEY" ); + getRootPane().getActionMap().put( "INTRO_KEY", okAction ); + // ESC + getRootPane().getInputMap( JComponent.WHEN_IN_FOCUSED_WINDOW ) + .put( KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, 0 ), + "ESCAPE_KEY" ); + getRootPane().getActionMap().put( "ESCAPE_KEY", cancelAction ); + + // window + setPreferredSize( new Dimension( 600, 200 ) ); + pack(); + setLocationRelativeTo( app ); + + setDefaultCloseOperation( JDialog.DISPOSE_ON_CLOSE ); + } + } +} diff --git a/src/jdrafting/gui/controller/actions/MediatrixAction.java b/src/jdrafting/gui/controller/actions/MediatrixAction.java new file mode 100644 index 0000000..f4d898d --- /dev/null +++ b/src/jdrafting/gui/controller/actions/MediatrixAction.java @@ -0,0 +1,40 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.MediatrixListener; + +@SuppressWarnings("serial") +public class MediatrixAction extends AbstractAction +{ + private Application app; + + public MediatrixAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "mediatrix" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "mediatrix_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_mediatrix" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_3, InputEvent.ALT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "mediatrix.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "mediatrix.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( new MediatrixListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/MidpointAction.java b/src/jdrafting/gui/controller/actions/MidpointAction.java new file mode 100644 index 0000000..2f88854 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/MidpointAction.java @@ -0,0 +1,41 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.MidpointListener; + +@SuppressWarnings("serial") +public class MidpointAction extends AbstractAction +{ + private Application app; + + public MidpointAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "midpoint" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "midpoint_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_midpoint" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_7, InputEvent.ALT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "midpoint.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "midpoint.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + app.getCanvas().setCanvasListener( new MidpointListener( app.getCanvas() ) ); + } + +} diff --git a/src/jdrafting/gui/controller/actions/ModifySegmentAction.java b/src/jdrafting/gui/controller/actions/ModifySegmentAction.java new file mode 100644 index 0000000..4947e67 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/ModifySegmentAction.java @@ -0,0 +1,40 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.ModifySegmentListener; + +@SuppressWarnings("serial") +public class ModifySegmentAction extends AbstractAction +{ + private Application app; + + public ModifySegmentAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "modify" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "modify_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_mod_seg" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_6, InputEvent.ALT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "modify_segment.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "modify_segment.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( new ModifySegmentListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/MoveZBufferAction.java b/src/jdrafting/gui/controller/actions/MoveZBufferAction.java new file mode 100644 index 0000000..e9ec6c0 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/MoveZBufferAction.java @@ -0,0 +1,161 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; +import javax.swing.undo.AbstractUndoableEdit; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; + +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; + +@SuppressWarnings("serial") +public class MoveZBufferAction extends AbstractAction +{ + private Application app; + private boolean up; + + public MoveZBufferAction( Application app, boolean up ) + { + this.app = app; + this.up = up; + + putValue( NAME, getLocaleText( up ? "move_up" : "move_down" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( up ? "move_up_des" : "move_down_des" ) ); + putValue( MNEMONIC_KEY, up + ? JDUtils.getLocaleMnemonic( "mne_z_up" ) + : JDUtils.getLocaleMnemonic( "mne_z_down" ) ); + putValue( ACCELERATOR_KEY, + up + ? KeyStroke.getKeyStroke( KeyEvent.VK_ADD, InputEvent.CTRL_DOWN_MASK ) + : KeyStroke.getKeyStroke( KeyEvent.VK_SUBTRACT, InputEvent.CTRL_DOWN_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( up ? "up.png" : "down.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( up ? "up.png" : "down.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + if ( app.getSelectedShapes().isEmpty() ) return; + + final Set selected = app.getSelectedShapes(); + + // order selected shapes by index + final List ordered = + selected.stream() + .sorted( (s1,s2) -> Integer.compare( app.getExercise().indexOf( s1 ), + app.getExercise().indexOf( s2 ) ) ) + .collect( Collectors.toList() ); + + // move selected shapes up or down + if ( up ? moveUp( ordered ) : moveDown( ordered ) ) + app.undoRedoSupport.postEdit( new EditMoveZBuffer( ordered ) ); + + // recover selection (modified in shapelist listener) + app.setSelectedShapes( selected ); + + // refresh + app.getCanvas().repaint(); + app.scrollList.repaint(); + } + + private boolean moveUp( List ordered ) + { + final int maxIndex = app.getExercise().indexOf( ordered.get( ordered.size() - 1 ) ); + + if ( maxIndex == app.getExercise().size() - 1 ) + return false; + + final List reverse = new ArrayList( ordered ); + Collections.reverse( reverse ); + + for ( final JDraftingShape jdshape : reverse ) + { + final int index = app.getExercise().indexOf( jdshape ); + app.getExercise().addShape( index + 2, jdshape ); + app.shapeList.getModel().add( index + 2, jdshape ); + app.getExercise().removeShape( index ); + app.shapeList.getModel().remove( index ); + } + app.shapeList.ensureIndexIsVisible( maxIndex + 1 ); + + return true; + } + + private boolean moveDown( List ordered ) + { + final int minIndex = app.getExercise().indexOf( ordered.get( 0 ) ); + + if ( minIndex == 0 ) + return false; + + for ( final JDraftingShape jdshape : ordered ) + { + final int index = app.getExercise().indexOf( jdshape ); + app.getExercise().addShape( index - 1, jdshape ); + app.shapeList.getModel().add( index - 1, jdshape ); + app.getExercise().removeShape( index + 1 ); + app.shapeList.getModel().remove( index + 1 ); + } + app.shapeList.ensureIndexIsVisible( minIndex - 1 ); + + return true; + } + + /** + * UndoableEdit to move shapes zbuffer + */ + private class EditMoveZBuffer extends AbstractUndoableEdit + { + private List ordered; + + private EditMoveZBuffer( List ordered ) + { + this.ordered = ordered; + } + + @Override + public void undo() throws CannotUndoException + { + if ( up ) + moveDown( ordered ); + else + moveUp( ordered ); + } + + @Override + public void redo() throws CannotRedoException + { + if ( up ) + moveUp( ordered ); + else + moveDown( ordered ); + } + + @Override + public boolean canUndo() { return true; } + @Override + public boolean canRedo() { return true; } + + @Override + public String getPresentationName() + { + return getLocaleText( up ? "move_up" : "move_down" ) + + " (" + ordered.size() + " shapes)"; + } + } +} diff --git a/src/jdrafting/gui/controller/actions/NewAction.java b/src/jdrafting/gui/controller/actions/NewAction.java new file mode 100644 index 0000000..0bed8ea --- /dev/null +++ b/src/jdrafting/gui/controller/actions/NewAction.java @@ -0,0 +1,53 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.JOptionPane; +import javax.swing.KeyStroke; + +import jdrafting.Exercise; +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; + +@SuppressWarnings("serial") +public class NewAction extends AbstractAction +{ + private Application app; + + public NewAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "new" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "new_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_new" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_N, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "new.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "new.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + // check for not saved file + if ( app.undoManager.canUndo() ) + { + int option = JOptionPane.showConfirmDialog( app, + getLocaleText( "exit_msg" ), + getLocaleText( "new" ), + JOptionPane.YES_NO_OPTION ); + if ( option != JOptionPane.YES_OPTION ) return; // cancel new file + } + + // new exercise + app.setExercise( new Exercise() ); + } +} diff --git a/src/jdrafting/gui/controller/actions/OpenAction.java b/src/jdrafting/gui/controller/actions/OpenAction.java new file mode 100644 index 0000000..e67c922 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/OpenAction.java @@ -0,0 +1,161 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.geom.Rectangle2D; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.Arrays; +import java.util.HashSet; + +import javax.swing.AbstractAction; +import javax.swing.BorderFactory; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; +import javax.swing.filechooser.FileNameExtensionFilter; + +import jdrafting.Exercise; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; +import jdrafting.gui.Viewport; + +@SuppressWarnings("serial") +public class OpenAction extends AbstractAction +{ + private Application app; + private static JFileChooser fileChooser; + + public OpenAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "open" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "open_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_open" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_O, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "open.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "open.png" ) ); + + // create static file chooser dialog + fileChooser = new JFileChooser(); + // add accesory panel for preview to file chooser + JPanel accesoryPanel = new JPanel(); + accesoryPanel.setBorder( BorderFactory.createTitledBorder( "Preview" ) ); + PreviewPanel previewPanel = new PreviewPanel(); + accesoryPanel.add( previewPanel ); + fileChooser.setAccessory( accesoryPanel ); + fileChooser.addPropertyChangeListener( previewPanel ); + fileChooser.setPreferredSize( JDUtils.getScreenSize( 0.7f, 0.7f ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + // check for not saved file + if ( app.undoManager.canUndo() ) + { + int option = JOptionPane.showConfirmDialog( app, + getLocaleText( "exit_msg" ), + getLocaleText( "open" ), + JOptionPane.YES_NO_OPTION ); + if ( option != JOptionPane.YES_OPTION ) return; // cancel open + } + + // update file chooser L&F + SwingUtilities.updateComponentTreeUI( fileChooser ); + + // set png format as the unique filter + Arrays.stream( fileChooser.getChoosableFileFilters() ) + .forEach( f -> fileChooser.removeChoosableFileFilter( f ) ); + fileChooser.addChoosableFileFilter( new FileNameExtensionFilter( + "JDrafting exercise (.jd)", "jd" ) ); + // reset preview + fileChooser.setSelectedFile( null ); // ensure property change trigger + PreviewPanel prev = (PreviewPanel) fileChooser.getAccessory().getComponent( 0 ); + prev.exercise = null; + + // Open dialog + if ( fileChooser.showOpenDialog( app ) == JFileChooser.APPROVE_OPTION ) + app.openFile( fileChooser.getSelectedFile() ); + } + + private class PreviewPanel extends JPanel implements PropertyChangeListener + { + private static final int SIZE = 200; + private Exercise exercise; + + PreviewPanel() + { + setPreferredSize( new Dimension( SIZE, SIZE ) ); + setOpaque( true ); + } + + @Override + public void propertyChange( PropertyChangeEvent e ) + { + String propertyName = e.getPropertyName(); + + if ( propertyName.equals( JFileChooser.SELECTED_FILE_CHANGED_PROPERTY ) ) + { + File selected = (File) e.getNewValue(); + + if ( selected != null ) + { + try ( FileInputStream is = new FileInputStream( selected ) ) + { + exercise = + (Exercise) new ObjectInputStream( is ).readObject(); + } + catch ( IOException | ClassNotFoundException ex ) {} + } + else + exercise = null; + + repaint(); + } + } + + @Override + public void paintComponent( Graphics g ) + { + super.paintComponent( g ); + + if ( exercise != null ) + { + setBackground( exercise.getBackgroundColor() ); + + Graphics2D g2 = (Graphics2D) g; + // High quality render + JDUtils.setHighQualityRender( g2 ); + + final Rectangle2D bounds = exercise.getBounds(); + int width = SIZE; + int height = Math.min( getPreferredSize().height, + (int) ( width * bounds.getHeight() / bounds.getWidth() ) ); + CanvasPanel.drawExercise( g2, CanvasPanel.getTransform( + new Viewport( bounds ), + new Viewport( 0, width, 0, height ) ), + exercise, new HashSet<>(), false ); + } + else + setBackground( fileChooser.getBackground() ); + } + } +} diff --git a/src/jdrafting/gui/controller/actions/PaintAction.java b/src/jdrafting/gui/controller/actions/PaintAction.java new file mode 100644 index 0000000..081a19e --- /dev/null +++ b/src/jdrafting/gui/controller/actions/PaintAction.java @@ -0,0 +1,40 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.PaintListener; + +@SuppressWarnings("serial") +public class PaintAction extends AbstractAction +{ + private Application app; + + public PaintAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "paint" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "paint_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_paint" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_F, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "paint.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "paint.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + app.getCanvas().setCanvasListener( new PaintListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/ParabolaAction.java b/src/jdrafting/gui/controller/actions/ParabolaAction.java new file mode 100644 index 0000000..1a58c7b --- /dev/null +++ b/src/jdrafting/gui/controller/actions/ParabolaAction.java @@ -0,0 +1,41 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.ParabolaListener; + +@SuppressWarnings("serial") +public class ParabolaAction extends AbstractAction +{ + private Application app; + + public ParabolaAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "parabola" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "parabola_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_parabola" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_6, InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "parabola.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "parabola.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( new ParabolaListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/ParallelAction.java b/src/jdrafting/gui/controller/actions/ParallelAction.java new file mode 100644 index 0000000..fe5df1d --- /dev/null +++ b/src/jdrafting/gui/controller/actions/ParallelAction.java @@ -0,0 +1,40 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.ParallelListener; + +@SuppressWarnings("serial") +public class ParallelAction extends AbstractAction +{ + private Application app; + + public ParallelAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "para" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "para_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_parallel" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_2, InputEvent.ALT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "parallel.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "parallel.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( new ParallelListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/PasteAction.java b/src/jdrafting/gui/controller/actions/PasteAction.java new file mode 100644 index 0000000..067767e --- /dev/null +++ b/src/jdrafting/gui/controller/actions/PasteAction.java @@ -0,0 +1,100 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.geom.AffineTransform; +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; +import jdrafting.gui.Toast; + +@SuppressWarnings("serial") +public class PasteAction extends AbstractAction +{ + private Application app; + + public PasteAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "paste" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "paste_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_paste" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_V, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "paste.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "paste.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + if ( app.getInnerClipboard() != null && app.getInnerClipboard().length > 0 ) + { + //////////////////////////// TRANSACTION //////////////////////////// + final JDCompoundEdit transaction = new JDCompoundEdit() { + public String getPresentationName() + { + return getLocaleText( "paste" ) + " (" + edits.size() + " shapes)"; + } + }; + + // move copies relative to originals and add them to exercise + final double tx = 0. /*app.getCanvas().getViewport().getWidth() / 30.*/, + ty = 0. /*app.getCanvas().getViewport().getHeight() / 30.*/; + final AffineTransform transform = AffineTransform.getTranslateInstance( tx, -ty ); + + final Set copySet = Arrays.stream( app.getInnerClipboard() ) + .map( jdshape -> { + final JDraftingShape copy = app.addShapeFromIterator( + jdshape.getShape().getPathIterator( transform ), + /*"copy of " +*/ jdshape.getName(), jdshape.getDescription(), + jdshape.getColor(), jdshape.getFill(), jdshape.getStroke(), + transaction ); + copy.setAsText( jdshape.isText() ); + copy.setFont( jdshape.getFont() ); + copy.setTextPosition( jdshape.getTextPosition() ); + return copy; + }) + .collect( Collectors.toSet() ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + new Toast( String.format( "%d %s", + app.getInnerClipboard().length, + getLocaleText( "toast_paste" ) ), + Toast.ONE_SECOND ).showToast(); + + // change selection to new shapes + app.setSelectedShapes( copySet ); + + // refresh + app.scrollList.repaint(); + app.getCanvas().repaint(); + } + else + { + final Toast toast = new Toast( String.format( + "%s", + getLocaleText( "toast_no_copy" ) ), + Toast.ONE_SECOND ); + toast.getToastLabel().setBackground( new Color( 255, 75, 75 ) ); + toast.showToast(); + } + } +} diff --git a/src/jdrafting/gui/controller/actions/PasteStyleAction.java b/src/jdrafting/gui/controller/actions/PasteStyleAction.java new file mode 100644 index 0000000..a79a975 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/PasteStyleAction.java @@ -0,0 +1,40 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.PasteStyleListener; + +@SuppressWarnings("serial") +public class PasteStyleAction extends AbstractAction +{ + private Application app; + + public PasteStyleAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "paste_style" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "paste_style_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_paste" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_P, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "paste_style.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "paste_style.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + app.getCanvas().setCanvasListener( new PasteStyleListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/PerpendicularAction.java b/src/jdrafting/gui/controller/actions/PerpendicularAction.java new file mode 100644 index 0000000..e51bc9b --- /dev/null +++ b/src/jdrafting/gui/controller/actions/PerpendicularAction.java @@ -0,0 +1,40 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.PerpendicularListener; + +@SuppressWarnings("serial") +public class PerpendicularAction extends AbstractAction +{ + private Application app; + + public PerpendicularAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "perp" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "perp_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_perp" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_1, InputEvent.ALT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "perpendicular.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "perpendicular.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( new PerpendicularListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/PointAction.java b/src/jdrafting/gui/controller/actions/PointAction.java new file mode 100644 index 0000000..17109ec --- /dev/null +++ b/src/jdrafting/gui/controller/actions/PointAction.java @@ -0,0 +1,42 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.PointListener; + +@SuppressWarnings("serial") +public class PointAction extends AbstractAction +{ + private Application app; + + public PointAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "point" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "point_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_point" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_1, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "point.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "point.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( + new PointListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/PointColorAction.java b/src/jdrafting/gui/controller/actions/PointColorAction.java new file mode 100644 index 0000000..b365389 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/PointColorAction.java @@ -0,0 +1,138 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.Box; +import javax.swing.JButton; +import javax.swing.JColorChooser; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.KeyStroke; +import javax.swing.SwingConstants; +import javax.swing.border.EmptyBorder; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; + +@SuppressWarnings("serial") +public class PointColorAction extends AbstractAction +{ + private Application app; + private JDialog colorChooser; + private JColorChooser jcc; + + public PointColorAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "point_color" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "point_color_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_point_col" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_K, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "point_color.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "point_color.png" ) ); + + jcc = new JColorChooser(); + colorChooser = JColorChooser.createDialog( + app, getLocaleText( "point_color_des" ), true, jcc, + (evt) -> app.setPointColor( jcc.getColor() ), // ok + null ); // cancel + + // "transparent" button and panel + final Box box = new Box( SwingConstants.VERTICAL ); + box.setBorder( new EmptyBorder( 10, 10, 10, 10 ) ); + box.add( Box.createVerticalGlue() ); + final JButton btnTransparent = new JButton( "Transparent" ); + btnTransparent.addActionListener( e -> { + app.setPointColor( new Color( 0, 0, 0, 0 ) ); + colorChooser.setVisible( false ); + }); + btnTransparent.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( btnTransparent ); + // choose fill color button + final JButton btnFillColor = new JButton() { + protected void paintComponent( Graphics g ) { + super.paintComponent(g); + g.setColor( app.getFill() != null ? app.getFill() : new Color( 0, 0, 0, 0 ) ); + g.fillRect( 0, 0, getWidth(), getHeight() ); + } + }; + JDUtils.fixSize( btnFillColor, 40, 40 ); + btnFillColor.addActionListener( e -> { + app.setPointColor( app.getFill() != null ? app.getFill() : new Color( 0, 0, 0, 0 ) ); + colorChooser.setVisible( false ); + } ); + btnFillColor.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( Box.createVerticalStrut( 16 ) ); + box.add( btnFillColor ); + JLabel label = new JLabel( getLocaleText( "lbl_fill_color" ), SwingConstants.CENTER ); + label.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( label ); + final JButton btnLineSwap = new JButton( getSmallIcon( "swap_colors.png" ) ); + btnLineSwap.setToolTipText( getLocaleText( "tip_swap_color" ) ); + JDUtils.fixSize( btnLineSwap, 32, 32 ); + btnLineSwap.addActionListener( e -> { + final Color point = app.getPointColor(); + app.setPointColor( app.getFill() != null ? app.getFill() : new Color( 0, 0, 0, 0 ) ); + app.setFill( point.getAlpha() > 0 ? point : null ); + colorChooser.setVisible( false ); + }); + btnLineSwap.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( btnLineSwap ); + // choose line color button + final JButton btnLineColor = new JButton() { + protected void paintComponent( Graphics g ) { + super.paintComponent(g); + g.setColor( app.getColor() ); + g.fillRect( 0, 0, getWidth(), getHeight() ); + } + }; + JDUtils.fixSize( btnLineColor, 40, 40 ); + btnLineColor.addActionListener( e -> { + app.setPointColor( app.getColor() ); + colorChooser.setVisible( false ); + } ); + btnLineColor.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( Box.createVerticalStrut( 16 ) ); + box.add( btnLineColor ); + label = new JLabel( getLocaleText( "lbl_line_color" ), SwingConstants.CENTER ); + label.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( label ); + final JButton btnPointSwap = new JButton( getSmallIcon( "swap_colors.png" ) ); + btnPointSwap.setToolTipText( getLocaleText( "tip_swap_color" ) ); + JDUtils.fixSize( btnPointSwap, 32, 32 ); + btnPointSwap.addActionListener( e -> { + final Color point = app.getPointColor(); + app.setPointColor( app.getColor() ); + app.setColor( point ); + colorChooser.setVisible( false ); + }); + btnPointSwap.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( btnPointSwap ); + + box.add( Box.createVerticalGlue() ); + colorChooser.add( box, BorderLayout.WEST ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + jcc.setColor( app.getPointColor() ); + jcc.requestFocus(); + colorChooser.setSize( new Dimension( 750, 380 ) ); + colorChooser.setVisible( true ); + } +} diff --git a/src/jdrafting/gui/controller/actions/PolyLineAction.java b/src/jdrafting/gui/controller/actions/PolyLineAction.java new file mode 100644 index 0000000..d1fdeef --- /dev/null +++ b/src/jdrafting/gui/controller/actions/PolyLineAction.java @@ -0,0 +1,40 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.PolygonListener; + +@SuppressWarnings("serial") +public class PolyLineAction extends AbstractAction +{ + private Application app; + + public PolyLineAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "polyline" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "polyline_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_polyline" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_4, InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "polyline.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "polyline.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( new PolygonListener( app.getCanvas(), false ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/PolygonAction.java b/src/jdrafting/gui/controller/actions/PolygonAction.java new file mode 100644 index 0000000..1b8323c --- /dev/null +++ b/src/jdrafting/gui/controller/actions/PolygonAction.java @@ -0,0 +1,40 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.PolygonListener; + +@SuppressWarnings("serial") +public class PolygonAction extends AbstractAction +{ + private Application app; + + public PolygonAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "polygon" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "polygon_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_polygon" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_3, InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "polygon.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "polygon.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( new PolygonListener( app.getCanvas(), true ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/PrintAction.java b/src/jdrafting/gui/controller/actions/PrintAction.java new file mode 100644 index 0000000..6f2afbe --- /dev/null +++ b/src/jdrafting/gui/controller/actions/PrintAction.java @@ -0,0 +1,106 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.Graphics2D; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; +import java.util.HashSet; + +import javax.swing.AbstractAction; +import javax.swing.JOptionPane; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; +import jdrafting.gui.Viewport; + +@SuppressWarnings("serial") +public class PrintAction extends AbstractAction +{ + private Application app; + + public PrintAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "print" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "print_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_print" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_L, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "print.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "print.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + if ( app.getExercise().isEmpty() ) + return; + + // set job and get a printer + final PrinterJob job = PrinterJob.getPrinterJob(); + job.setJobName( Application.APPNAME + ": " + app.getExercise().getTitle() ); + if ( !job.printDialog() ) + return; + // set printable image + job.setPrintable( (g, pageFormat, pageIndex) -> { + + final double pageWidth = pageFormat.getImageableWidth(), + pageHeight = pageFormat.getImageableHeight(); + + Rectangle2D bounds = app.getExercise().getBounds(); + final double marginX = bounds.getWidth() / 16., marginY = bounds.getHeight() / 16.; + bounds = new Rectangle2D.Double( bounds.getX() - marginX, + bounds.getY() - marginY, + bounds.getWidth() + 2*marginX, + bounds.getHeight() + 2*marginY ); + + final double scaleX = pageWidth / bounds.getWidth(), + scaleY = pageHeight / bounds.getHeight(), + scale = Math.min( scaleX, scaleY ); + + final BufferedImage img = new BufferedImage( (int) (bounds.getWidth() * scale), + (int) (bounds.getHeight() * scale), + BufferedImage.TYPE_INT_ARGB ); + final Graphics2D g2 = (Graphics2D) img.getGraphics(); + + // High quality render + JDUtils.setHighQualityRender( g2 ); + + // draw exercise in image context + CanvasPanel.drawExercise( g2, CanvasPanel.getTransform( + new Viewport( bounds ), + new Viewport( 0, img.getWidth() - 1, 0, img.getHeight() - 1 ) ), + app.getExercise(), + new HashSet<>(), true ); + + g.translate( (int) (pageFormat.getImageableX()), (int) (pageFormat.getImageableY()) ); + if ( pageIndex == 0 ) + { + g.drawImage( img, 0, 0, null ); + return Printable.PAGE_EXISTS; + } + return Printable.NO_SUCH_PAGE; + } ); + // print job + try + { + job.print(); + } + catch ( PrinterException ex ) + { + JOptionPane.showMessageDialog( app, ex.getMessage(), "Printing error", + JOptionPane.ERROR_MESSAGE ); + } + } +} diff --git a/src/jdrafting/gui/controller/actions/ProtractorAction.java b/src/jdrafting/gui/controller/actions/ProtractorAction.java new file mode 100644 index 0000000..476ed04 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/ProtractorAction.java @@ -0,0 +1,42 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.ProtractorListener; + +@SuppressWarnings("serial") +public class ProtractorAction extends AbstractAction +{ + private Application app; + + public ProtractorAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "protractor" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "protractor_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_protractor" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( + KeyEvent.VK_INSERT, InputEvent.ALT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "protractor.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "protractor.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( + new ProtractorListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/RectangleAction.java b/src/jdrafting/gui/controller/actions/RectangleAction.java new file mode 100644 index 0000000..f7bd30a --- /dev/null +++ b/src/jdrafting/gui/controller/actions/RectangleAction.java @@ -0,0 +1,41 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.RectangleListener; + +@SuppressWarnings("serial") +public class RectangleAction extends AbstractAction +{ + private Application app; + + public RectangleAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "rectangle" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "rectangle_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_rect" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_7, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "rectangle.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "rectangle.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( new RectangleListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/RedoAction.java b/src/jdrafting/gui/controller/actions/RedoAction.java new file mode 100644 index 0000000..c1c7cbc --- /dev/null +++ b/src/jdrafting/gui/controller/actions/RedoAction.java @@ -0,0 +1,45 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.util.HashSet; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; + +@SuppressWarnings("serial") +public class RedoAction extends AbstractAction +{ + private Application app; + + public RedoAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "redo" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_redo" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_Z, + InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "redo.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "redo.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + app.undoManager.redo(); + app.getCanvas().repaint(); + app.scrollList.repaint(); + app.setSelectedShapes( new HashSet<>() ); + app.refreshUndoRedo(); + } +} diff --git a/src/jdrafting/gui/controller/actions/RegularPolygonAction.java b/src/jdrafting/gui/controller/actions/RegularPolygonAction.java new file mode 100644 index 0000000..4b33c66 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/RegularPolygonAction.java @@ -0,0 +1,59 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.JOptionPane; +import javax.swing.JSpinner; +import javax.swing.KeyStroke; +import javax.swing.SpinnerNumberModel; + +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.RegularPolygonListener; + +@SuppressWarnings("serial") +public class RegularPolygonAction extends AbstractAction +{ + private Application app; + private int vertex = 5; + + public RegularPolygonAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "reg_poly" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "reg_poly_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_reg_pol" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_2, InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "reg_poly.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "reg_poly.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + CanvasPanel canvas = app.getCanvas(); + + // dialog for polygon vertex + final JSpinner spinFactor = new JSpinner( new SpinnerNumberModel( + vertex, 3, Integer.MAX_VALUE, 1 ) ); + spinFactor.addChangeListener( + evt -> vertex = (int) ( (JSpinner) evt.getSource() ).getValue() ); + + final int option = JOptionPane.showOptionDialog( app, spinFactor, + getLocaleText( "reg_poly_dlg" ), + JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE, + getLargeIcon( "reg_poly.png" ), null, null ); + if ( option != JOptionPane.OK_OPTION ) return; + + canvas.setCanvasListener( new RegularPolygonListener( canvas, vertex ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/RewindAction.java b/src/jdrafting/gui/controller/actions/RewindAction.java new file mode 100644 index 0000000..4c30475 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/RewindAction.java @@ -0,0 +1,62 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.ToastCanvasStep; + +@SuppressWarnings("serial") +public class RewindAction extends AbstractAction +{ + private Application app; + + public RewindAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "rewind" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "rewind_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_rewind" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_DOWN, InputEvent.ALT_DOWN_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "rewind.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "rewind.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + if ( !app.getExercise().isEmpty() ) + { + // update frame + app.getExercise().setFrameIndex( app.getExercise().getStartIndex() ); + // create step description toast + final JDraftingShape shape = + app.shapeList.getModel().get( app.getExercise().getStartIndex() - 1 ); + if ( app.currentToast != null ) + { + if ( app.currentToast.getClosingTimer() != null ) + app.currentToast.getClosingTimer().stop(); + app.currentToast.dispose(); + } + app.currentToast = new ToastCanvasStep( shape, app.getExercise().getFrameIndex(), + app.canvas.getLocationOnScreen() ) + .showToast(); + + // refresh + app.getCanvas().repaint(); + app.scrollList.repaint(); + } + } +} diff --git a/src/jdrafting/gui/controller/actions/RotationAction.java b/src/jdrafting/gui/controller/actions/RotationAction.java new file mode 100644 index 0000000..21204c8 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/RotationAction.java @@ -0,0 +1,48 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.JOptionPane; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.RotationListener; + +@SuppressWarnings("serial") +public class RotationAction extends AbstractAction +{ + private Application app; + + public RotationAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "rotation" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "rotation_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_rotation" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_R, InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "rotation.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "rotation.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + if ( app.getSelectedShapes().size() > 0 ) + app.getCanvas().setCanvasListener( + new RotationListener( app.getCanvas() ) ); + else + JOptionPane.showMessageDialog( app, + getLocaleText( "selected_shapes_msg" ), + getLocaleText( "rotation" ) + " error", + JOptionPane.ERROR_MESSAGE ); + } +} diff --git a/src/jdrafting/gui/controller/actions/RulerAction.java b/src/jdrafting/gui/controller/actions/RulerAction.java new file mode 100644 index 0000000..bd5fca0 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/RulerAction.java @@ -0,0 +1,45 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.RulerListener; + +@SuppressWarnings("serial") +public class RulerAction extends AbstractAction +{ + private Application app; + + public RulerAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "ruler" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "ruler_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_ruler" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( + KeyEvent.VK_BACK_SPACE, InputEvent.ALT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "ruler.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "ruler.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( + new RulerListener( app.getCanvas() ) ); + + app.setUseDistance( true ); + app.checkRuler.setSelected( true ); + } +} diff --git a/src/jdrafting/gui/controller/actions/SaveAction.java b/src/jdrafting/gui/controller/actions/SaveAction.java new file mode 100644 index 0000000..312ff0a --- /dev/null +++ b/src/jdrafting/gui/controller/actions/SaveAction.java @@ -0,0 +1,117 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.util.Arrays; + +import javax.swing.AbstractAction; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; +import javax.swing.filechooser.FileNameExtensionFilter; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; + +/** + * Save exercise to media in JDrafting format + */ +@SuppressWarnings("serial") +public class SaveAction extends AbstractAction +{ + private Application app; + private boolean as; + private static JFileChooser fileChooser; + private int counter = 1; + + public SaveAction( Application app, boolean as ) + { + this.app = app; + this.as = as; + + putValue( NAME, getLocaleText( as ? "save_as" : "save" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "save_des" ) ); + if ( !as ) + { + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_save" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_S, InputEvent.CTRL_MASK ) ); + } + else + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_save_as" ) ); + putValue( SMALL_ICON, getSmallIcon( as ? "save_as.png": "save.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( as ? "save_as.png" : "save.png" ) ); + + // create static file chooser dialog + fileChooser = new JFileChooser(); + } + + @Override + public void actionPerformed(ActionEvent e) + { + if ( app.getExercise().isEmpty() ) + { + JOptionPane.showMessageDialog( app, + getLocaleText( "save_error2" ), + getLocaleText( "save_error1" ), + JOptionPane.ERROR_MESSAGE ); + return; + } + + // update file chooser L&F + SwingUtilities.updateComponentTreeUI( fileChooser ); + + // set png format as the unique filter + Arrays.stream( fileChooser.getChoosableFileFilters() ) + .forEach( f -> fileChooser.removeChoosableFileFilter( f ) ); + fileChooser.addChoosableFileFilter( new FileNameExtensionFilter( + "JDrafting exercise (.jd)", "jd" ) ); + + // update file name + if ( app.getExercise().getTitle().trim().isEmpty() ) + fileChooser.setSelectedFile( + new File( "jd_" + ( counter++ ) + ".jd" ) ); + else + fileChooser.setSelectedFile( new File( + JDUtils.camelCase( app.getExercise().getTitle() ) +".jd" ) ); + + + String saveFilename = app.getSaveFilename(); + + // save dialog + if ( as || saveFilename == null || saveFilename.length() == 0 ) + { + if ( fileChooser.showSaveDialog( app ) + == JFileChooser.APPROVE_OPTION ) + { + saveFilename = fileChooser.getSelectedFile().getAbsolutePath(); + } + else + return; + } + + // save exercise + try ( FileOutputStream os = new FileOutputStream( saveFilename ) ) + { + ObjectOutputStream oos = new ObjectOutputStream( os ); + oos.writeObject( app.getExercise() ); + app.setSaveFilename( saveFilename ); + app.initializeUndoRedoSystem(); + } + catch ( IOException ex ) + { + JOptionPane.showMessageDialog( + app, ex, "Error while saving", JOptionPane.ERROR_MESSAGE ); + } + } +} diff --git a/src/jdrafting/gui/controller/actions/SaveImageAction.java b/src/jdrafting/gui/controller/actions/SaveImageAction.java new file mode 100644 index 0000000..d8b9384 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/SaveImageAction.java @@ -0,0 +1,326 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.Application.APPNAME; +import static jdrafting.gui.Application.PROJECT_PAGE; +import static jdrafting.gui.Application.VERSION; +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.KeyEvent; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.HashSet; + +import javax.imageio.ImageIO; +import javax.swing.AbstractAction; +import javax.swing.BorderFactory; +import javax.swing.GroupLayout; +import javax.swing.ImageIcon; +import javax.swing.JCheckBox; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.filechooser.FileNameExtensionFilter; + +import org.apache.batik.dom.GenericDOMImplementation; +import org.apache.batik.svggen.SVGGraphics2D; +import org.apache.batik.svggen.SVGGraphics2DIOException; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; + +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; +import jdrafting.gui.Viewport; + +@SuppressWarnings("serial") +public class SaveImageAction extends AbstractAction +{ + private Application app; + private CanvasPanel canvas; + private static ImageChooser fileChooser; + private static int counter = 1; + private static final int MINI_SIZE = 120; + + public SaveImageAction( Application app ) + { + this.app = app; + canvas = app.getCanvas(); + + putValue( NAME, getLocaleText( "save_image" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "save_image_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_save_img" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_I, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "save_image.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "save_image.png" ) ); + + // create static file chooser dialog + fileChooser = new ImageChooser(); + fileChooser.setPreferredSize( JDUtils.getScreenSize( 0.8f, 0.8f ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + if ( app.getExercise().isEmpty() ) + return; + + // update file chooser L&F + SwingUtilities.updateComponentTreeUI( fileChooser ); + + // set png format as the unique filter + Arrays.stream( fileChooser.getChoosableFileFilters() ) + .forEach( f -> fileChooser.removeChoosableFileFilter( f ) ); + FileNameExtensionFilter svgFilter = new FileNameExtensionFilter( "SVG images", "svg" ); + FileNameExtensionFilter pngFilter = new FileNameExtensionFilter( "PNG images", "png" ); + fileChooser.addChoosableFileFilter( svgFilter ); + fileChooser.addChoosableFileFilter( pngFilter ); + + // update file name + if ( app.getExercise().getTitle().trim().isEmpty() ) + fileChooser.setSelectedFile( new File( "jd_" + ( counter++ ) ) ); + else + fileChooser.setSelectedFile( new File( + JDUtils.camelCase( app.getExercise().getTitle() ) ) ); + // set fields + fileChooser.textHeight.setText( String.valueOf( getAutomaticHeight( 1000 ) ) ); + fileChooser.labelImage.setIcon( new ImageIcon( + createBImage( MINI_SIZE, MINI_SIZE, false ) ) ); + fileChooser.checkText.setSelected( app.isVisibleNames() ); + + if ( fileChooser.showSaveDialog( app ) == JFileChooser.APPROVE_OPTION ) + { + // get file selected and add filter extension + File filename = fileChooser.getSelectedFile(); + String ext = "." + + ( (FileNameExtensionFilter) fileChooser.getFileFilter() ) + .getExtensions()[0].toLowerCase(); + if ( !filename.getName().toLowerCase().endsWith( ext ) ) + filename = new File( filename.getAbsolutePath() + ext ); + + // overwrite dialog + if ( filename.exists() ) + { + int option = JOptionPane.showConfirmDialog( app, + getLocaleText( "overwrite1" ), + getLocaleText( "overwrite2" ) + " " + filename.getName(), + JOptionPane.YES_NO_OPTION ); + if ( option == JOptionPane.NO_OPTION ) + return; + } + + // save image + try + { + int width = Integer.parseInt( fileChooser.textWidth.getText() ); + int height = + Integer.parseInt( fileChooser.textHeight.getText() ); + + // saving image as PNG + if ( filename.getName().toLowerCase().endsWith( ".png" ) ) + { + BufferedImage img = createBImage( width, height, + fileChooser.checkText.isSelected() ); + ImageIO.write( img, "png", filename ); + } + // saving image as SVG + else + { + toSVG( filename, width, height ); + } + } + catch ( Exception ex ) + { + JOptionPane.showMessageDialog( app, ex, "Save error", + JOptionPane.ERROR_MESSAGE ); + } + } + } + + private int getAutomaticHeight( int width ) + { + final Rectangle2D bound = app.getExercise().getBounds(); + + return (int) Math.round( width * bound.getHeight() / bound.getWidth() ); + } + + /** + * Save SVG file using Apache Batik libraries + * @param filename SVG file name + * @param width image width + * @param height image height + * @throws UnsupportedEncodingException + * @throws SVGGraphics2DIOException + */ + private void toSVG( File filename, int width, int height ) + throws UnsupportedEncodingException, SVGGraphics2DIOException + { + // Get a DOMImplementation. + DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation(); + + // Create an instance of org.w3c.dom.Document. + String svgNS = "http://www.w3.org/2000/svg"; + Document document = domImpl.createDocument( svgNS, "svg", null ); + + // Create an instance of the SVG Generator. + SVGGraphics2D svgGenerator = new SVGGraphics2D(document); + + // draw background in SVG generator + if ( fileChooser.checkBackground.isSelected() ) + { + svgGenerator.setColor( canvas.getBackground() ); + svgGenerator.fillRect( 0, 0, width, height ); + } + + // draw exercise to SVG generator + CanvasPanel.drawExercise( svgGenerator, + CanvasPanel.getTransform( + new Viewport( app.getExercise().getBounds() ), + new Viewport( 0., width, 0., height ) ), + app.getExercise(), new HashSet<>(), + fileChooser.checkText.isSelected() ); + + // Stream out SVG to output using UTF-8 encoding + boolean useCSS = true; // we want to use CSS style attributes + try ( PrintWriter out = new PrintWriter( filename, "UTF-8" ) ) + { + svgGenerator.stream( out, useCSS ); + out.write( "" ); + } + catch ( FileNotFoundException e ) {} + } + + private BufferedImage createBImage( int width, int height, boolean text ) + { + Rectangle2D bounds = CanvasPanel.getExerciseBounds( app.getExercise(), + new Viewport( app.getExercise().getBounds() ), + new Viewport( 0, width, 0, height ) ); + BufferedImage img = new BufferedImage( width, height, BufferedImage.TYPE_INT_ARGB ); + + Graphics2D g2 = (Graphics2D) img.getGraphics(); + + // High quality render + JDUtils.setHighQualityRender( g2 ); + + // draw background in image + g2.setColor( fileChooser.checkBackground.isSelected() + ? canvas.getBackground() + : new Color( 0, 0, 0, 0 ) ); + g2.fillRect( 0, 0, img.getWidth(), img.getHeight() ); + + // draw exercise in image context + CanvasPanel.drawExercise( g2, CanvasPanel.getTransform( + new Viewport( bounds ), + new Viewport( 0, img.getWidth() - 1, 0, img.getHeight() - 1 ) ), + app.getExercise(), new HashSet<>(), text ); + + return img; + } + + private class ImageChooser extends JFileChooser + { + private JTextField textWidth = new JTextField( "1000" ); + private JTextField textHeight = new JTextField(); + private JCheckBox checkBackground = new JCheckBox( + getLocaleText( "save_image_acce2" ), true ); + private JCheckBox checkText = new JCheckBox( + getLocaleText( "save_image_acce3" ), app.isVisibleNames() ); + private JLabel labelImage = new JLabel(); + + private ImageChooser() + { + // custom accessory + textWidth.setPreferredSize( new Dimension( 80, 30 ) ); + textWidth.getDocument() + .addDocumentListener( new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) + { + try + { + textHeight.setText( String.valueOf( getAutomaticHeight( + Integer.parseInt( textWidth.getText() ) ) ) ); + } + catch ( NumberFormatException ex ) {} + } + @Override + public void removeUpdate(DocumentEvent e) + { + insertUpdate( e ); + } + @Override + public void changedUpdate(DocumentEvent e) + { + insertUpdate( e ); + } + }); + textHeight.setPreferredSize( new Dimension( 80, 30 ) ); + JLabel labelWidth = new JLabel( getLocaleText( "width" ) + ":" ); + JLabel labelHeight = new JLabel( getLocaleText( "height" ) + ":" ); + checkBackground.addItemListener( new ItemListener() { + @Override + public void itemStateChanged( ItemEvent e ) + { + labelImage.setIcon( new ImageIcon( + createBImage( MINI_SIZE, MINI_SIZE, false ) ) ); + } + }); + + JPanel panel = new JPanel(); + panel.setBorder( BorderFactory.createTitledBorder( + getLocaleText( "save_image_acce1" ) ) ); + + GroupLayout layout = new GroupLayout( panel ); + panel.setLayout( layout ); + layout.setAutoCreateGaps(true); + layout.setAutoCreateContainerGaps(true); + + layout.setHorizontalGroup( layout.createSequentialGroup() + .addGroup( layout.createParallelGroup( GroupLayout.Alignment.CENTER ) + .addComponent( labelImage ) + .addGroup( layout.createSequentialGroup() + .addComponent( labelWidth ) + .addComponent( textWidth ) ) + .addGroup( layout.createSequentialGroup() + .addComponent( labelHeight ) + .addComponent( textHeight ) ) + .addComponent( checkBackground ) + .addComponent( checkText ) ) ); + layout.setVerticalGroup( layout.createSequentialGroup() + .addComponent( labelImage ) + .addGroup( layout.createParallelGroup( GroupLayout.Alignment.BASELINE ) + .addComponent( labelWidth ) + .addComponent( textWidth ) ) + .addGroup( layout.createParallelGroup( GroupLayout.Alignment.BASELINE ) + .addComponent( labelHeight ) + .addComponent( textHeight ) ) + .addComponent( checkBackground ) + .addComponent( checkText ) ); + layout.linkSize( labelWidth, labelHeight ); + layout.linkSize( textWidth, textHeight ); + + setAccessory( panel ); + } + } +} diff --git a/src/jdrafting/gui/controller/actions/SegmentAction.java b/src/jdrafting/gui/controller/actions/SegmentAction.java new file mode 100644 index 0000000..864185c --- /dev/null +++ b/src/jdrafting/gui/controller/actions/SegmentAction.java @@ -0,0 +1,40 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.SegmentListener; + +@SuppressWarnings("serial") +public class SegmentAction extends AbstractAction +{ + private Application app; + + public SegmentAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "segment" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "segment_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_segment" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_2, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "segment.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "segment.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( new SegmentListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/SelectAllAction.java b/src/jdrafting/gui/controller/actions/SelectAllAction.java new file mode 100644 index 0000000..ac62ca5 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/SelectAllAction.java @@ -0,0 +1,44 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.util.HashSet; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; + +@SuppressWarnings("serial") +public class SelectAllAction extends AbstractAction +{ + private Application app; + + public SelectAllAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "select_all" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "select_all_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_sel_all" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_A, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "select_all.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "select_all.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.setSelectedShapes( new HashSet<>( app.getExercise().getShapes() ) ); + + app.scrollList.repaint(); + app.getCanvas().repaint(); + } +} diff --git a/src/jdrafting/gui/controller/actions/SelectionAction.java b/src/jdrafting/gui/controller/actions/SelectionAction.java new file mode 100644 index 0000000..2e78a89 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/SelectionAction.java @@ -0,0 +1,42 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.SelectionListener; + +@SuppressWarnings("serial") +public class SelectionAction extends AbstractAction +{ + private Application app; + + public SelectionAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "selection" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "selection_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_selection" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_0, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "selection.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "selection.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( + new SelectionListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/ShapeColorAction.java b/src/jdrafting/gui/controller/actions/ShapeColorAction.java new file mode 100644 index 0000000..42d4ba2 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/ShapeColorAction.java @@ -0,0 +1,137 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.Box; +import javax.swing.JButton; +import javax.swing.JColorChooser; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.KeyStroke; +import javax.swing.SwingConstants; +import javax.swing.border.EmptyBorder; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; + +@SuppressWarnings("serial") +public class ShapeColorAction extends AbstractAction +{ + private Application app; + private JDialog colorChooser; + private JColorChooser jcc; + + public ShapeColorAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "color" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "color_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_shape_col" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_J, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "color.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "color.png" ) ); + + jcc = new JColorChooser(); + colorChooser = JColorChooser.createDialog( + app, getLocaleText( "color_des" ), true, jcc, + evt -> app.setColor( jcc.getColor() ), // ok + null ); // cancel + + // "transparent" button and panel + final Box box = new Box( SwingConstants.VERTICAL ); + box.setBorder( new EmptyBorder( 10, 10, 10, 10 ) ); + box.add( Box.createVerticalGlue() ); + final JButton btnTransparent = new JButton( "Transparent" ); + btnTransparent.addActionListener( e -> { + app.setColor( new Color( 0, 0, 0, 0 ) ); + colorChooser.setVisible( false ); + }); + btnTransparent.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( btnTransparent ); + // choose fill color button + final JButton btnFillColor = new JButton() { + protected void paintComponent( Graphics g ) { + super.paintComponent(g); + g.setColor( app.getFill() != null ? app.getFill() : new Color( 0, 0, 0, 0 ) ); + g.fillRect( 0, 0, getWidth(), getHeight() ); + } + }; + JDUtils.fixSize( btnFillColor, 40, 40 ); + btnFillColor.addActionListener( e -> { + app.setColor( app.getFill() != null ? app.getFill() : new Color( 0, 0, 0, 0 ) ); + colorChooser.setVisible( false ); + } ); + btnFillColor.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( Box.createVerticalStrut( 16 ) ); + box.add( btnFillColor ); + JLabel label = new JLabel( getLocaleText( "lbl_fill_color" ), SwingConstants.CENTER ); + label.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( label ); + final JButton btnLineSwap = new JButton( getSmallIcon( "swap_colors.png" ) ); + btnLineSwap.setToolTipText( getLocaleText( "tip_swap_color" ) ); + JDUtils.fixSize( btnLineSwap, 32, 32 ); + btnLineSwap.addActionListener( e -> { + final Color line = app.getColor(); + app.setColor( app.getFill() != null ? app.getFill() : new Color( 0, 0, 0, 0 ) ); + app.setFill( line.getAlpha() > 0 ? line : null ); + colorChooser.setVisible( false ); + }); + btnLineSwap.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( btnLineSwap ); + // choose point color button + final JButton btnPointColor = new JButton() { + protected void paintComponent( Graphics g ) { + super.paintComponent(g); + g.setColor( app.getPointColor() ); + g.fillRect( 0, 0, getWidth(), getHeight() ); + } + }; + JDUtils.fixSize( btnPointColor, 40, 40 ); + btnPointColor.addActionListener( e -> { + app.setColor( app.getPointColor() ); + colorChooser.setVisible( false ); + }); + btnPointColor.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( Box.createVerticalStrut( 16 ) ); + box.add( btnPointColor ); + label = new JLabel( getLocaleText( "lbl_point_color" ), SwingConstants.CENTER ); + label.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( label ); + final JButton btnPointSwap = new JButton( getSmallIcon( "swap_colors.png" ) ); + btnPointSwap.setToolTipText( getLocaleText( "tip_swap_color" ) ); + JDUtils.fixSize( btnPointSwap, 32, 32 ); + btnPointSwap.addActionListener( e -> { + final Color point = app.getPointColor(); + app.setPointColor( app.getColor() ); + app.setColor( point ); + colorChooser.setVisible( false ); + } ); + btnPointSwap.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( btnPointSwap ); + + box.add( Box.createVerticalGlue() ); + colorChooser.add( box, BorderLayout.WEST ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + jcc.setColor( app.getColor() ); + jcc.requestFocus(); + colorChooser.setSize( new Dimension( 750, 380 ) ); + colorChooser.setVisible( true ); + } +} diff --git a/src/jdrafting/gui/controller/actions/ShapeFillAction.java b/src/jdrafting/gui/controller/actions/ShapeFillAction.java new file mode 100644 index 0000000..c0ab5bd --- /dev/null +++ b/src/jdrafting/gui/controller/actions/ShapeFillAction.java @@ -0,0 +1,155 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.Box; +import javax.swing.JButton; +import javax.swing.JColorChooser; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.KeyStroke; +import javax.swing.SwingConstants; +import javax.swing.border.EmptyBorder; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; + +/** + * @since 0.1.11.2 + * @version 0.1.12 + */ +@SuppressWarnings("serial") +public class ShapeFillAction extends AbstractAction +{ + private Application app; + private JDialog colorChooser; + private JColorChooser jcc; + + public ShapeFillAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "fill" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "fill_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_shape_fill" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_R, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "fill_color.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "fill_color.png" ) ); + + // JFileChooser + jcc = new JColorChooser(); + colorChooser = JColorChooser.createDialog( app, getLocaleText( "fill_des" ), true, jcc, + evt -> app.setFill( jcc.getColor() ), // ok + null ); // cancel + // "remove fill" button and panel + final Box box = new Box( SwingConstants.VERTICAL ); + box.setBorder( new EmptyBorder( 10, 10, 10, 10 ) ); + box.add( Box.createVerticalGlue() ); + final JButton btnRemove = new JButton() { + @Override + protected void paintComponent( Graphics g ) { + super.paintComponent( g ); + for ( int n = 5, step = getWidth() / n, i = 0; i < n; i++ ) + for ( int j = 0; j < n; j++ ) + { + g.setColor( ((i+j)&1) == 0 ? Color.GRAY : Color.DARK_GRAY ); + g.fillRect( i*step, j*step, step, step ); + } + } + }; + JDUtils.fixSize( btnRemove, 40, 40 ); + btnRemove.addActionListener( e -> { + app.setFill(null); + colorChooser.setVisible( false ); + }); + btnRemove.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( btnRemove ); + JLabel label = new JLabel( getLocaleText( "lbl_no_fill" ), SwingConstants.CENTER ); + label.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( label ); + // choose line color button + final JButton btnLineColor = new JButton() { + protected void paintComponent( Graphics g ) { + super.paintComponent(g); + g.setColor( app.getColor() ); + g.fillRect( 0, 0, getWidth(), getHeight() ); + } + }; + JDUtils.fixSize( btnLineColor, 40, 40 ); + btnLineColor.addActionListener( e -> { + app.setFill( app.getColor() ); + colorChooser.setVisible( false ); + } ); + btnLineColor.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( Box.createVerticalStrut( 16 ) ); + box.add( btnLineColor ); + label = new JLabel( getLocaleText( "lbl_line_color" ), SwingConstants.CENTER ); + label.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( label ); + final JButton btnLineSwap = new JButton( getSmallIcon( "swap_colors.png" ) ); + btnLineSwap.setToolTipText( getLocaleText( "tip_swap_color" ) ); + JDUtils.fixSize( btnLineSwap, 32, 32 ); + btnLineSwap.addActionListener( e -> { + final Color line = app.getColor(); + app.setColor( app.getFill() != null ? app.getFill() : new Color( 0, 0, 0, 0 ) ); + app.setFill( line.getAlpha() > 0 ? line : null ); + colorChooser.setVisible( false ); + }); + btnLineSwap.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( btnLineSwap ); + // choose point color button + final JButton btnPointColor = new JButton() { + protected void paintComponent( Graphics g ) { + super.paintComponent(g); + g.setColor( app.getPointColor() ); + g.fillRect( 0, 0, getWidth(), getHeight() ); + } + }; + JDUtils.fixSize( btnPointColor, 40, 40 ); + btnPointColor.addActionListener( e -> { + app.setFill( app.getPointColor() ); + colorChooser.setVisible( false ); + } ); + btnPointColor.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( Box.createVerticalStrut( 16 ) ); + box.add( btnPointColor ); + label = new JLabel( getLocaleText( "lbl_point_color" ), SwingConstants.CENTER ); + label.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( label ); + final JButton btnPointSwap = new JButton( getSmallIcon( "swap_colors.png" ) ); + btnPointSwap.setToolTipText( getLocaleText( "tip_swap_color" ) ); + JDUtils.fixSize( btnPointSwap, 32, 32 ); + btnPointSwap.addActionListener( e -> { + final Color point = app.getPointColor(); + app.setPointColor( app.getFill() != null ? app.getFill() : new Color( 0, 0, 0, 0 ) ); + app.setFill( point.getAlpha() > 0 ? point : null ); + colorChooser.setVisible( false ); + } ); + btnPointSwap.setAlignmentX( Component.CENTER_ALIGNMENT ); + box.add( btnPointSwap ); + + box.add( Box.createVerticalGlue() ); + colorChooser.add( box, BorderLayout.WEST ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + jcc.setColor( app.getFill() ); + jcc.requestFocus(); + colorChooser.setSize( new Dimension( 750, 380 ) ); + colorChooser.setVisible( true ); + } +} diff --git a/src/jdrafting/gui/controller/actions/SplineAction.java b/src/jdrafting/gui/controller/actions/SplineAction.java new file mode 100644 index 0000000..30e3c56 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/SplineAction.java @@ -0,0 +1,42 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.SplineListener; + +@SuppressWarnings("serial") +public class SplineAction extends AbstractAction +{ + private Application app; + + public SplineAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "spline" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "spline_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_spline" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_8, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "spline.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "spline.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( + new SplineListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/TextBoxAction.java b/src/jdrafting/gui/controller/actions/TextBoxAction.java new file mode 100644 index 0000000..e3295ed --- /dev/null +++ b/src/jdrafting/gui/controller/actions/TextBoxAction.java @@ -0,0 +1,40 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.TextBoxListener; + +@SuppressWarnings("serial") +public class TextBoxAction extends AbstractAction +{ + private Application app; + + public TextBoxAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "comment" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "comment_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_comment" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_T, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "text.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "text.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( new TextBoxListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/TextVisibleAction.java b/src/jdrafting/gui/controller/actions/TextVisibleAction.java new file mode 100644 index 0000000..886873c --- /dev/null +++ b/src/jdrafting/gui/controller/actions/TextVisibleAction.java @@ -0,0 +1,41 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; + +@SuppressWarnings("serial") +public class TextVisibleAction extends AbstractAction +{ + private Application app; + + public TextVisibleAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "text" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "text_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_text" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_W, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "names.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "names.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.setVisibleNames( !app.isVisibleNames() ); + app.getCanvas().repaint(); + } +} diff --git a/src/jdrafting/gui/controller/actions/TranslationAction.java b/src/jdrafting/gui/controller/actions/TranslationAction.java new file mode 100644 index 0000000..7c5d52d --- /dev/null +++ b/src/jdrafting/gui/controller/actions/TranslationAction.java @@ -0,0 +1,47 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.JOptionPane; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.TranslationListener; + +@SuppressWarnings("serial") +public class TranslationAction extends AbstractAction +{ + private Application app; + + public TranslationAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "translation" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "translation_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_trans" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_T, InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "translation.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "translation.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + if ( app.getSelectedShapes().size() > 0 ) + app.getCanvas().setCanvasListener( new TranslationListener( app.getCanvas() ) ); + else + JOptionPane.showMessageDialog( app, + getLocaleText( "selected_shapes_msg" ), + getLocaleText( "translation" ) + " error", + JOptionPane.ERROR_MESSAGE ); + } +} diff --git a/src/jdrafting/gui/controller/actions/TriangleAction.java b/src/jdrafting/gui/controller/actions/TriangleAction.java new file mode 100644 index 0000000..8645387 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/TriangleAction.java @@ -0,0 +1,40 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.TriangleListener; + +@SuppressWarnings("serial") +public class TriangleAction extends AbstractAction +{ + private Application app; + + public TriangleAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "triangle" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "triangle_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_triangle" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_1, InputEvent.SHIFT_MASK ) ); + putValue( SMALL_ICON, JDUtils.getSmallIcon( "triangle.png" ) ); + putValue( LARGE_ICON_KEY, JDUtils.getLargeIcon( "triangle.png" ) ); + } + + @Override + public void actionPerformed(ActionEvent e) + { + app.getCanvas().setCanvasListener( + new TriangleListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/TrianglePointsAction.java b/src/jdrafting/gui/controller/actions/TrianglePointsAction.java new file mode 100644 index 0000000..d06c1c2 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/TrianglePointsAction.java @@ -0,0 +1,63 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleMnemonic; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.ImageIcon; + +import jdrafting.gui.Application; +import jdrafting.gui.controller.mouse.TrianglePointsListener; + +@SuppressWarnings("serial") +public class TrianglePointsAction extends AbstractAction +{ + private Application app; + private int type; + + public TrianglePointsAction( Application app, int type ) + { + this.app = app; + this.type = type; + + putValue( NAME, TrianglePointsListener.getName( type ) ); + ImageIcon small = null, large = null; + int mnemonic = -1; + switch( type ) + { + case TrianglePointsListener.INCENTER: + small = getSmallIcon( "incenter.png" ); + large = getLargeIcon( "incenter.png" ); + mnemonic = getLocaleMnemonic( "mne_incenter" ); + break; + case TrianglePointsListener.BARICENTER: + small = getSmallIcon( "baricenter.png" ); + large = getLargeIcon( "baricenter.png" ); + mnemonic = getLocaleMnemonic( "mne_baricenter" ); + break; + case TrianglePointsListener.CIRCUMCENTER: + small = getSmallIcon( "circumcenter.png" ); + large = getLargeIcon( "circumcenter.png" ); + mnemonic = getLocaleMnemonic( "mne_circumcenter" ); + break; + case TrianglePointsListener.ORTOCENTER: + small = getSmallIcon( "ortocenter.png" ); + large = getLargeIcon( "ortocenter.png" ); + mnemonic = getLocaleMnemonic( "mne_ortocenter" ); + break; + } + putValue( MNEMONIC_KEY, mnemonic ); + putValue( SMALL_ICON, small ); + putValue( LARGE_ICON_KEY, large ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + app.getCanvas().setCanvasListener( + new TrianglePointsListener( app.getCanvas(), type ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/UndoAction.java b/src/jdrafting/gui/controller/actions/UndoAction.java new file mode 100644 index 0000000..6b6872c --- /dev/null +++ b/src/jdrafting/gui/controller/actions/UndoAction.java @@ -0,0 +1,44 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.util.HashSet; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; + +@SuppressWarnings("serial") +public class UndoAction extends AbstractAction +{ + private Application app; + + public UndoAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "undo" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_undo" ) ); + putValue( ACCELERATOR_KEY, + KeyStroke.getKeyStroke( KeyEvent.VK_Z, InputEvent.CTRL_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "undo.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "undo.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + app.undoManager.undo(); + app.getCanvas().repaint(); + app.scrollList.repaint(); + app.setSelectedShapes( new HashSet<>() ); + app.refreshUndoRedo(); + } +} diff --git a/src/jdrafting/gui/controller/actions/UndoRedoHistoryAction.java b/src/jdrafting/gui/controller/actions/UndoRedoHistoryAction.java new file mode 100644 index 0000000..67f520e --- /dev/null +++ b/src/jdrafting/gui/controller/actions/UndoRedoHistoryAction.java @@ -0,0 +1,75 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.Color; +import java.awt.Point; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.WindowEvent; +import java.awt.event.WindowFocusListener; + +import javax.swing.AbstractAction; +import javax.swing.BorderFactory; +import javax.swing.JScrollPane; +import javax.swing.JWindow; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.UndoRedoHistoryList; + +@SuppressWarnings("serial") +public class UndoRedoHistoryAction extends AbstractAction +{ + private Application app; + + public UndoRedoHistoryAction( Application app ) + { + this.app = app; + + putValue( NAME, "\u25BC" ); + putValue( SHORT_DESCRIPTION, getLocaleText( "tip_undo_redo" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( "F2" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + if ( app.undoRedoWindow != null ) + app.undoRedoWindow.dispose(); + + // create window, list and scroll + final JWindow win = app.undoRedoWindow = new JWindow( app ); + final UndoRedoHistoryList lst = new UndoRedoHistoryList( app ); + win.add( new JScrollPane( lst ) ); + + // config + lst.setBorder( BorderFactory.createLineBorder( Color.DARK_GRAY, 2, true ) ); + win.addWindowFocusListener( new WindowFocusListener() { + @Override + public void windowLostFocus( WindowEvent e ) + { + win.dispose(); + app.refreshUndoRedo(); + } + @Override + public void windowGainedFocus( WindowEvent e ) {} + }); + win.pack(); + + // window position + final Point loc = app.btnHistory.getLocationOnScreen(); + win.setLocation( loc.x, + loc.y+win.getHeight() > Toolkit.getDefaultToolkit().getScreenSize().height + ? loc.y - win.getHeight() + : loc.y + app.btnHistory.getHeight() ); + + // show window + win.setVisible( true ); + + // gain focus + lst.requestFocus(); + lst.requestFocusInWindow(); + } + +} diff --git a/src/jdrafting/gui/controller/actions/VertexAction.java b/src/jdrafting/gui/controller/actions/VertexAction.java new file mode 100644 index 0000000..729dc54 --- /dev/null +++ b/src/jdrafting/gui/controller/actions/VertexAction.java @@ -0,0 +1,40 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.JDUtils; +import jdrafting.gui.controller.mouse.VertexListener; + +@SuppressWarnings("serial") +public class VertexAction extends AbstractAction +{ + private Application app; + + public VertexAction( Application app ) + { + this.app = app; + + putValue( NAME, getLocaleText( "vertex" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "vertex_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_vertex" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_8, InputEvent.ALT_MASK ) ); + putValue( SMALL_ICON, getSmallIcon( "vertex.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "vertex.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + app.getCanvas().setCanvasListener( new VertexListener( app.getCanvas() ) ); + } +} diff --git a/src/jdrafting/gui/controller/actions/ZoomAllAction.java b/src/jdrafting/gui/controller/actions/ZoomAllAction.java new file mode 100644 index 0000000..f86ee2e --- /dev/null +++ b/src/jdrafting/gui/controller/actions/ZoomAllAction.java @@ -0,0 +1,63 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; +import java.awt.geom.Rectangle2D; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; +import jdrafting.gui.Viewport; + +@SuppressWarnings("serial") +public class ZoomAllAction extends AbstractAction +{ + private Application app; + private CanvasPanel canvas; + + public ZoomAllAction( Application app ) + { + this.app = app; + canvas = app.getCanvas(); + + putValue( NAME, getLocaleText( "zoom_all" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( "zoom_all_des" ) ); + putValue( MNEMONIC_KEY, JDUtils.getLocaleMnemonic( "mne_zoom_all" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( "typed *" ) ); + putValue( SMALL_ICON, getSmallIcon( "zoom_all.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( "zoom_all.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + if ( !app.getExercise().getShapes().isEmpty() ) + { + Rectangle2D enclosure = CanvasPanel.getExerciseBounds( + app.getExercise(), + new Viewport( app.getExercise().getBounds() ), + canvas.getCanvasViewport() ); + if ( Double.isNaN( enclosure.getWidth() ) + || Double.isNaN( enclosure.getHeight() ) ) + { + enclosure = new Rectangle2D.Double( 0., 0., 1., 1. ); + } + + canvas.setViewport( new Viewport( enclosure ) ); + /*double maxsize = Math.max( enclosure.getWidth(), enclosure.getHeight() ); + canvas.setViewport( new Viewport( + enclosure.getCenterX() - maxsize / 2, + enclosure.getCenterX() + maxsize / 2, + enclosure.getCenterY() - maxsize / 2, + enclosure.getCenterY() + maxsize / 2 ) );*/ + + canvas.repaint(); + } + } +} diff --git a/src/jdrafting/gui/controller/actions/ZoomInOutAction.java b/src/jdrafting/gui/controller/actions/ZoomInOutAction.java new file mode 100644 index 0000000..bb4a45e --- /dev/null +++ b/src/jdrafting/gui/controller/actions/ZoomInOutAction.java @@ -0,0 +1,56 @@ +package jdrafting.gui.controller.actions; + +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; +import static jdrafting.gui.JDUtils.getSmallIcon; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; +import jdrafting.gui.Viewport; + +@SuppressWarnings("serial") +public class ZoomInOutAction extends AbstractAction +{ + private final double ZOOM_FACTOR; + private CanvasPanel canvas; + + public ZoomInOutAction( Application app, boolean zoomIn ) + { + canvas = app.getCanvas(); + ZOOM_FACTOR = zoomIn ? 1 / Math.sqrt( 2 ) : Math.sqrt( 2 ); + + putValue( NAME, getLocaleText( zoomIn ? "zoom_in" : "zoom_out" ) ); + putValue( SHORT_DESCRIPTION, getLocaleText( zoomIn ? "zoom_in_des" : "zoom_out_des" ) ); + putValue( MNEMONIC_KEY, zoomIn + ? JDUtils.getLocaleMnemonic( "mne_zoom_in" ) + : JDUtils.getLocaleMnemonic( "mne_zoom_out" ) ); + putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( zoomIn ? "typed +" : "typed -" ) ); + putValue( SMALL_ICON, getSmallIcon( zoomIn ? "zoom_in.png" : "zoom_out.png" ) ); + putValue( LARGE_ICON_KEY, getLargeIcon( zoomIn ? "zoom_in.png" : "zoom_out.png" ) ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + final Viewport oldView = canvas.getViewport(); + + // avoid very small zooms for accuracy bugs + if ( ZOOM_FACTOR < 1. ) + if ( oldView.getWidth() < 1. || oldView.getHeight() < 1. ) + return; + + final double w = oldView.getWidth() * ZOOM_FACTOR, + h = oldView.getHeight() * ZOOM_FACTOR; + canvas.setViewport( new Viewport( oldView.getCenterX() - w / 2, + oldView.getCenterX() + w / 2, + oldView.getCenterY() - h / 2, + oldView.getCenterY() + h / 2 ) ); + canvas.repaint(); + } +} diff --git a/src/jdrafting/gui/controller/mouse/AbstractCanvasMouseListener.java b/src/jdrafting/gui/controller/mouse/AbstractCanvasMouseListener.java new file mode 100644 index 0000000..73d8dc5 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/AbstractCanvasMouseListener.java @@ -0,0 +1,99 @@ +package jdrafting.gui.controller.mouse; + +import java.awt.Component; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.awt.geom.Point2D; + +import javax.swing.event.MouseInputAdapter; + +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.Viewport; + +public abstract class AbstractCanvasMouseListener extends MouseInputAdapter + implements MouseWheelListener +{ + // last mouse event + private MouseEvent lastMouseEvent; + + public AbstractCanvasMouseListener( Component mouseEventSource ) + { + // create initial MouseEvent + Point mousePos = mouseEventSource.getMousePosition(); + if ( mousePos == null ) + mousePos = new Point( 0, 0 ); + lastMouseEvent = new MouseEvent( mouseEventSource, + MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, + mousePos.x, mousePos.y, 0, false, MouseEvent.NOBUTTON ); + } + + /** + * Get last mouse event + * @return the last mouse event + */ + final public MouseEvent mouse() { return lastMouseEvent; }; + + /** + * Draw tool graphics over a given Graphics2D (tipically canvas graphics) + * @param g2 graphics to draw tool + */ + public void paintTool( Graphics2D g2 ) {} + + @Override + public void mouseWheelMoved( MouseWheelEvent e ) + { + // zoom over canvas + if ( e.getSource() instanceof CanvasPanel ) + { + final CanvasPanel canvas = (CanvasPanel) e.getSource(); + + // mouse logic viewport position + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // zoom in mouse position + final double factor = Math.pow( 2, 1. / 4 ); // zoom factor + final Viewport oldView = canvas.getViewport(); + // (avoid very small viewports due to accuracy bugs) + if ( e.getPreciseWheelRotation() < 0 ) + if ( oldView.getWidth() < 1. || oldView.getHeight() < 1. ) + return; + canvas.getViewport().zoom( logicMouse.getX(), logicMouse.getY(), + e.getPreciseWheelRotation() < 0 ? 1 / factor : factor ); + + // update canvas + canvas.repaint(); + } + } + + @Override + public void mouseMoved( MouseEvent e ) + { + mouseUserInfo( e ); + } + + @Override + public void mousePressed( MouseEvent e ) + { + mouseUserInfo( e ); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + mouseUserInfo( e ); + } + + @Override + public void mouseDragged( MouseEvent e ) + { + mouseUserInfo( e ); + } + + protected void mouseUserInfo( MouseEvent e ) + { + lastMouseEvent = e; + } +} diff --git a/src/jdrafting/gui/controller/mouse/AngleListener.java b/src/jdrafting/gui/controller/mouse/AngleListener.java new file mode 100644 index 0000000..b405d5f --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/AngleListener.java @@ -0,0 +1,211 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.geom.JDMath.pointRelativeToCenter; +import static jdrafting.geom.JDMath.projection; +import static jdrafting.geom.JDMath.vector; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Arc2D; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; + +import jdrafting.geom.JDMath; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +/** + * Create segment by angle using mouse control + */ +public class AngleListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "angle_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private Point2D vertex, p1; + + public AngleListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_angle1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( vertex != null ) + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // put vertex + if ( vertex == null ) + { + vertex = logicMouse; + app.setStatusText( getLocaleText( "txt_angle2" ) ); + } + // put first angle side + else if ( p1 == null ) + { + p1 = logicMouse; + app.setStatusText( getLocaleText( "txt_angle3" ) ); + } + // put second angle side + else + { + final Line2D segment = getSegment( logicMouse ); + + //////////////////////////// TRANSACTION //////////////////////////// + @SuppressWarnings("serial") + final JDCompoundEdit transaction = new JDCompoundEdit() { + public String getPresentationName() { + return getLocaleText( "angle" ) + " (" + edits.size() + " segment/s)"; + } + }; + + // add segment to exercise + app.addShapeFromIterator( segment.getPathIterator( null ), "", + getLocaleText( "new_segment" ), + app.getColor(), null, app.getStroke(), transaction ); + if ( isTwoSides() ) // add first side + app.addShapeFromIterator( new Line2D.Double( vertex, p1 ).getPathIterator( null ), + "", getLocaleText( "new_segment" ), + app.getColor(), null, app.getStroke(), transaction ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + + canvas.repaint(); + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( vertex != null ) + { + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // get transform + final AffineTransform transform = canvas.getTransform(); + + // set tool style + g2.setStroke( new BasicStroke( 1f ) ); + g2.setColor( Application.toolMainColor ); + + // draw first side + final Line2D line1 = new Line2D.Double( vertex, p1 == null ? logicMouse : p1 ); + g2.draw( transform.createTransformedShape( line1 ) ); + + if ( p1 != null ) + { + // draw second side + final Line2D line2 = getSegment( logicMouse ); + g2.draw( transform.createTransformedShape( line2 ) ); + + // draw angle + final double ang = Math.toDegrees( JDMath.lineAng( line1, line2 ) ), + dist = Math.min( vertex.distance( p1 ), + vertex.distance( line2.getP2() ) ) / 2., + offang = Math.toDegrees( Math.atan2( + p1.getY() - vertex.getY(), p1.getX() - vertex.getX() ) ); + + final Arc2D arc = new Arc2D.Double( + vertex.getX() - dist / 2, vertex.getY() - dist / 2, + dist, dist, + -offang, line1.relativeCCW( logicMouse ) <= 0 ? -ang : ang, + Arc2D.PIE ); + + g2.setColor( Color.MAGENTA ); + g2.setStroke( new BasicStroke( 2f, BasicStroke.CAP_ROUND, + BasicStroke.JOIN_ROUND, 0f, new float[] { 3f, 5f }, + (float) Math.random() * 4 ) ); + g2.draw( transform.createTransformedShape( arc ) ); + + // draw angle info + if ( !Double.isNaN( ang ) ) + { + final int mouseX = mouse().getX(), mouseY = mouse().getY(); + final String angInfo = String.format( "%.2f", ang ) + "º"; + g2.setFont( new Font( Font.SERIF, Font.BOLD, 16 ) ); + g2.setColor( new Color( 40, 40, 180 ) ); + g2.drawString( angInfo, mouseX + 21, mouseY - 9 ); + g2.setColor( Color.LIGHT_GRAY ); + g2.drawString( angInfo, mouseX + 20, mouseY - 10 ); + } + } + } + } + + + // --- HELPERS --- + + // check modifiers + private boolean isSuplementary() { return mouse().isShiftDown(); } + private boolean isComplementary() { return mouse().isControlDown(); } + private boolean isTwoSides() { return mouse().isAltDown(); } + + /** + * Gets the segment in the logic viewport + * @param logicMouse + * @return the segment + */ + private Line2D getSegment( Point2D logicMouse ) + { + // get angle + final double s1Ang = Math.atan2( p1.getY() - vertex.getY(), p1.getX() - vertex.getX() ); + double extent = app.getAngle() <= 180 + ? app.getAngle() + : 360 - app.getAngle(); + if ( isSuplementary() && extent <= 180 ) + extent = 180 - extent; + else if ( isComplementary() && extent <= 90 ) + extent = 90 - extent; + if ( new Line2D.Double( vertex, p1 ).relativeCCW( logicMouse ) > 0 ) + extent = -extent; + final double ang = s1Ang + Math.toRadians( extent ); + + // return segment + Point2D p2; + if ( app.isUsingRuler() ) + p2 = pointRelativeToCenter( vertex, ang, app.getDistance() ); + else // mouse->side projection + { + p2 = pointRelativeToCenter( vertex, ang, 1. ); + final Point2D v = vector( vertex, p2 ), + w = vector( vertex, logicMouse ); + p2 = pointRelativeToCenter( vertex, ang, projection( v, w ) ); + } + + return new Line2D.Double( vertex, p2 ); + } +} diff --git a/src/jdrafting/gui/controller/mouse/ArcListener.java b/src/jdrafting/gui/controller/mouse/ArcListener.java new file mode 100644 index 0000000..a894fcf --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/ArcListener.java @@ -0,0 +1,250 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.geom.JDMath.adjustVectorToSize; +import static jdrafting.geom.JDMath.pointRelativeToCenter; +import static jdrafting.geom.JDMath.sumVectors; +import static jdrafting.geom.JDMath.vector; +import static jdrafting.geom.JDMath.vectorArg; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Arc2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; + +import javax.swing.undo.CompoundEdit; + +import jdrafting.geom.JDMath; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +/** + * Creates an arc using mouse control + */ +public class ArcListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "arc_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private Point2D center, start; + + public ArcListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_arc1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( center != null ) + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // set center + if ( center == null ) + { + center = logicMouse; + app.setStatusText( getLocaleText( "txt_arc2" ) ); + } + // set arc start point + else if ( start == null ) + { + start = getStart( logicMouse ); + app.setStatusText( getLocaleText( "txt_arc3" ) ); + } + // set arc + else + { + // get end arc point + final Point2D end = getEnd( logicMouse ); + + // create arc + final double radius = center.distance( end ), + startAng = Math.toDegrees( vectorArg( vector( center, start ) ) ), + extent = getArcExtent( end, logicMouse ); + final Arc2D arc = new Arc2D.Double( center.getX() - radius, center.getY() - radius, + 2 * radius, 2 * radius, + -startAng, -extent, + Arc2D.OPEN ); + final double flatness = JDMath.length( arc, null ) /*arc.getWidth()*/ / app.getFlatnessValue(); + + final String descHtml = String.format( "(%.1fº)", extent ); + + //////////////////////////// TRANSACTION //////////////////////////// + final CompoundEdit transaction = new JDCompoundEdit( + getLocaleText( "arc" ) + " " + descHtml ); + + // add shape to exercise + app.addShapeFromIterator( arc.getPathIterator( null, flatness ), "", + getLocaleText( "new_arc" ) + " " + descHtml, app.getColor(), + null, app.getStroke(), transaction ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + + canvas.repaint(); + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( center != null ) + { + // get transform + final AffineTransform transform = canvas.getTransform(); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // set style + g2.setStroke( new BasicStroke( 1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) ); + g2.setColor( Application.toolMainColor ); + + // draw center-start radius and circumference + if ( start == null ) + { + final Point2D tempStart = getStart( logicMouse ); + final double radius = center.distance( tempStart ); + g2.draw( transform.createTransformedShape( + new Line2D.Double( center, tempStart ) ) ); + g2.draw( transform.createTransformedShape( + new Ellipse2D.Double( center.getX() - radius, + center.getY() - radius, + 2 * radius, 2 * radius ) ) ); + } + // draw center-end radius, circumference and arc + else + { + // circumference + final double radius = center.distance( start ); + g2.draw( transform.createTransformedShape( + new Ellipse2D.Double( center.getX() - radius, + center.getY() - radius, + 2 * radius, 2 * radius ) ) ); + // radius + final Point2D end = getEnd( logicMouse ); + g2.draw( transform.createTransformedShape( new Line2D.Double( center, end ) ) ); + // arc + g2.setStroke( new BasicStroke( 5f, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_ROUND ) ); + final double startAng = Math.toDegrees( vectorArg( vector( center, start ) ) ), + extent = getArcExtent( end, logicMouse ); + final Arc2D arc = new Arc2D.Double( center.getX() - radius, center.getY() - radius, + 2 * radius, 2 * radius, + -startAng, -extent, Arc2D.OPEN ); + g2.draw( transform.createTransformedShape( arc ) ); + // draw angle info + final int mouseX = mouse().getX(), mouseY = mouse().getY(); + final String angInfo = String.format( "%.2f", extent ) + "º"; + g2.setFont( new Font( Font.SERIF, Font.BOLD, 16 ) ); + g2.setColor( new Color( 40, 40, 180 ) ); + g2.drawString( angInfo, mouseX + 21, mouseY - 9 ); + g2.setColor( Color.LIGHT_GRAY ); + g2.drawString( angInfo, mouseX + 20, mouseY - 10 ); + } + } + } + + + // --- HELPERS --- + + // check modifiers + private boolean isAngleFixed() { return mouse().isShiftDown(); } + private boolean isAngleConjugated() { return mouse().isControlDown(); } + private boolean isAngleInteger() { return mouse().isAltDown(); } + + /** + * Get start point of the arc + * @param logicMouse + * @return logic start point + */ + private Point2D getStart( Point2D logicMouse ) + { + return app.isUsingRuler() + ? sumVectors( center, adjustVectorToSize( + vector( center, logicMouse ), app.getDistance() ) ) + : logicMouse; + } + + /** + * Get end point of the arc + * @param logicMouse + * @return logic end point + */ + private Point2D getEnd( Point2D logicMouse ) + { + Point2D end; + final double radius = center.distance( start ); + + if ( isAngleFixed() ) // fixed angle + { + final Line2D startLine = new Line2D.Double( center, start ); + final double offAng = vectorArg( vector( center, start ) ), + fixAng = startLine.relativeCCW( logicMouse ) < 0 + ? app.getAngle() + : -app.getAngle(); + end = pointRelativeToCenter( center, offAng + Math.toRadians( fixAng ), radius ); + } + else // free angle + end = sumVectors( center, adjustVectorToSize( vector( center, logicMouse ), radius ) ); + + return end; + } + + /** + * Get extent angle of the arc + * @param end endpoint + * @param logicMouse + * @return the extent angle in degrees + */ + private double getArcExtent( Point2D end, Point2D logicMouse ) + { + final double startAng = vectorArg( vector( center, start ) ), + endAng = vectorArg( vector( center, end ) ); + + double extent = startAng < endAng + ? endAng - startAng + : 2 * Math.PI + ( endAng - startAng ); + extent = Math.toDegrees( extent ); + + if ( isAngleConjugated() ) + extent = extent - 360; + + if ( isAngleInteger() ) + extent = Math.rint( extent ); + + return extent; + } +} diff --git a/src/jdrafting/gui/controller/mouse/AreaSubstractListener.java b/src/jdrafting/gui/controller/mouse/AreaSubstractListener.java new file mode 100644 index 0000000..0b576d5 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/AreaSubstractListener.java @@ -0,0 +1,146 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.gui.JDUtils.elvis; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.Cursor; +import java.awt.Shape; +import java.awt.event.MouseEvent; +import java.awt.geom.Area; +import java.awt.geom.Point2D; +import java.util.List; + +import javax.swing.JOptionPane; + +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; +/** + * Select two shapes and substract the second to the first + */ +public class AreaSubstractListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "area_substract_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private JDraftingShape shape1, shape2; + + public AreaSubstractListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_area_substract1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + if ( jdshape != null ) + { + final List vertex = jdshape.getVertex(); + if ( !jdshape.isSegment( vertex ) && !jdshape.isPoint( vertex ) ) + canvas.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + } + else + canvas.setCursor( CURSOR ); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // select first shape + if ( shape1 == null ) + { + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + if ( jdshape != null ) + { + final List vertex = jdshape.getVertex(); + if ( !jdshape.isSegment( vertex ) && !jdshape.isPoint( vertex ) ) + { + shape1 = jdshape; + canvas.setCursor( CURSOR ); + app.setStatusText( getLocaleText( "txt_area_substract2" ) ); + } + } + } + // select second shape + else if ( shape2 == null ) + { + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + if ( jdshape != null ) + { + final List vertex = jdshape.getVertex(); + if ( !jdshape.isSegment( vertex ) && !jdshape.isPoint( vertex ) ) + { + shape2 = jdshape; + substract(); + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + return; + } + } + } + + //canvas.repaint(); + } + + // --- HELPERS --- + + private void substract() + { + // create substract area + final Area substractArea = new Area( shape1.getShape() ); + substractArea.subtract( new Area( shape2.getShape() ) ); + + // check for empty intersection + if ( substractArea.isEmpty() ) + { + JOptionPane.showMessageDialog( app, getLocaleText( "empty_substract_error" ), + getLocaleText( "area_substract" ), + JOptionPane.ERROR_MESSAGE ); + return; + } + + // final shape + final Shape result = JDUtils.removeUnnecessarySegments( substractArea.isSingular() + ? JDUtils.closeShapeWithLine( substractArea, true ) + : substractArea ); + + // shape enum for description + final String descHtml = String.format( + "(%s,%s)", + elvis( shape1.getName(), "?" ), elvis( shape2.getName(), "?" ), + Application.HTML_SHAPE_NAMES_COL ); + + //////////////////////////// TRANSACTION //////////////////////////// + final JDCompoundEdit transaction = new JDCompoundEdit( + getLocaleText( "area_substract" ) + " " + descHtml ); + + // add new shape to exercise + app.addShapeFromIterator( result.getPathIterator( null ), "", + getLocaleText( "new_substract" ) + " " + descHtml, app.getColor(), + null, app.getStroke(), transaction ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + // refresh + app.getCanvas().repaint(); + app.scrollList.repaint(); + } + +} diff --git a/src/jdrafting/gui/controller/mouse/ArrowListener.java b/src/jdrafting/gui/controller/mouse/ArrowListener.java new file mode 100644 index 0000000..69003e8 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/ArrowListener.java @@ -0,0 +1,152 @@ +package jdrafting.gui.controller.mouse; + +import static java.lang.Math.PI; +import static jdrafting.geom.JDMath.adjustVectorToSize; +import static jdrafting.geom.JDMath.pointRelativeToCenter; +import static jdrafting.geom.JDMath.sumVectors; +import static jdrafting.geom.JDMath.vector; +import static jdrafting.geom.JDMath.vectorArg; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; + +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; + +/** + * Create an arrow using mouse control + */ +public class ArrowListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "arrow_cursor.png" ); + private static final double ANGLE_INTERVAL = PI / 4.; + private CanvasPanel canvas; + private Application app; + + private Point2D start; + + public ArrowListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_arrow1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( start != null ) + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + if ( start == null ) + { + start = logicMouse; + app.setStatusText( getLocaleText( "txt_arrow2" ) ); + canvas.repaint(); + } + else + { + // add shape to exercise + app.addShapeFromIterator( getArrow( logicMouse ).getPathIterator( null ), "", + getLocaleText( "new_arrow" ), + app.getColor(), null, app.getStroke() ); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( start != null ) + { + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // draw segment + g2.setStroke( new BasicStroke( 1f ) ); + g2.setColor( Application.toolMainColor ); + + g2.draw( canvas.getTransform().createTransformedShape( getArrow( logicMouse ) ) ); + } + } + + + // --- HELPERS --- + + // check modifiers + private boolean isFixedAngle() { return mouse().isShiftDown(); } + private boolean isDouble() { return mouse().isControlDown(); } + + /** + * Get arrow in logic viewport + * @param logicMouse + * @return the segment + */ + private Path2D getArrow( Point2D logicMouse ) + { + Point2D end = logicMouse; + + // adjust to basic main angles + if ( isFixedAngle() ) + { + double ang = Math.atan2( end.getY() - start.getY(), + end.getX() - start.getX() ); + double newAng = ANGLE_INTERVAL * Math.round( ang / ANGLE_INTERVAL ); + end = pointRelativeToCenter( start, newAng, start.distance( end ) ); + } + + // fixed distance + if ( app.isUsingRuler() ) + end = sumVectors( start, adjustVectorToSize( vector( start, end ), + app.getDistance() ) ); + + final double dist = start.distance( end ), + afactor = 1.075, dfactor = Math.log( Math.E + dist / 1000 ) * 4., + ang = vectorArg( vector( start, end ) ); + final Path2D arrow = new Path2D.Double(); + + arrow.moveTo( start.getX(), start.getY() ); + arrow.lineTo( end.getX(), end.getY() ); + Point2D aux = pointRelativeToCenter( end, ang + PI * afactor, dist / dfactor ); + arrow.lineTo( aux.getX(), aux.getY() ); + arrow.moveTo( end.getX(), end.getY() ); + aux = pointRelativeToCenter( end, ang - PI * afactor, dist / dfactor ); + arrow.lineTo( aux.getX(), aux.getY() ); + if ( isDouble() ) + { + arrow.moveTo( start.getX(), start.getY() ); + aux = pointRelativeToCenter( start, ang + PI * ( afactor - 1 ), dist / dfactor ); + arrow.lineTo( aux.getX(), aux.getY() ); + arrow.moveTo( start.getX(), start.getY() ); + aux = pointRelativeToCenter( start, ang - PI * ( afactor - 1 ), dist / dfactor ); + arrow.lineTo( aux.getX(), aux.getY() ); + } + arrow.moveTo( end.getX(), end.getY() ); // (for extremes tool) + + return arrow; + } +} diff --git a/src/jdrafting/gui/controller/mouse/AxialSymmetryListener.java b/src/jdrafting/gui/controller/mouse/AxialSymmetryListener.java new file mode 100644 index 0000000..96444af --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/AxialSymmetryListener.java @@ -0,0 +1,183 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.util.HashSet; +import java.util.Set; + +import javax.swing.undo.AbstractUndoableEdit; + +import jdrafting.geom.JDMath; +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; + +/** + * Axial symmetry of selected shapes using mouse control + */ +public class AxialSymmetryListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "axial_symmetry_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private Point2D start; + + public AxialSymmetryListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_axial_sym1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( start != null ) + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // put start + if ( start == null ) + { + start = logicMouse; + app.setStatusText( getLocaleText( "txt_axial_sym2" ) ); + } + // modify shapes by axis symmetry + else + { + // vector director of the axis + final Point2D vector = JDMath.vector( start, logicMouse ); + + // modify shapes by transform + final Shape[] translatedShapes = getSymmetric( app.getSelectedShapes(), vector, start ); + int index = 0; + for ( JDraftingShape jdshape : app.getSelectedShapes() ) + jdshape.setShape( translatedShapes[index++] ); + + app.undoRedoSupport.postEdit( new EditSymmetry( + new HashSet<>( app.getSelectedShapes() ), vector ) ); + + app.scrollList.repaint(); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( start != null ) + { + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // get transform + final AffineTransform transform = canvas.getTransform(); + + // draw transformed shapes and axis + final Point2D vector = JDMath.vector( start, logicMouse ); + g2.setStroke( new BasicStroke( 1f ) ); + g2.setColor( Color.RED ); // axis + g2.draw( transform.createTransformedShape( + new Line2D.Double( start, logicMouse ) ) ); + g2.setColor( Application.toolMainColor ); // transformed shapes + for ( Shape symmetric : getSymmetric( app.getSelectedShapes(), vector, start ) ) + g2.draw( transform.createTransformedShape( symmetric ) ); + } + } + + + // --- HELPERS --- + + /** + * Get symmetryc shapes + * @param logicMouse mouse logic position + * @return a list of new shapes + */ + private Shape[] getSymmetric( Set selected, Point2D vector, Point2D anchor ) + { + /* Axial symmetry matrix + * [cos(2a),sin(2a),-tx(cos(2a)-1)-ty*sin(2a); + * sin(2a),-cos(2a),ty(cos(2a)+1)-tx*sin(2a); + * 0,0,1] + */ + final AffineTransform symmetry = AffineTransform.getTranslateInstance( + anchor.getX(), anchor.getY() ); + symmetry.rotate( vector.getX(), vector.getY() ); + symmetry.scale( 1., -1. ); + symmetry.rotate( vector.getX(), -vector.getY() ); + symmetry.translate( -anchor.getX(), -anchor.getY() ); + + return selected + .stream() + .map( jdshape -> + symmetry.createTransformedShape( jdshape.getShape() ) ) + .toArray( Shape[]::new ); + } + + /** + * UndoableEdit for undo/redo symmetry + */ + @SuppressWarnings("serial") + private class EditSymmetry extends AbstractUndoableEdit + { + private Set selected; + private Point2D vector; + + private EditSymmetry( Set selected, Point2D vector ) + { + this.selected = selected; + this.vector = vector; + } + + @Override + public void undo() { redo(); } + + @Override + public void redo() + { + final Shape[] symmetricShapes = getSymmetric( selected, vector, start ); + int index = 0; + for ( final JDraftingShape jdshape : selected ) + jdshape.setShape( symmetricShapes[index++] ); + } + + @Override + public boolean canRedo() { return true; } + @Override + public boolean canUndo() { return true; } + + @Override + public String getPresentationName() + { + return getLocaleText( "axial_sym" ) + " (" + selected.size() + " shapes)"; + } + } +} diff --git a/src/jdrafting/gui/controller/mouse/BisectrixListener.java b/src/jdrafting/gui/controller/mouse/BisectrixListener.java new file mode 100644 index 0000000..1e48b88 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/BisectrixListener.java @@ -0,0 +1,220 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.geom.JDMath.linesIntersection; +import static jdrafting.geom.JDMath.normal; +import static jdrafting.geom.JDMath.pointRelativeToCenter; +import static jdrafting.geom.JDMath.sumVectors; +import static jdrafting.geom.JDMath.vector; +import static jdrafting.geom.JDMath.vectorArg; +import static jdrafting.gui.JDUtils.elvis; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.util.List; + +import javax.swing.JOptionPane; +import javax.swing.undo.CompoundEdit; + +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +/** + * Create bisectrix segment using mouse control + */ +public class BisectrixListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "bisectrix_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private JDraftingShape seg1, seg2; + private Point2D bis1; + + public BisectrixListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_bisectrix1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( seg2 == null ) + { + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + if ( jdshape != null && jdshape.isSegment( jdshape.getVertex() ) ) + canvas.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + else + canvas.setCursor( CURSOR ); + } + if ( seg1 != null ) + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // select first segment + if ( seg1 == null ) + { + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + if ( jdshape != null && jdshape.isSegment( jdshape.getVertex() ) ) + { + seg1 = jdshape; + canvas.setCursor( CURSOR ); + app.setStatusText( getLocaleText( "txt_bisectrix2" ) ); + } + else + return; + } + // select second segment + else if ( seg2 == null ) + { + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + if ( jdshape != null && jdshape.isSegment( jdshape.getVertex() ) ) + { + seg2 = jdshape; + final Line2D bisectrix = getBisectrix( logicMouse ); + if ( bisectrix == null ) // parallel or coincident + { + // error message + JOptionPane.showMessageDialog( app, + getLocaleText( "bisectrix_dlg" ), + getLocaleText( "bisectrix_title" ), + JOptionPane.ERROR_MESSAGE ); + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + return; + } + + canvas.setCursor( CURSOR ); + app.setStatusText( getLocaleText( "txt_bisectrix3" ) ); + } + else + return; + } + // select first bisectrix point + else if ( bis1 == null ) + { + bis1 = getBisectrix( logicMouse ).getP2(); + app.setStatusText( getLocaleText( "txt_bisectrix4" ) ); + } + // create bisectrix + else + { + final String descHtml = String.format( + "{[%s],[%s]}", + Application.HTML_SHAPE_NAMES_COL, + elvis( seg1.getName(), "?" ), elvis( seg2.getName(), "?" ) ); + + //////////////////////////// TRANSACTION //////////////////////////// + final CompoundEdit transaction = new JDCompoundEdit( + getLocaleText( "bisectrix" ) + " " + descHtml ); + + app.addShapeFromIterator( getBisectrix( logicMouse ).getPathIterator( null ), "", + getLocaleText( "new_bisectrix" ) + " " + descHtml, + app.getColor(), null, app.getStroke(), transaction ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + canvas.repaint(); + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( seg1 == null ) return; + + final AffineTransform transform = canvas.getTransform(); + + g2.setColor( Application.toolMainColor ); + g2.setStroke( new BasicStroke( seg1.getStroke().getLineWidth() ) ); + // mark first segment + g2.draw( transform.createTransformedShape( seg1.getShape() ) ); + + if ( seg2 != null ) + { + // mark second segmen + g2.draw( transform.createTransformedShape( seg2.getShape() ) ); + + // get logic mouse position + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // get bisectrix + final Line2D bisectrix = getBisectrix( logicMouse ); + if ( bisectrix == null ) return; + // draw bisectrix + g2.setStroke( new BasicStroke( 1f ) ); + g2.draw( canvas.getTransform().createTransformedShape( bisectrix ) ); + } + } + + // --- HELPERS --- + + private Line2D getBisectrix( Point2D logicMouse ) + { + // get segment vertex and director vectors + final List vertexList1 = seg1.getVertex(), + vertexList2 = seg2.getVertex(); + final Point2D vertex1 = vertexList1.get( 0 ), + vertex2 = vertexList1.get( 1 ), + v1 = vector( vertex1, vertex2 ), + vertex3 = vertexList2.get( 0 ), + vertex4 = vertexList2.get( 1 ), + v2 = vector( vertex3, vertex4 ), + // get intersection point + intersection = linesIntersection( vertex1, vertex2, vertex3, vertex4 ); + + if ( intersection == null ) // parallel or coincident + return null; + + // calculate bisectrix angle + double ang = ( vectorArg( v1 ) + vectorArg( v2 ) ) / 2.; + final int region = new Line2D.Double( vertex1, vertex2 ) + .relativeCCW( logicMouse ) * new Line2D.Double( vertex3, vertex4 ) + .relativeCCW( logicMouse ); + if ( region > 0 ) // determinate bisectrix 1 or 2 (ortogonal) + ang += Math.PI / 2.; + + // get bisectrix extremes + final Point2D p1 = bis1 == null ? intersection : bis1, + p2 = bis1 == null || bis1.distance( intersection ) < 0.000001 + ? pointRelativeToCenter( p1, ang, 1. ) + : intersection, + normal = normal( vector( p1, p2 ) ), + q1 = logicMouse, + q2 = sumVectors( q1, normal ), + bis2 = linesIntersection( p1, p2, q1, q2 ); + + if ( bis2 == null ) return null; // parallel or coincident + + return new Line2D.Double( p1, bis2 ); + } +} diff --git a/src/jdrafting/gui/controller/mouse/BoundsListener.java b/src/jdrafting/gui/controller/mouse/BoundsListener.java new file mode 100644 index 0000000..8d6471e --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/BoundsListener.java @@ -0,0 +1,99 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.gui.JDUtils.elvis; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; + +import javax.swing.undo.CompoundEdit; + +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +/** + * Create a rectangle bounds for a shape by mouse control + */ +public class BoundsListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = + JDUtils.getCustomCursor( "bounds_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + public BoundsListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_bounds1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + canvas.setCursor( canvas.getShapeAtCanvasPoint( e.getPoint() ) == null + ? CURSOR + : new Cursor( Cursor.HAND_CURSOR ) ); + + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // get shape + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + + if ( jdshape != null ) + { + final String descHtml = String.format( "[%s]", + Application.HTML_SHAPE_NAMES_COL, + elvis( jdshape.getName(), "?" ) ); + + //////////////////////////// TRANSACTION //////////////////////////// + final CompoundEdit transaction = new JDCompoundEdit( + getLocaleText( "bounds" ) + " " + descHtml ); + + // add rectangle bounds to exercise + app.addShapeFromIterator( jdshape.getShape().getBounds2D().getPathIterator( null ), "", + getLocaleText( "new_bounds" ) + " " + descHtml, + app.getColor(), null, app.getStroke(), transaction ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + + @Override + public void paintTool( Graphics2D g2 ) + { + // get shape + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( mouse().getPoint() ); + if ( jdshape == null ) return; + + // set tool style + g2.setStroke( new BasicStroke( 1f ) ); + g2.setColor( Application.toolMainColor ); + + // draw bounds + g2.draw( canvas.getTransform().createTransformedShape( jdshape.getShape().getBounds2D() ) ); + } +} diff --git a/src/jdrafting/gui/controller/mouse/CapableArcListener.java b/src/jdrafting/gui/controller/mouse/CapableArcListener.java new file mode 100644 index 0000000..e24b302 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/CapableArcListener.java @@ -0,0 +1,167 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.geom.JDMath.linesIntersection; +import static jdrafting.geom.JDMath.midpoint; +import static jdrafting.geom.JDMath.normal; +import static jdrafting.geom.JDMath.pointRelativeToCenter; +import static jdrafting.geom.JDMath.sumVectors; +import static jdrafting.geom.JDMath.vector; +import static jdrafting.geom.JDMath.vectorArg; +import static jdrafting.gui.JDUtils.elvis; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Arc2D; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.util.List; + +import javax.swing.undo.CompoundEdit; + +import jdrafting.geom.JDMath; +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +/** + * Create capable arc of a segment using mouse control + */ +public class CapableArcListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "capable_arc_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private JDraftingShape segment; + + public CapableArcListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_cap1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + // Mark segments when the mouse is over them + if ( segment == null ) + { + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + canvas.setCursor( jdshape != null && jdshape.isSegment( jdshape.getVertex() ) + ? new Cursor( Cursor.HAND_CURSOR ) + : CURSOR ); + } + else + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // select segment + if ( segment == null ) + { + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + if ( jdshape != null && jdshape.isSegment( jdshape.getVertex() ) ) + { + segment = jdshape; + canvas.setCursor( CURSOR ); + app.setStatusText( getLocaleText( "txt_cap2" ) ); + canvas.repaint(); + } + } + // add one of the two capable arcs + else + { + final String descHtml = String.format( "[%s] (%.1fº)", + Application.HTML_SHAPE_NAMES_COL, + elvis( segment.getName(), "?" ), + app.getAngle() ); + // add capable arc to exercise + final Arc2D arc = getArc( logicMouse ); + final double flatness = JDMath.length( arc, null )/*arc.getWidth()*/ / app.getFlatnessValue(); + + //////////////////////////// TRANSACTION //////////////////////////// + final CompoundEdit transaction = new JDCompoundEdit( + getLocaleText( "capable_arc" ) + " " + descHtml ); + + app.addShapeFromIterator( arc.getPathIterator( null, flatness ), "", + getLocaleText( "new_capable_arc" ) + " " + descHtml, + app.getColor(), null, app.getStroke(), transaction ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( segment == null ) return; + + final AffineTransform transform = canvas.getTransform(); + + g2.setColor( Application.toolMainColor ); + g2.setStroke( new BasicStroke( 1f ) ); + + // draw capable arc + final Arc2D arc = getArc( CanvasPanel.getInverseTransform( transform ).transform( + mouse().getPoint(), null ) ); + g2.draw( transform.createTransformedShape( arc ) ); + } + + // --- HELPERS --- + + /** + * Get capable arc + * @param logicMouse + * @return the capable arc + */ + private Arc2D getArc( Point2D logicMouse ) + { + final List vertex = segment.getVertex(); + final Point2D p1 = vertex.get( 0 ), p2 = vertex.get( 1 ); + final int relative = new Line2D.Double( p1, p2 ).relativeCCW( logicMouse ); + final double ang = Math.toRadians( 90 + relative * app.getAngle() ); + final Point2D midpoint = midpoint( p1, p2 ), + normal = normal( vector( p1, p2 ) ), + aux = pointRelativeToCenter( p1, vectorArg( vector( p1, p2 ) ) + ang, 1. ); + Point2D center = linesIntersection( midpoint, sumVectors( midpoint, normal ), p1, aux ); + if ( center == null ) // 90º + center = midpoint; + final double radius = center.distance( p1 ); + + final double startAng = -vectorArg( relative < 0 + ? vector( center, p2 ) + : vector( center, p1 ) ); + + return new Arc2D.Double( center.getX() - radius, center.getY() - radius, + 2 * radius, 2 * radius, + Math.toDegrees( startAng ), + -( 360 - app.getAngle() * 2 ), + Arc2D.OPEN ); + } +} diff --git a/src/jdrafting/gui/controller/mouse/CentralSymmetryListener.java b/src/jdrafting/gui/controller/mouse/CentralSymmetryListener.java new file mode 100644 index 0000000..d479b43 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/CentralSymmetryListener.java @@ -0,0 +1,159 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.geom.JDMath.nearInt; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.util.HashSet; +import java.util.Set; + +import javax.swing.undo.AbstractUndoableEdit; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; + +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; + +/** + * Central symmety on selected shapes using mouse control + */ +public class CentralSymmetryListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "central_symmetry_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + public CentralSymmetryListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_central_sym1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // modify shapes by central symmetry + final Shape[] symmetricShapes = getSymmetric( app.getSelectedShapes(), logicMouse ); + int index = 0; + for ( JDraftingShape jdshape : app.getSelectedShapes() ) + jdshape.setShape( symmetricShapes[index++] ); + + app.undoRedoSupport.postEdit( new EditSymmetric( + new HashSet<>( app.getSelectedShapes() ), logicMouse ) ); + + app.scrollList.repaint(); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + + @Override + public void paintTool( Graphics2D g2 ) + { + // get transforms + final AffineTransform transform = canvas.getTransform(); + + final Point2D center = canvas.adjustToPoint( mouse().getPoint() ), + canCenter = transform.transform( center, null ); + + // draw symmetric shapes and center + g2.setStroke( new BasicStroke( 1f ) ); + g2.setColor( Color.RED ); + g2.fillOval( nearInt( canCenter.getX() - 4 ), + nearInt( canCenter.getY() - 4 ), 8, 8 ); + g2.setColor( Application.toolMainColor ); + for ( Shape sym : getSymmetric( app.getSelectedShapes(), center ) ) + g2.draw( transform.createTransformedShape( sym ) ); + } + + + // --- HELPERS --- + + /** + * Get symmetric shapes + * @param logicMouse mouse logic position + * @return a list of new shapes + */ + private Shape[] getSymmetric( Set selected, Point2D center ) + { + final AffineTransform symmetry = new AffineTransform(); + symmetry.translate( center.getX(), center.getY() ); + symmetry.scale( -1., -1. ); + symmetry.translate( -center.getX(), -center.getY() ); + + return selected + .stream() + .map( jdshape -> symmetry.createTransformedShape( jdshape.getShape() ) ) + .toArray( Shape[]::new ); + } + + /** + * UndoableEdit for undo/redo rotations + */ + @SuppressWarnings("serial") + private class EditSymmetric extends AbstractUndoableEdit + { + private Set selected; + private Point2D center; + + private EditSymmetric( Set selected, Point2D center ) + { + this.selected = selected; + this.center = center; + } + + @Override + public void undo() throws CannotUndoException + { + final Shape[] symmetricShapes = getSymmetric( selected, center ); + int index = 0; + for ( JDraftingShape jdshape : selected ) + jdshape.setShape( symmetricShapes[index++] ); + } + + @Override + public void redo() throws CannotRedoException + { + undo(); + } + + @Override + public boolean canRedo() { return true; } + @Override + public boolean canUndo() { return true; } + + @Override + public String getPresentationName() + { + return getLocaleText( "central_sym" ) + " (" + selected.size() + " shapes)"; + } + } +} diff --git a/src/jdrafting/gui/controller/mouse/CircumferenceListener.java b/src/jdrafting/gui/controller/mouse/CircumferenceListener.java new file mode 100644 index 0000000..b5069b6 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/CircumferenceListener.java @@ -0,0 +1,163 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; + +import jdrafting.geom.JDMath; +import jdrafting.geom.JDPoint; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +public class CircumferenceListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "circumference_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private Point2D center; + + public CircumferenceListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_circ1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( app.isUsingRuler() || center != null ) + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + if ( center == null && !app.isUsingRuler() ) + { + center = logicMouse; + app.setStatusText( getLocaleText( "txt_circ2" ) ); + } + else + { + // create circumference + final Ellipse2D circ = getCircumference( logicMouse ); + center = center == null ? logicMouse : center; + final double flatness = JDMath.length( circ, null )/*circ.getWidth()*/ / app.getFlatnessValue(); + + // add shape to exercise + if ( addCenter() ) + { + //////////////////////////// TRANSACTION //////////////////////////// + final JDCompoundEdit transaction = new JDCompoundEdit( + getLocaleText( "circumference" ) + " (& center)" ); + + // add circumference + app.addShapeFromIterator( JDUtils.openShape( circ ) + .getPathIterator( null, flatness ), "", + getLocaleText( "new_circumference" ), + app.getColor(), null, app.getStroke(), transaction ); + // add center + app.addShapeFromIterator( new JDPoint( center ).getPathIterator( null ), "", + getLocaleText( "new_circumference_center" ), + app.getPointColor(), null, app.getPointStroke(), + transaction ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + } + else // add circumference + app.addShapeFromIterator( JDUtils.openShape( circ ) + .getPathIterator( null, flatness ), "", + getLocaleText( "new_circumference" ), + app.getColor(), null, app.getStroke() ); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + + canvas.repaint(); + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( center != null || app.isUsingRuler() ) + { + final AffineTransform transform = canvas.getTransform(); + + // mouse position on logic viewport + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // set tool style + g2.setStroke( new BasicStroke( 1f ) ); + g2.setColor( Application.toolMainColor ); + + // draw circumference + g2.draw( transform.createTransformedShape( + getCircumference( logicMouse ) ) ); + // draw radius + if ( !app.isUsingRuler() ) + g2.draw( transform.createTransformedShape( + new Line2D.Double( center, + isDiameter() + ? JDMath.midpoint( center, logicMouse ) + : logicMouse ) ) ); + } + } + + + // --- HELPERS --- + + // check modifiers + private boolean isDiameter() { return mouse().isShiftDown(); } + private boolean addCenter() { return mouse().isControlDown(); } + + /** + * Get the circumference in the logic viewport + * @param logicMouse + * @return the circumference + */ + private Ellipse2D getCircumference( Point2D logicMouse ) + { + Point2D c; + double dist; + if ( app.isUsingRuler() ) + { + c = center == null ? logicMouse : center; + dist = app.getDistance(); + } + else + { + c = center; + dist = c.distance( logicMouse ); + } + if ( isDiameter() ) + dist /= 2.; + + return new Ellipse2D.Double( c.getX() - dist, c.getY() - dist, 2 * dist, 2 * dist ); + } +} diff --git a/src/jdrafting/gui/controller/mouse/DivisionPointsListener.java b/src/jdrafting/gui/controller/mouse/DivisionPointsListener.java new file mode 100644 index 0000000..5426996 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/DivisionPointsListener.java @@ -0,0 +1,192 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.geom.JDMath.adjustVectorToSize; +import static jdrafting.geom.JDMath.length; +import static jdrafting.geom.JDMath.sumVectors; +import static jdrafting.geom.JDMath.vector; +import static jdrafting.gui.JDUtils.elvis; +import static jdrafting.gui.JDUtils.getLargeIcon; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseEvent; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JOptionPane; +import javax.swing.JSpinner; +import javax.swing.SpinnerNumberModel; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import jdrafting.geom.JDPoint; +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +public class DivisionPointsListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "divisions_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private int divisions = 2; + + public DivisionPointsListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_div1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + JDraftingShape jdshape; + canvas.setCursor( ( jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ) ) == null + || jdshape.isPoint( jdshape.getVertex() ) + ? CURSOR + : new Cursor( Cursor.HAND_CURSOR ) ); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + + if ( jdshape != null && !jdshape.isPoint( jdshape.getVertex() ) ) + { + if ( !app.isUsingRuler() ) + { + // dialog for division number + final JSpinner spinDivisions = new JSpinner( + new SpinnerNumberModel( 2, 1, Integer.MAX_VALUE, 1 ) ); + spinDivisions.addChangeListener( new ChangeListener() { + @Override + public void stateChanged( ChangeEvent e ) + { + divisions = (int) ((JSpinner) e.getSource()).getValue(); + } + }); + + int option = JOptionPane.showOptionDialog( app, spinDivisions, + getLocaleText( "div_dlg" ), + JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE, + getLargeIcon( "divisions.png" ), null, null ); + if ( option == JOptionPane.CANCEL_OPTION + || option == JOptionPane.CLOSED_OPTION ) + { + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + return; + } + } + + // point style + final Color color = isPointStyle() + ? app.getColor() + : app.getPointColor(); + final BasicStroke stroke = isPointStyle() + ? app.getStroke() + : app.getPointStroke(); + + // shape length and interval length + final double length = length( jdshape.getShape(), null ), + interval = app.isUsingRuler() + ? app.getDistance() + : length / Math.nextUp( divisions ); + + // shape iterator vars + final PathIterator path = jdshape.getShape().getPathIterator( null ); + final double[] current = new double[6]; + path.currentSegment( current ); + double x = current[0], + y = current[1]; + + // point list + final List pointList = new ArrayList<>(); + // add start point to open shapes + if ( !jdshape.isClosed( jdshape.getVertex() ) ) + pointList.add( new Point2D.Double( x, y ) ); + + // iterate over shape + double dist; + double currentInterval = interval; + while ( !path.isDone() ) + { + int type = path.currentSegment( current ); + if ( type != PathIterator.SEG_MOVETO ) + { + dist = Point2D.distance( x, y, current[0], current[1] ); + if ( dist >= currentInterval ) + { + final Point2D xy = new Point2D.Double( x, y ), + v = vector( xy, new Point2D.Double( current[0], current[1] ) ), + point = sumVectors( xy, + adjustVectorToSize( v, currentInterval ) ); + pointList.add( point ); + x = point.getX(); + y = point.getY(); + currentInterval = interval; + continue; + } + else + currentInterval -= dist; + } + + x = current[0]; + y = current[1]; + path.next(); + } + + final String descHtml = String.format( + "[%s]", + Application.HTML_SHAPE_NAMES_COL, elvis( jdshape.getName(), "?" ) ); + + //////////////////////////// TRANSACTION //////////////////////////// + @SuppressWarnings("serial") + final JDCompoundEdit transaction = new JDCompoundEdit() { + public String getPresentationName() + { + return getLocaleText( "divisions" ) + " " + descHtml + + " (" + edits.size() + " points)"; + } + }; + + // add points to exercise + pointList + .stream() + .forEach( point -> app.addShapeFromIterator( + new JDPoint( point ).getPathIterator( null ), "", + getLocaleText( "new_div" ) + " " + descHtml, + color, null, stroke, transaction ) ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + // refresh + app.scrollList.repaint(); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + + private boolean isPointStyle() { return mouse().isShiftDown(); } +} diff --git a/src/jdrafting/gui/controller/mouse/EllipseListener.java b/src/jdrafting/gui/controller/mouse/EllipseListener.java new file mode 100644 index 0000000..4fe61a4 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/EllipseListener.java @@ -0,0 +1,211 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import jdrafting.geom.JDMath; +import jdrafting.geom.JDPoint; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +public class EllipseListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "ellipse_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private Point2D centerOrCorner; + + public EllipseListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_ellipse1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( centerOrCorner != null ) + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // set center or corner + if ( centerOrCorner == null ) + { + centerOrCorner = logicMouse; + app.setStatusText( getLocaleText( "txt_ellipse2" ) ); + canvas.repaint(); + } + // set ellipse + else + { + // create ellipse + final Ellipse2D ellipse = getEllipse( logicMouse ); + + final double flatness = JDMath.length( ellipse, null )/* ellipse.getWidth()*/ / app.getFlatnessValue(); + + // add shape to exercise + if ( addAxisFocus() ) + { + //////////////////////////// TRANSACTION //////////////////////////// + final JDCompoundEdit transaction = new JDCompoundEdit( + getLocaleText( "ellipse" ) + " (& axis & focus)" ); + + // add ellipse + final Rectangle2D bounds = ellipse.getBounds2D(); + app.addShapeFromIterator( JDUtils.openShape( ellipse ) + .getPathIterator( null, flatness ), "", + getLocaleText( "new_ellipse" ), + app.getColor(), null, app.getStroke(), transaction ); + // add axis + final Line2D axisH = new Line2D.Double( bounds.getMinX(), bounds.getCenterY(), + bounds.getMaxX(), bounds.getCenterY() ), + axisV = new Line2D.Double( bounds.getCenterX(), bounds.getMinY(), + bounds.getCenterX(), bounds.getMaxY() ); + app.addShapeFromIterator( axisH.getPathIterator( null ), "", + getLocaleText( "new_h_axis" ), + app.getColor(), null, app.getStroke(), transaction ); + app.addShapeFromIterator( axisV.getPathIterator( null ), "", + getLocaleText( "new_v_axis" ), + app.getColor(), null, app.getStroke(), transaction ); + // add focuses + final double w = ellipse.getWidth() / 2, h = ellipse.getHeight() / 2; + Point2D f1, f2; + if ( w > h ) + { + final double f = Math.sqrt( w * w - h * h ); + f1 = new Point2D.Double( bounds.getCenterX() - f, + bounds.getCenterY() ); + f2 = new Point2D.Double( bounds.getCenterX() + f, + bounds.getCenterY() ); + } + else + { + final double f = Math.sqrt( h * h - w * w ); + f1 = new Point2D.Double( bounds.getCenterX(), + bounds.getCenterY() + f ); + f2 = new Point2D.Double( bounds.getCenterX(), + bounds.getCenterY() - f ); + } + app.addShapeFromIterator( new JDPoint( f1 ).getPathIterator( null ), "", + getLocaleText( "new_focus" ) + " 1", app.getPointColor(), + null, app.getPointStroke(), transaction ); + app.addShapeFromIterator( new JDPoint( f2 ).getPathIterator( null ), "", + getLocaleText( "new_focus" ) + " 2", + app.getPointColor(), null, app.getPointStroke(), + transaction ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + } + else + app.addShapeFromIterator( JDUtils.openShape( ellipse ) + .getPathIterator( null, flatness ), "", + getLocaleText( "new_ellipse" ), + app.getColor(), null, app.getStroke() ); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( centerOrCorner != null ) + { + final AffineTransform transform = canvas.getTransform(); + + // mouse position on logic viewport + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // set tool style + g2.setStroke( new BasicStroke( 1f ) ); + g2.setColor( Application.toolMainColor ); + + // draw ellipse + final Ellipse2D ellipse = getEllipse( logicMouse ); + g2.draw( transform.createTransformedShape( ellipse ) ); + // draw axis + g2.draw( transform.createTransformedShape( + new Line2D.Double( ellipse.getMinX(), ellipse.getCenterY(), + ellipse.getMaxX(), ellipse.getCenterY() ) ) ); + g2.draw( transform.createTransformedShape( + new Line2D.Double( ellipse.getCenterX(), ellipse.getMinY(), + ellipse.getCenterX(), ellipse.getMaxY() ) ) ); + } + } + + // --- HELPERS --- + + // check modifiers + private boolean isNotCentered() { return mouse().isShiftDown(); } + private boolean addAxisFocus() { return mouse().isControlDown(); } + + /** + * Get the ellipse in the logic viewport + * @param logicMouse + * @return the ellipse + */ + private Ellipse2D getEllipse( Point2D logicMouse ) + { + Point2D corner; // upper-left corner + double w, h; // ellipse width, height + + // diagonal + Point2D diag = JDMath.vector( centerOrCorner, logicMouse ); + if ( app.isUsingRuler() ) + diag = new Point2D.Double( Math.signum( diag.getX() )*app.getDistance(), diag.getY() ); + // ellipse from corner + if ( isNotCentered() ) + { + w = diag.getX(); + h = diag.getY(); + corner = centerOrCorner; + } + // ellipse from center + else + { + w = 2 * diag.getX(); + h = 2 * diag.getY(); + corner = new Point2D.Double( centerOrCorner.getX() - diag.getX(), + centerOrCorner.getY() - diag.getY() ); + } + // adjust corner to mouse side + if ( w < 0. ) + corner = new Point2D.Double( corner.getX() + w, corner.getY() ); + if ( h < 0. ) + corner = new Point2D.Double( corner.getX(), corner.getY() + h ); + + return new Ellipse2D.Double( corner.getX(), corner.getY(), + Math.abs( w ), Math.abs( h ) ); + } +} diff --git a/src/jdrafting/gui/controller/mouse/ExtremesListener.java b/src/jdrafting/gui/controller/mouse/ExtremesListener.java new file mode 100644 index 0000000..5bebde6 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/ExtremesListener.java @@ -0,0 +1,114 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.gui.JDUtils.elvis; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.swing.JOptionPane; + +import jdrafting.geom.JDPoint; +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +public class ExtremesListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "extremes_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + public ExtremesListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_ext1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + canvas.setCursor( canvas.getShapeAtCanvasPoint( e.getPoint() ) == null + ? CURSOR + : new Cursor( Cursor.HAND_CURSOR ) ); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + + if ( jdshape != null ) + { + // point style + final Color color = isPointStyle(e) + ? app.getColor() + : app.getPointColor(); + final BasicStroke stroke = isPointStyle(e) + ? app.getStroke() + : app.getPointStroke(); + + // get shape vertex + List vertex = jdshape.getVertex(); + + // ERROR; closed shapes doesn't have extreme vertex + if ( jdshape.isClosed( vertex ) ) + JOptionPane.showMessageDialog( app, getLocaleText( "ext_dlg" ), + getLocaleText( "ext_title" ), + JOptionPane.ERROR_MESSAGE ); + else + { + // get first and last vertex + vertex = new ArrayList( Arrays.asList( + new Point2D[] { vertex.get( 0 ), + vertex.get( vertex.size() - 1 ) } ) ); + + final String descHtml = String.format( "[%s]", + Application.HTML_SHAPE_NAMES_COL, + elvis( jdshape.getName(), "?" ) ); + + //////////////////////////// TRANSACTION //////////////////////////// + final JDCompoundEdit transaction = new JDCompoundEdit( + getLocaleText( "extremes" ) + " " + descHtml + " (2 points)" ); + + // add extreme points to exercise + vertex + .stream() + .forEach( point -> app.addShapeFromIterator( + new JDPoint( point ).getPathIterator( null ), "", + getLocaleText( "new_extreme" ) + " " + descHtml, + color, null, stroke, transaction ) ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + // refresh + app.scrollList.repaint(); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + } + + private boolean isPointStyle( MouseEvent e ) { return e.isShiftDown(); } +} diff --git a/src/jdrafting/gui/controller/mouse/EyedropperListener.java b/src/jdrafting/gui/controller/mouse/EyedropperListener.java new file mode 100644 index 0000000..329fd26 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/EyedropperListener.java @@ -0,0 +1,128 @@ +package jdrafting.gui.controller.mouse; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; +import java.util.Arrays; +import java.util.HashSet; + +import jdrafting.geom.JDStrokes; +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; + +/** + * Capture line&fill/point/screen color and styles + * @version 0.1.12 + */ +public class EyedropperListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "eyedropper_cursor.png" ); + private Application app; + private CanvasPanel canvas; + + public EyedropperListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + app.setStatusText( JDUtils.getLocaleText( "txt_eyedropper" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( canvas.getShapeAtCanvasPoint( e.getPoint() ) == null ) + canvas.setCursor( CURSOR ); + else + canvas.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + Color color, fill = null; + JDraftingShape jdshape = null; + + // capture screen color + if ( isScreenCapture() ) // > color in screen (pure eyedropper) + { + final BufferedImage img = new BufferedImage( canvas.getWidth(), canvas.getHeight(), + BufferedImage.TYPE_INT_ARGB ); + final Graphics2D g2 = (Graphics2D) img.getGraphics(); + g2.setColor( canvas.getBackground() ); + g2.fillRect( 0, 0, img.getWidth(), img.getHeight() ); + + CanvasPanel.drawExercise( g2, canvas.getTransform(), app.getExercise(), new HashSet<>(), + true ); + color = new Color( img.getRGB( e.getX(), e.getY() ), true ); + } + // capure shape color + else + { + // get shape + jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + if ( jdshape == null ) return; + color = jdshape.getColor(); + fill = jdshape.getFill(); + } + + // capture shape style + if ( isPointStyle() ) // > point style + { + app.setPointColor( color ); + if ( jdshape != null ) + app.setPointStroke( JDStrokes.cloneStrokeStyle( + jdshape.getStroke().getLineWidth(), app.getPointStroke() ) ); + } + else // > line style + { + // set captured color and fill + app.setColor( color ); + app.setFill( fill ); + + if ( jdshape != null ) + { + // set captured stroke + app.setStroke( jdshape.getStroke() ); + // set captured style line into combobox + BasicStroke stroke = jdshape.isPoint( jdshape.getVertex() ) + ? JDStrokes.PLAIN_ROUND.getStroke() + : jdshape.getStroke(); + int size = app.comboLineStyle.getModel().getSize(); + for ( int index = 0; index < size; index++ ) + { + BasicStroke cStroke = app.comboLineStyle.getItemAt( index ); + cStroke = JDStrokes.getStroke( + cStroke, jdshape.getStroke().getLineWidth() ); + if ( stroke.getEndCap() == cStroke.getEndCap() + && stroke.getMiterLimit() == cStroke.getMiterLimit() + && stroke.getDashPhase() == cStroke.getDashPhase() + && stroke.getLineJoin() == cStroke.getLineJoin() + && Arrays.equals( stroke.getDashArray(), + cStroke.getDashArray() ) ) + { + app.comboLineStyle.setSelectedIndex( index ); + break; + } + } + } + } + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + + private boolean isPointStyle() { return mouse().isShiftDown(); } + private boolean isScreenCapture() { return mouse().isControlDown(); } +} diff --git a/src/jdrafting/gui/controller/mouse/FragmentListener.java b/src/jdrafting/gui/controller/mouse/FragmentListener.java new file mode 100644 index 0000000..d6a488b --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/FragmentListener.java @@ -0,0 +1,107 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.gui.JDUtils.elvis; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseEvent; +import java.awt.geom.Line2D; +import java.util.List; + +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +public class FragmentListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "hammer_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + public FragmentListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_fragment1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + canvas.setCursor( canvas.getShapeAtCanvasPoint( e.getPoint() ) == null + ? CURSOR + : new Cursor( Cursor.HAND_CURSOR ) ); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + + if ( jdshape != null ) + { + // get shape vertex + final List segments = jdshape.getSegments(); + + // fragment except points or segments + if ( !segments.isEmpty() ) + { + final String descHtml = String.format( "[%s]", + Application.HTML_SHAPE_NAMES_COL, + elvis( jdshape.getName(), "?" ) ); + + //////////////////////////// TRANSACTION //////////////////////////// + @SuppressWarnings("serial") + final JDCompoundEdit transaction = new JDCompoundEdit() { + public String getPresentationName() + { + return getLocaleText( "fragment" ) + " " + descHtml + + " (" + (edits.size() - 1) + " shapes)"; + } + }; + + // delete shape from exercise + int index = app.getExercise().removeShape( jdshape ); + app.shapeList.getModel().remove( index ); + transaction.addEdit( app.new EditRemoveShapeFromExercise( jdshape, index ) ); + + // create segments from sides + final BasicStroke stroke = e.isShiftDown() ? app.getStroke() : jdshape.getStroke(); + final Color color = e.isShiftDown() ? app.getColor() : jdshape.getColor(); + for ( final Line2D segment : segments ) + { + // create side and add it from [index] position + final JDraftingShape side = JDraftingShape.createFromIterator( + segment.getPathIterator( null ), "", + getLocaleText( "new_fragment" ) + " " + descHtml, + color, null, stroke ); + app.getExercise().addShape( index, side ); + app.shapeList.getModel().add( index, side ); + transaction.addEdit( app.new EditAddShapeToExercise( side, index++ ) ); + } + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + app.scrollList.repaint(); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + } +} diff --git a/src/jdrafting/gui/controller/mouse/FreeHandListener.java b/src/jdrafting/gui/controller/mouse/FreeHandListener.java new file mode 100644 index 0000000..18778d2 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/FreeHandListener.java @@ -0,0 +1,107 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; + +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; + +public class FreeHandListener extends AbstractCanvasMouseListener +{ + private static final Cursor UP_CURSOR = JDUtils.getCustomCursor( "free_hand_up_cursor.png" ); + private static final Cursor DOWN_CURSOR = JDUtils.getCustomCursor("free_hand_down_cursor.png"); + private CanvasPanel canvas; + private Application app; + + private Path2D path = new Path2D.Double(); + + // drag parameters + private boolean dragging = false; + + public FreeHandListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( UP_CURSOR ); + + app.setStatusText( JDUtils.getLocaleText( "txt_free1" ) ); + } + + @Override + public void mousePressed( MouseEvent e ) + { + super.mousePressed( e ); + + canvas.setCursor( DOWN_CURSOR ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + if ( dragging ) + path.lineTo( logicMouse.getX(), logicMouse.getY() ); + else + path.moveTo( logicMouse.getX(), logicMouse.getY() ); + + canvas.repaint(); + } + + @Override + public void mouseDragged( MouseEvent e ) + { + super.mouseDragged( e ); + + dragging = true; + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + path.lineTo( logicMouse.getX(), logicMouse.getY() ); + + // refresh canvas + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + if ( e.getClickCount() == 2 ) + { + // add free polyline to exercise + app.addShapeFromIterator( path.getPathIterator( null ), "", + getLocaleText( "new_free_hand" ), + app.getColor(), null, app.getStroke() ); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + return; + } + + dragging = false; + canvas.setCursor( UP_CURSOR ); + + canvas.repaint(); + } + + @Override + public void paintTool( Graphics2D g2 ) + { + // set tool style + g2.setStroke( new BasicStroke( 1f ) ); + g2.setColor( Application.toolMainColor ); + + // draw polyline + g2.draw( canvas.getTransform().createTransformedShape( path ) ); + } +} diff --git a/src/jdrafting/gui/controller/mouse/HandListener.java b/src/jdrafting/gui/controller/mouse/HandListener.java new file mode 100644 index 0000000..e91534f --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/HandListener.java @@ -0,0 +1,253 @@ +package jdrafting.gui.controller.mouse; + +import java.awt.BasicStroke; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +import javax.swing.undo.AbstractUndoableEdit; + +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.EditShapeDialog; +import jdrafting.gui.JDUtils; + +public class HandListener extends AbstractCanvasMouseListener +{ + private static final Cursor DRAG_CURSOR = JDUtils.getCustomCursor( "dragging_cursor.png" ); + private static final Cursor HAND_CURSOR = JDUtils.getCustomCursor( "hand_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private JDraftingShape textShape; + + // drag parameters + private int newMouseX, newMouseY; + public boolean moving = false; + private int button = -1; // (mousedragged doesn't keep pressed button) + + public HandListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( HAND_CURSOR ); + + app.setStatusText( JDUtils.getLocaleText( "txt_hand" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + // cursor over shape or over canvas background + canvas.setCursor( canvas.getShapeAtCanvasPoint( e.getPoint() ) != null + ? new Cursor( Cursor.HAND_CURSOR ) + : HAND_CURSOR ); + } + + @Override + public void mousePressed( MouseEvent e ) + { + super.mousePressed( e ); + + newMouseX = e.getX(); + newMouseY = e.getY(); + button = e.getButton(); + + // check for text label selected + if ( app.isVisibleNames() ) + { + JDraftingShape jdshape = + canvas.getShapeWithNameAtCanvasPoint( e.getPoint() ); + if ( jdshape != null ) + { + textShape = jdshape; + return; + } + } + + // check for select a shape + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + if ( jdshape != null ) + { + // edit shape dialog + if ( e.getClickCount() == 2 ) + { + new EditShapeDialog( app, jdshape ).setVisible( true ); + return; + } + + app.shapeList.getSelectionModel().clearSelection(); + + // select single shape + if ( app.getSelectedShapes().isEmpty() ) + app.getSelectedShapes().add( jdshape ); + // select/deselect shape + else if ( e.isShiftDown() ) + { + // select + if ( !app.getSelectedShapes().contains( jdshape ) ) + app.getSelectedShapes().add( jdshape ); + // deselect + else + app.getSelectedShapes().remove( jdshape ); + } + // new selection with this shape + else if ( !app.getSelectedShapes().contains( jdshape ) ) + { + app.setSelectedShapes( new HashSet<>() ); + app.getSelectedShapes().add( jdshape ); + } + } + else + // remove selection when pressing canvas background + app.setSelectedShapes( new HashSet<>() ); + + canvas.repaint(); + } + + @Override + public void mouseDragged( MouseEvent e ) + { + super.mouseDragged( e ); + + // update mouse position + final int mouseX = newMouseX, mouseY = newMouseY; + newMouseX = e.getX(); + newMouseY = e.getY(); + + // dragging cursor + canvas.setCursor( DRAG_CURSOR ); + + // canvas delta + final Point2D canvasDelta = new Point2D.Double( newMouseX - mouseX, newMouseY - mouseY ); + + // move text relative to shape + if ( textShape != null ) + { + final Rectangle2D bounds = CanvasPanel.getShapeCanvasBounds( + textShape, canvas.getTransform() ); + + final Point2D namePosition = CanvasPanel.getNamePosition( bounds, textShape ); + + final double textX = namePosition.getX() + canvasDelta.getX(), + textY = namePosition.getY() + canvasDelta.getY(); + + textShape.setTextPosition( new Point2D.Double( + ( textX - bounds.getCenterX() ) / bounds.getWidth(), + -( textY - bounds.getCenterY() ) / bounds.getHeight() ) ); + + canvas.repaint(); + + return; + } + + // logic delta + final Point2D delta = canvas.getInverseTransform().deltaTransform( canvasDelta, null ); + + // drag with hand + if ( app.getSelectedShapes().isEmpty() ) + canvas.getViewport().move( -delta.getX(), -delta.getY() ); + // move shape with second button + else if ( button == MouseEvent.BUTTON3 ) + { + if ( !moving ) + app.undoRedoSupport.postEdit( new EditMoveShapes( app.getSelectedShapes() ) ); + moving = true; + + app.getSelectedShapes() + .stream() + .forEach( jdshape -> jdshape.move( delta.getX(), delta.getY() ) ); + } + + // refresh canvas + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + textShape = null; + moving = false; + canvas.setCursor( HAND_CURSOR ); + + // refresh + canvas.repaint(); + app.scrollList.repaint(); + } + + @Override + public void paintTool( Graphics2D g2 ) + { + // draw segment from shape bounds center to name text + if ( textShape != null ) + { + final AffineTransform transform = canvas.getTransform(); + final Rectangle2D bounds = CanvasPanel.getShapeCanvasBounds( textShape, transform ); + final Point2D namePosition = CanvasPanel.getNamePosition( bounds, textShape ); + + g2.setColor( Application.toolMainColor ); + g2.setStroke( textShape.getStroke() ); + g2.draw( transform.createTransformedShape( textShape.getShape() ) ); + g2.setStroke( new BasicStroke( 1f ) ); + g2.draw( new Line2D.Double( + namePosition.getX(), namePosition.getY(), + bounds.getCenterX(), bounds.getCenterY() ) ); + } + } + + /** + * Undo/Redo class for move shapes action + */ + @SuppressWarnings("serial") + private class EditMoveShapes extends AbstractUndoableEdit + { + final private Map oldValuesMap = new HashMap<>(), + newValuesMap = new HashMap<>(); + + EditMoveShapes( Iterable shapes ) + { + for ( JDraftingShape jdshape : shapes ) + oldValuesMap.put( jdshape, jdshape.getShape() ); + } + + @Override + public void undo() + { + oldValuesMap.forEach( (key,value) -> newValuesMap.put( key, key.getShape() ) ); + oldValuesMap.forEach( (key,value) -> key.setShape( value ) ); + } + + @Override + public void redo() + { + newValuesMap.forEach( (key,value) -> key.setShape( value ) ); + } + + @Override + public boolean canRedo() { return true; } + @Override + public boolean canUndo() { return true; } + + @Override + public String getPresentationName() + { + return "Move (" + oldValuesMap.size() + " shapes)"; + } + } +} diff --git a/src/jdrafting/gui/controller/mouse/HomothetyListener.java b/src/jdrafting/gui/controller/mouse/HomothetyListener.java new file mode 100644 index 0000000..da7aa53 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/HomothetyListener.java @@ -0,0 +1,218 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.geom.JDMath.nearInt; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.text.DecimalFormat; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.swing.undo.AbstractUndoableEdit; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; + +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; + +/** + * Apply homothety to selected shapes using mouse control + */ +public class HomothetyListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "homothety_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private double factor; + + public HomothetyListener( CanvasPanel canvas, double factor ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + this.factor = factor; + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_homothety1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + final Shape[] transformedShapes = + getTransformed( app.getSelectedShapes(), logicMouse, getFactor() ); + int index = 0; + for ( JDraftingShape jdshape : app.getSelectedShapes() ) + jdshape.setShape( transformedShapes[index++] ); + + app.undoRedoSupport.postEdit( new EditHomothety( new HashSet<>( + app.getSelectedShapes() ), logicMouse, getFactor() ) ); + + app.scrollList.repaint(); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + + @Override + public void paintTool( Graphics2D g2 ) + { + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // get transform + final AffineTransform transform = canvas.getTransform(); + + // draw shapes transformed and center + g2.setStroke( new BasicStroke( 1f ) ); // center + g2.setColor( Color.RED ); + Point2D canCenter = transform.transform( logicMouse, null ); + g2.fillOval( nearInt( canCenter.getX() - 4 ), + nearInt( canCenter.getY() - 4 ), 8, 8 ); + + g2.setStroke( new BasicStroke( 1f ) ); // aux + g2.setColor( new Color( 32, 128, 64, 64 ) ); + double factor = getFactor(); + Shape[] transformed = getTransformed( app.getSelectedShapes(), + logicMouse, factor ); + int index = 0; + for ( final JDraftingShape jdshape : app.getSelectedShapes() ) + { + final JDraftingShape temp = new JDraftingShape( transformed[index++], null, null, null ); + final List oriVertex = jdshape.getVertex(), + transVertex = temp.getVertex(); + Point2D p1, p2; + for ( int i = 0; i < oriVertex.size(); i++ ) + { + p1 = factor > 1 ? transVertex.get( i ) : oriVertex.get( i ); + p2 = factor < 0 ? transVertex.get( i ) : logicMouse; + g2.draw( transform.createTransformedShape( + new Line2D.Double( p1, p2 ) ) ); + } + } + + g2.setStroke( new BasicStroke( 2f ) ); // transformed shapes + g2.setColor( Application.toolMainColor ); + for ( Shape tshape : transformed ) + g2.draw( transform.createTransformedShape( tshape ) ); + + // factor info + final int mouseX = mouse().getX(), mouseY = mouse().getY(); + final String factorInfo = "x" + new DecimalFormat( "#.########" ).format( getFactor() ); + g2.setFont( new Font( Font.SERIF, Font.BOLD, 16 ) ); + g2.setColor( new Color( 40, 40, 180 ) ); + g2.drawString( factorInfo, mouseX + 21, mouseY - 9 ); + g2.setColor( Color.LIGHT_GRAY ); + g2.drawString( factorInfo, mouseX + 20, mouseY - 10 ); + } + + + // --- HELPERS --- + + // check modifiers + private boolean isNegateFactor() { return mouse().isShiftDown(); } + private boolean isInverseFactor() { return mouse().isControlDown(); } + + private double getFactor() + { + double factor = this.factor; + if ( isNegateFactor() ) + factor = -factor; + if ( isInverseFactor() ) + factor = 1. / factor; + + return factor; + } + + /** + * Get transformed shapes + * @param logicMouse mouse logic position + * @return a list of new shapes + */ + private Shape[] getTransformed( Set selected, Point2D center, double factor ) + { + final AffineTransform homothecy = new AffineTransform(); + homothecy.translate( center.getX(), center.getY() ); + homothecy.scale( factor, factor ); + homothecy.translate( -center.getX(), -center.getY() ); + + return selected.stream() + .map( jdshape -> homothecy.createTransformedShape( jdshape.getShape() ) ) + .toArray( Shape[]::new ); + } + + /** + * UndoableEdit for undo/redo homothety + */ + @SuppressWarnings("serial") + private class EditHomothety extends AbstractUndoableEdit + { + private Set selected; + private double factor; + private Point2D center; + + private EditHomothety( Set selected, Point2D center, double factor ) + { + this.selected = selected; + this.center = center; + this.factor = factor; + } + + @Override + public void undo() throws CannotUndoException + { + final Shape[] transformedShapes = getTransformed( selected, center, 1./factor ); + int index = 0; + for ( JDraftingShape jdshape : selected ) + jdshape.setShape( transformedShapes[index++] ); + } + + @Override + public void redo() throws CannotRedoException + { + final Shape[] transformedShapes = getTransformed( selected, center, factor ); + int index = 0; + for ( final JDraftingShape jdshape : selected ) + jdshape.setShape( transformedShapes[index++] ); + } + + @Override + public boolean canRedo() { return true; } + @Override + public boolean canUndo() { return true; } + + @Override + public String getPresentationName() + { + return getLocaleText( "homothety" ) + " (" + selected.size() + " shapes)"; + } + } +} diff --git a/src/jdrafting/gui/controller/mouse/HyperbolaListener.java b/src/jdrafting/gui/controller/mouse/HyperbolaListener.java new file mode 100644 index 0000000..5075f49 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/HyperbolaListener.java @@ -0,0 +1,281 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.HashMap; +import java.util.Map; + +import jdrafting.geom.JDPoint; +import jdrafting.geom.JDStrokes; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +/** + * Create hyperbola from bounds by mouse control + */ +public class HyperbolaListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "hyperbola_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private Point2D start, end; + + public HyperbolaListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_hyperbola1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( start != null ) + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // put first corner of bounds + if ( start == null ) + { + start = logicMouse; + app.setStatusText( getLocaleText( "txt_hyperbola2" ) ); + canvas.repaint(); + } + // finish hyperbola bounds + else if ( end == null ) + { + end = logicMouse; + app.setStatusText( getLocaleText( "txt_hyperbola3" ) ); + canvas.repaint(); + } + // set 'a' value (semiaxis) and finish + else + { + //////////////////////////// TRANSACTION //////////////////////////// + final JDCompoundEdit transaction = new JDCompoundEdit( + getLocaleText( "hyperbola" ) + + (addMore() ? " (& auxiliar shapes)" : "") ); + + final Map map = getHyperbola( logicMouse ); + // add hyperbola branches + app.addShapeFromIterator( + ((Path2D) map.get( "branch1" )).getPathIterator( null ), + "", getLocaleText( "new_hype_branch" ) + " 1", app.getColor(), null, + app.getStroke(), transaction ); + app.addShapeFromIterator( + ((Path2D) map.get( "branch2" )).getPathIterator( null ), + "", getLocaleText( "new_hype_branch" ) + " 2", app.getColor(), null, + app.getStroke(), transaction ); + // add more hyperbola elements + if ( addMore() ) + { + app.addShapeFromIterator( ( (Rectangle2D) map.get( "bounds" ) ) + .getPathIterator( null ), + "", getLocaleText( "new_hype_bounds" ), app.getColor(), null, + app.getStroke(), transaction ); + app.addShapeFromIterator( ( (Line2D) map.get( "main_axis" ) ) + .getPathIterator( null ), + "", getLocaleText( "new_main_axis" ), app.getColor(), null, + app.getStroke(), transaction ); + app.addShapeFromIterator( ( (Line2D) map.get( "img_axis" ) ) + .getPathIterator( null ), + "", getLocaleText( "new_img_axis" ), app.getColor(), null, + app.getStroke(), transaction ); + app.addShapeFromIterator( + ( new JDPoint( (Point2D) map.get( "center" ) ) ) + .getPathIterator( null ), + "", getLocaleText( "new_hype_center" ), + app.getPointColor(), null, app.getPointStroke(), transaction ); + app.addShapeFromIterator( + ( new JDPoint( (Point2D) map.get( "vertex1" ) ) ) + .getPathIterator( null ), + "", getLocaleText( "new_hype_vertex" ), + app.getPointColor(), null, app.getPointStroke(), transaction ); + app.addShapeFromIterator( + ( new JDPoint( (Point2D) map.get( "vertex2" ) ) ) + .getPathIterator( null ), + "", getLocaleText( "new_hype_vertex" ), + app.getPointColor(), null, app.getPointStroke(), transaction ); + app.addShapeFromIterator( + ( new JDPoint( (Point2D) map.get( "focus1" ) ) ) + .getPathIterator( null ), + "", getLocaleText( "new_focus" ), app.getPointColor(), null, + app.getPointStroke(), transaction ); + app.addShapeFromIterator( + ( new JDPoint( (Point2D) map.get( "focus2" ) ) ) + .getPathIterator( null ), + "", getLocaleText( "new_focus" ), app.getPointColor(), null, + app.getPointStroke(), transaction ); + } + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( start != null ) + { + final AffineTransform transform = canvas.getTransform(); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // set style + g2.setColor( Application.toolMainColor ); + + // get hyperbola data + final Map map = getHyperbola( logicMouse ); + + // draw hiperbola branches + g2.setStroke( new BasicStroke( 1f ) ); + g2.draw( transform.createTransformedShape( (Path2D) map.get( "branch1" ) ) ); + g2.draw( transform.createTransformedShape( (Path2D) map.get( "branch2" ) ) ); + // draw bounds + g2.setStroke( JDStrokes.getStroke( JDStrokes.DASHED.getStroke(), 1f ) ); + g2.draw( transform.createTransformedShape( (Rectangle2D) map.get( "bounds" ) ) ); + // draw main axis + g2.draw( transform.createTransformedShape( (Line2D) map.get( "main_axis" ) ) ); + g2.draw( transform.createTransformedShape( (Line2D) map.get( "img_axis" ) ) ); + // draw center + g2.setStroke( new BasicStroke( 4f ) ); + g2.draw( transform.createTransformedShape( + new JDPoint( (Point2D) map.get( "center" ) ) ) ); + // draw vertexes + g2.draw( transform.createTransformedShape( + new JDPoint( (Point2D) map.get( "vertex1" ) ) ) ); + g2.draw( transform.createTransformedShape( + new JDPoint( (Point2D) map.get( "vertex2" ) ) ) ); + // draw focuses + g2.draw( transform.createTransformedShape( + new JDPoint( (Point2D) map.get( "focus1" ) ) ) ); + g2.draw( transform.createTransformedShape( + new JDPoint( (Point2D) map.get( "focus2" ) ) ) ); + } + } + + // --- HELPERS --- + + // check modifiers + private boolean addMore() { return mouse().isControlDown(); } + + /** + * Gets the hyperbola + * @param logicMouse + * @return the hyperbola in the logic viewport + */ + private Map getHyperbola( Point2D logicMouse ) + { + Point2D endPoint = end == null ? logicMouse : end; + + // calculate coords + final double x = app.isUsingRuler() + ? start.getX() > endPoint.getX() + ? start.getX() - app.getDistance() + : start.getX() + app.getDistance() + : endPoint.getX(), + minX = Math.min( start.getX(), x ), + maxX = Math.max( start.getX(), x ), + minY = Math.min( start.getY(), endPoint.getY() ), + maxY = Math.max( start.getY(), endPoint.getY() ); + + // create hyperbola rectangle bounds + final Rectangle2D bounds = new Rectangle2D.Double( minX, minY, maxX - minX, maxY - minY ); + + // create hyperbola + final Path2D branch1 = new Path2D.Double(), branch2 = new Path2D.Double(); + + double w = bounds.getWidth(), h = bounds.getHeight(); + double a = end == null // main semi-axis + ? h / 4 + : Math.abs( logicMouse.getY() - bounds.getCenterY() ); + double b = w * a / Math.sqrt( h * h - 4 * a * a ); // img semi-axis + double c = Math.sqrt( a * a + b * b ); // focal semi-distance + int n = (int) Math.sqrt( app.getFlatnessValue() ); + + for ( int i = 0; i < n; i++ ) + { + double xh = -w / 2. + w / ( n - 1 ) * i; + // y=a*sqrt(x^2/b^2+1) (vertical branches) + double yh = a * Math.sqrt( xh * xh / ( b * b ) + 1 ); + if ( i != 0 ) + { + branch1.lineTo( xh, yh ); + branch2.lineTo( xh, -yh ); + } + else + { + branch1.moveTo( xh, yh ); + branch2.moveTo( xh, -yh ); + } + } + + // center + final Point2D center = new Point2D.Double( bounds.getCenterX(), bounds.getCenterY() ), + // vertexes + vertex1 = new Point2D.Double( bounds.getCenterX(), bounds.getCenterY() + a ), + vertex2 = new Point2D.Double( bounds.getCenterX(), bounds.getCenterY() - a ), + // focuses + focus1 = new Point2D.Double( center.getX(), center.getY() + c ), + focus2 = new Point2D.Double( center.getX(), center.getY() - c ); + // axis + final Line2D mainAxis = new Line2D.Double( vertex1, vertex2 ), + imgAxis = new Line2D.Double( + new Point2D.Double( bounds.getCenterX() - b, bounds.getCenterY() ), + new Point2D.Double( bounds.getCenterX() + b, bounds.getCenterY() ) ); + + // translate hyperbola to center + final AffineTransform translation = AffineTransform.getTranslateInstance( + bounds.getCenterX(), bounds.getCenterY() ); + branch1.transform( translation ); + branch2.transform( translation ); + + // return results + final Map map = new HashMap<>(); + map.put( "branch1", branch1 ); + map.put( "branch2", branch2 ); + map.put( "center", center ); + map.put( "vertex1", vertex1 ); + map.put( "vertex2", vertex2 ); + map.put( "focus1", focus1 ); + map.put( "focus2", focus2 ); + map.put( "bounds", bounds ); + map.put( "main_axis", mainAxis ); + map.put( "img_axis", imgAxis ); + + return map; + } +} diff --git a/src/jdrafting/gui/controller/mouse/MathFunctionListener.java b/src/jdrafting/gui/controller/mouse/MathFunctionListener.java new file mode 100644 index 0000000..93dcdb7 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/MathFunctionListener.java @@ -0,0 +1,191 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; +import java.util.Map; + +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; +import jme.Expresion; +import jme.abstractas.Numero; +import jme.abstractas.Terminal; +import jme.excepciones.ExpresionException; +import jme.terminales.Vector; +import jme.terminales.VectorEvaluado; + +/** + * Create jme function from bounds by mouse control + */ +public class MathFunctionListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "jme_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private Point2D start; + private Point2D[] values; + private Map jmeParams; + + public MathFunctionListener( CanvasPanel canvas, Map jmeParams ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + this.jmeParams = jmeParams; + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_jme1" ) ); + + // calculate graph + final Expresion exp = (Expresion) jmeParams.get( "expression" ); + final int n = (Integer) jmeParams.get( "intervals" ) + 1; // number of points + final double xa = (Double) jmeParams.get( "xmin" ), + xb = (Double) jmeParams.get( "xmax" ); + + values = new Point2D[n]; + for ( int i = 0; i < n; i++ ) + { + final double x = xa + i * ( xb - xa ) / ( n - 1 ); + exp.setVariable( "x", x ) + .setVariable( "t", x ); + try + { + final Terminal result = exp.evaluar(); + if ( result.esNumero() && !result.esComplejo() ) + values[i] = new Point2D.Double( x, ( (Numero) result ).doble() ); + else if ( result.esVector() + && ( (Vector) result ).dimension() == 2 ) + { + final VectorEvaluado v = (VectorEvaluado) result; + final Terminal t1 = v.getComponente( 0 ), + t2 = v.getComponente( 1 ); + if ( !t1.esNumero() || !t2.esNumero() + || t1.esComplejo() || t2.esComplejo() ) + values[i] = null; + else + values[i] = new Point2D.Double( ((Numero) t1).doble(), + ((Numero) t2).doble() ); + } + else + values[i] = null; + } + catch ( ExpresionException e ) + { + values[i] = null; + } + } + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( start != null ) + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // put first point + if ( start == null ) + { + start = logicMouse; + app.setStatusText( getLocaleText( "txt_jme2" ) ); + canvas.repaint(); + } + // finish bounds + else + { + // add function to exercise + app.addShapeFromIterator( getGraph( logicMouse ).getPathIterator( null ), + "", jmeParams.get( "expression" ).toString(), + app.getColor(), null, app.getStroke() ); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( start != null ) + { + final AffineTransform transform = canvas.getTransform(); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // set style + g2.setStroke( new BasicStroke( 1f ) ); + g2.setColor( Application.toolMainColor ); + + // draw graph + g2.draw( transform.createTransformedShape( getGraph( logicMouse ) ) ); + } + } + + // --- HELPERS --- + + /** + * Get the graph + * @param logicMouse + * @return the graph in the logic viewport + */ + private Path2D getGraph( Point2D logicMouse ) + { + // calculate coords + final double px = app.isUsingRuler() + ? start.getX() > logicMouse.getX() + ? start.getX() - app.getDistance() + : start.getX() + app.getDistance() + : logicMouse.getX(), + minX = Math.min( start.getX(), px ), + maxX = Math.max( start.getX(), px ); + + // generate graph + final Path2D graph = new Path2D.Double(); + final int n = values.length; + final double xa = (Double) jmeParams.get( "xmin" ), + xb = (Double) jmeParams.get( "xmax" ); + + for ( int i = 0; i < n; i++ ) + if ( values[i] != null ) + { + final double x = minX + ( values[i].getX() - xa ) * ( maxX - minX ) / ( xb - xa ), + y = values[i].getY() * ( maxX - minX ) / ( xb - xa ); + final boolean cond = + // line except for first point + i != 0 + // and except points without previous image point + && values[ i - 1 ] != null + // and except big steps in the graph + && Math.abs( values[ i - 1 ].getY() * ( maxX - minX ) + / ( xb - xa ) - y ) < ( maxX - minX ); + if ( cond ) + graph.lineTo( x, y ); + else + graph.moveTo( x, y ); + } + + return (Path2D) AffineTransform.getTranslateInstance( 0, start.getY() ) + .createTransformedShape( graph ); + } +} diff --git a/src/jdrafting/gui/controller/mouse/MediatrixListener.java b/src/jdrafting/gui/controller/mouse/MediatrixListener.java new file mode 100644 index 0000000..0f635fe --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/MediatrixListener.java @@ -0,0 +1,160 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.geom.JDMath.linesIntersection; +import static jdrafting.geom.JDMath.midpoint; +import static jdrafting.geom.JDMath.normal; +import static jdrafting.geom.JDMath.sumVectors; +import static jdrafting.geom.JDMath.vector; +import static jdrafting.gui.JDUtils.elvis; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.util.List; + +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +/** + * Creates a mediatrix segment using mouse control + */ +public class MediatrixListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "mediatrix_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private JDraftingShape segment; + private Point2D start; + + public MediatrixListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_mediatrix1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( segment == null ) + { + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + canvas.setCursor( jdshape != null && jdshape.isSegment( jdshape.getVertex() ) + ? new Cursor( Cursor.HAND_CURSOR ) + : CURSOR ); + } + else + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // set segment + if ( segment == null ) + { + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + if ( jdshape != null && jdshape.isSegment( jdshape.getVertex() ) ) + { + segment = jdshape; + canvas.setCursor( CURSOR ); + app.setStatusText( getLocaleText( "txt_mediatrix2" ) ); + } + } + // set mediatrix + else + { + // get mouse logic position + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // set start point + if ( start == null ) + { + start = getMediatrix( logicMouse ).getP1(); + app.setStatusText( getLocaleText( "txt_mediatrix3" ) ); + } + // set perpendicular segment + else + { + final String descHtml = String.format( "[%s]", + Application.HTML_SHAPE_NAMES_COL, + elvis( segment.getName(), "?" ) ); + + //////////////////////////// TRANSACTION //////////////////////////// + final JDCompoundEdit transaction = new JDCompoundEdit( + getLocaleText( "mediatrix" ) + " " + descHtml ); + + app.addShapeFromIterator( getMediatrix( logicMouse ).getPathIterator( null ), "", + getLocaleText( "new_mediatrix" ) + " " + descHtml, + app.getColor(), null, app.getStroke(), transaction ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + canvas.repaint(); + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( segment != null ) + { + // get logic mouse position + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // draw perpendicular + g2.setStroke( new BasicStroke( 1f ) ); + g2.setColor( Application.toolMainColor ); + g2.draw( canvas.getTransform().createTransformedShape( getMediatrix( logicMouse ) ) ); + } + } + + // --- HELPERS --- + + /** + * Gets a mediatrix segment to another in the logic viewport + * @param logicMouse + * @return the mediatrix + */ + private Line2D getMediatrix( Point2D logicMouse ) + { + final List vertex = segment.getVertex(); + final Point2D vertex1 = vertex.get( 0 ), + vertex2 = vertex.get( 1 ), + midpoint = midpoint( vertex1, vertex2 ), + vector = vector( vertex1, vertex2 ), + normal = normal( vector ), + + p1 = start == null + ? linesIntersection( midpoint, sumVectors( midpoint, normal ), + logicMouse, sumVectors( logicMouse, vector ) ) + : start, + p2 = sumVectors( p1, normal ), + q1 = start == null ? vertex1 : logicMouse, + q2 = sumVectors( q1, vector ); + + return new Line2D.Double( p1, linesIntersection( p1, p2, q1, q2 ) ); + } +} diff --git a/src/jdrafting/gui/controller/mouse/MidpointListener.java b/src/jdrafting/gui/controller/mouse/MidpointListener.java new file mode 100644 index 0000000..fe048c0 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/MidpointListener.java @@ -0,0 +1,104 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.geom.JDMath.centroid; +import static jdrafting.gui.JDUtils.elvis; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.List; + +import jdrafting.geom.JDPoint; +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +public class MidpointListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "midpoint_cursor.png" ); + private Application app; + private CanvasPanel canvas; + + public MidpointListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_midpoint" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + canvas.setCursor( canvas.getShapeAtCanvasPoint( e.getPoint() ) == null + ? CURSOR + : new Cursor( Cursor.HAND_CURSOR ) ); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + + if ( jdshape != null ) + { + Point2D center; + if ( isCentroid() ) // centroid + { + final List vertex = jdshape.getVertex(); + center = jdshape.isClosed( vertex ) + ? centroid( vertex.subList( 0, vertex.size() - 1 ) ) + : centroid( vertex ); + } + else // bounds center + { + final Rectangle2D bounds = jdshape.getShape().getBounds2D(); + center = new Point2D.Double( bounds.getCenterX(), bounds.getCenterY() ); + } + + // add point to exercise + final Color color = isPointStyle() ? app.getColor() : app.getPointColor(); + final BasicStroke stroke = isPointStyle() ? app.getStroke() : app.getPointStroke(); + + final String descHtml = String.format( "[%s]", + Application.HTML_SHAPE_NAMES_COL, + elvis( jdshape.getName(), "?" ) ); + + //////////////////////////// TRANSACTION //////////////////////////// + final JDCompoundEdit transaction = new JDCompoundEdit( + getLocaleText( isCentroid() ? "centroid" : "midpoint" ) + " " + descHtml ); + + app.addShapeFromIterator( new JDPoint( center ).getPathIterator( null ), "", + getLocaleText( isCentroid() ? "new_centroid" : "new_midpoint" ) + " " + descHtml, + color, null, stroke, transaction ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + + + // --- HELPERS + + // check modifiers + private boolean isCentroid() { return mouse().isControlDown(); } + private boolean isPointStyle() { return mouse().isShiftDown(); } +} diff --git a/src/jdrafting/gui/controller/mouse/ModifySegmentListener.java b/src/jdrafting/gui/controller/mouse/ModifySegmentListener.java new file mode 100644 index 0000000..4b45baa --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/ModifySegmentListener.java @@ -0,0 +1,177 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.geom.JDMath.linesIntersection; +import static jdrafting.geom.JDMath.normal; +import static jdrafting.geom.JDMath.sumVectors; +import static jdrafting.geom.JDMath.vector; +import static jdrafting.gui.JDUtils.elvis; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.util.List; + +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +/** + * Modifies a segment using mouse control + */ +public class ModifySegmentListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "modify_segment_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private JDraftingShape segment; + private Point2D start; + + public ModifySegmentListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + app.setStatusText( getLocaleText( "txt_modify1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + // adjust cursor over segments + if ( segment == null ) + { + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + canvas.setCursor( jdshape != null && jdshape.isSegment( jdshape.getVertex() ) + ? new Cursor( Cursor.HAND_CURSOR ) + : CURSOR ); + } + else + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + if ( segment == null ) + { + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + if ( jdshape != null && jdshape.isSegment( jdshape.getVertex() ) ) + { + segment = jdshape; + canvas.setCursor( CURSOR ); + app.setStatusText( getLocaleText( "txt_modify2" ) ); + canvas.repaint(); + } + } + else + { + // get mouse logic position + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + if ( start == null ) + { + start = getSegment( logicMouse ).getP2(); + app.setStatusText( getLocaleText( "txt_modify3" ) ); + canvas.repaint(); + } + else + { + // point style + final Color color = isPointStyle() + ? app.getColor() + : segment.getColor(); + final BasicStroke stroke = isPointStyle() + ? app.getStroke() + : segment.getStroke(); + + // new segment + final Line2D newSegment = getSegment( logicMouse ); + final JDraftingShape jdshape = JDraftingShape.createFromIterator( + newSegment.getPathIterator( null ), segment.getName(), + segment.getDescription(), color, null, stroke ); + + // replace segment + final int index = app.getExercise().indexOf( segment ); + app.getExercise().set( index, jdshape ); + app.shapeList.getModel().remove( index ); + app.shapeList.getModel().add( index, jdshape ); + + final String descHtml = String.format( "[%s]", + Application.HTML_SHAPE_NAMES_COL, + elvis( segment.getName(), "?" ) ); + + //////////////////////////// TRANSACTION //////////////////////////// + final JDCompoundEdit transaction = new JDCompoundEdit( + getLocaleText( "modify" ) + " " + descHtml ); + + // remove old segment and add new segment to exercise + transaction.addEdit( app.new EditAddShapeToExercise( jdshape, index + 1 ) ); + transaction.addEdit( app.new EditRemoveShapeFromExercise( segment, index ) ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + // refresh + app.scrollList.repaint(); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( segment != null ) + { + // get logic mouse position + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // draw segment + g2.setStroke( new BasicStroke( 3f ) ); + g2.setColor( Application.toolMainColor ); + g2.draw( canvas.getTransform().createTransformedShape( + getSegment( logicMouse ) ) ); + } + } + + // --- HELPERS --- + + /** + * Get new segment in logic viewport + * @param logicMouse + * @return the new segment + */ + private Line2D getSegment( Point2D logicMouse ) + { + final List vertex = segment.getVertex(); + final Point2D vertex1 = vertex.get( 0 ), + vertex2 = vertex.get( 1 ), + vector = vector( vertex1, vertex2 ), + normal = normal( vector ), + p1 = start == null ? vertex1 : start, + p2 = sumVectors( p1, vector ), + q1 = logicMouse, + q2 = sumVectors( q1, normal ); + + return new Line2D.Double( p1, linesIntersection( p1, p2, q1, q2 ) ); + } + + private boolean isPointStyle() { return mouse().isShiftDown(); } +} diff --git a/src/jdrafting/gui/controller/mouse/PaintListener.java b/src/jdrafting/gui/controller/mouse/PaintListener.java new file mode 100644 index 0000000..7097479 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/PaintListener.java @@ -0,0 +1,132 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.gui.JDUtils.elvis; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; +import java.util.List; + +import javax.swing.undo.AbstractUndoableEdit; + +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; + +public class PaintListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "paint_cursor.png" ); + private Application app; + private CanvasPanel canvas; + + public PaintListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + app.setStatusText( JDUtils.getLocaleText( "txt_paint1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + + if ( jdshape == null ) + canvas.setCursor( CURSOR ); + else + { + final List vertex = jdshape.getVertex(); + canvas.setCursor( jdshape.isPoint( vertex ) || jdshape.isSegment( vertex ) + ? CURSOR + : new Cursor( Cursor.HAND_CURSOR ) ); + } + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // get shape + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + if ( jdshape == null ) return; + + // don't apply to segments or points + final List vertex = jdshape.getVertex(); + if ( jdshape.isPoint( vertex ) || jdshape.isSegment( vertex ) ) + return; + + // set fill color + final Color newColor = isDeleteColor() + ? null // no fill + : isPointColor() + ? app.getPointColor() + : isLineColor() ? app.getColor() : app.getFill(), + oldColor = jdshape.getFill(); + jdshape.setFill( newColor ); + + // add undo/redo edit + app.undoRedoSupport.postEdit( new EditPaint( jdshape, newColor, oldColor ) ); + + // refresh + app.scrollList.repaint(); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + + + // --- HELPERS + private boolean isLineColor() { return mouse().isAltDown(); } + private boolean isPointColor() { return mouse().isShiftDown(); } + private boolean isDeleteColor() { return mouse().isControlDown(); } + + + @SuppressWarnings("serial") + private class EditPaint extends AbstractUndoableEdit + { + private JDraftingShape jdshape; + private Color newColor, oldColor; + + public EditPaint( JDraftingShape jdshape, Color newColor, Color oldColor ) + { + this.jdshape = jdshape; + this.newColor = newColor; + this.oldColor = oldColor; + } + + @Override + public void undo() + { + jdshape.setFill( oldColor ); + } + + @Override + public void redo() + { + jdshape.setFill( newColor ); + } + + @Override + public boolean canRedo() { return true; } + @Override + public boolean canUndo() { return true; } + + @Override + public String getPresentationName() + { + return String.format( "%s [%s] ", + getLocaleText( "paint" ), + Application.HTML_SHAPE_NAMES_COL, + elvis( jdshape.getName(), "?" ) ); + } + } +} diff --git a/src/jdrafting/gui/controller/mouse/ParabolaListener.java b/src/jdrafting/gui/controller/mouse/ParabolaListener.java new file mode 100644 index 0000000..54916d8 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/ParabolaListener.java @@ -0,0 +1,240 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.HashMap; +import java.util.Map; + +import jdrafting.geom.JDPoint; +import jdrafting.geom.JDStrokes; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +/** + * Create parabola from bounds by mouse control + */ +public class ParabolaListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "parabola_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private Point2D start; + + public ParabolaListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_parabola1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( start != null ) + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // put first corner + if ( start == null ) + { + start = logicMouse; + app.setStatusText( getLocaleText( "txt_parabola2" ) ); + canvas.repaint(); + } + // finish rectangle bounds + else + { + if ( addMore() ) // add complete transaction + { + //////////////////////////// TRANSACTION //////////////////////////// + final JDCompoundEdit transaction = new JDCompoundEdit( + getLocaleText( "parabola" ) + + (addMore() ? " (& auxiliar shapes)" : "") ); + + final Map map = getParabola( logicMouse ); + // add parabola, bounds, vertex, focus, directrix + app.addShapeFromIterator( ( (Path2D) map.get( "parabola" ) ) + .getPathIterator( null ), "", + getLocaleText( "new_parabola" ), + app.getColor(), null, app.getStroke(), transaction ); + app.addShapeFromIterator( ( (Rectangle2D) map.get( "bounds" ) ) + .getPathIterator( null ), "", + getLocaleText( "new_para_bounds" ), + app.getColor(), null, app.getStroke(), transaction ); + app.addShapeFromIterator( ( (Line2D) map.get( "directrix" ) ) + .getPathIterator( null ), "", + getLocaleText( "new_directrix" ), + app.getColor(), null, app.getStroke(), transaction ); + app.addShapeFromIterator( ( new JDPoint( (Point2D) map.get( "vertex" ) ) ) + .getPathIterator( null ), "", + getLocaleText( "new_para_vertex" ), + app.getPointColor(), null, app.getPointStroke(), + transaction ); + app.addShapeFromIterator( ( new JDPoint( (Point2D) map.get( "focus" ) ) ) + .getPathIterator( null ), "", + getLocaleText( "new_focus" ), + app.getPointColor(), null, app.getPointStroke(), + transaction ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + } + else + // add parabola to exercise + app.addShapeFromIterator( ( (Path2D) getParabola( logicMouse ).get( "parabola" ) ) + .getPathIterator( null ), "", + getLocaleText( "new_parabola" ), + app.getColor(), null, app.getStroke() ); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( start != null ) + { + AffineTransform transform = canvas.getTransform(); + + // mouse position in logic viewport + Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // set style + g2.setStroke( new BasicStroke( 1f ) ); + g2.setColor( Application.toolMainColor ); + + Map map = getParabola( logicMouse ); + + // draw parabola + g2.draw( transform.createTransformedShape( + (Path2D) map.get( "parabola" ) ) ); + // draw bounds + g2.setStroke( + JDStrokes.getStroke( JDStrokes.DASHED.getStroke(), 1f ) ); + g2.draw( transform.createTransformedShape( + (Rectangle2D) map.get( "bounds" ) ) ); + // draw directrix + g2.draw( transform.createTransformedShape( + (Line2D) map.get( "directrix" ) ) ); + // draw vertex + g2.setStroke( new BasicStroke( 4f ) ); + g2.draw( transform.createTransformedShape( + new JDPoint( (Point2D) map.get( "vertex" ) ) ) ); + // draw focus + g2.draw( transform.createTransformedShape( + new JDPoint( (Point2D) map.get( "focus" ) ) ) ); + } + } + + // --- HELPERS --- + + // check modifiers + private boolean isSquare() { return mouse().isShiftDown(); } + private boolean addMore() { return mouse().isControlDown(); } + + /** + * Gets the parabola + * @param logicMouse + * @return the parabola in the logic viewport + */ + private Map getParabola( Point2D logicMouse ) + { + // calculate coords + double x = app.isUsingRuler() + ? start.getX() > logicMouse.getX() + ? start.getX() - app.getDistance() + : start.getX() + app.getDistance() + : logicMouse.getX(); + double minX = Math.min( start.getX(), x ); + double maxX = Math.max( start.getX(), x ); + double minY = Math.min( start.getY(), logicMouse.getY() ); + double maxY = Math.max( start.getY(), logicMouse.getY() ); + + // create parabola rectangle bounds + Rectangle2D rect = + new Rectangle2D.Double( minX, minY, maxX - minX, maxY - minY ); + if ( isSquare() ) // create square bounds + { + double size = Math.min( rect.getWidth(), rect.getHeight() ); + double px = logicMouse.getX() < start.getX() + ? start.getX() - size + : start.getX(); + double py = logicMouse.getY() < start.getY() + ? start.getY() - size + : start.getY(); + rect = new Rectangle2D.Double( px, py, size, size ); + } + + // create parabola + Path2D parabola = new Path2D.Double(); + + double w = rect.getWidth(), h = rect.getHeight(); + double a = 4 * h / ( w * w ); // y=ax^2 + int n = (int) Math.sqrt( app.getFlatnessValue() ); + + for ( int i = 0; i < n; i++ ) + { + double xp = -w / 2. + w / ( n - 1 ) * i; + double yp = a * xp * xp; + if ( i != 0 ) + parabola.lineTo( xp, yp ); + else + parabola.moveTo( xp, yp ); + } + + // create vertex + Point2D vertex = new Point2D.Double( + ( rect.getMinX() + rect.getMaxX() ) / 2., rect.getMinY() ); + // create focus. [ p:focal distance -> a=1/4p -> p=1/(4a) ] + Point2D focus = + new Point2D.Double( vertex.getX(), rect.getMinY() + 1 / ( 4 * a ) ); + // create directrix + Line2D directrix = new Line2D.Double( + rect.getMinX(), rect.getMinY() - 1 / ( 4 * a ), + rect.getMaxX(), rect.getMinY() - 1 / ( 4 * a ) ); + + // translate parabola to vertex + parabola.transform( AffineTransform.getTranslateInstance( + vertex.getX(), vertex.getY() ) ); + + // return results + Map map = new HashMap<>(); + map.put( "parabola", parabola ); + map.put( "vertex", vertex ); + map.put( "focus", focus ); + map.put( "bounds", rect ); + map.put( "directrix", directrix ); + + return map; + } +} diff --git a/src/jdrafting/gui/controller/mouse/ParallelListener.java b/src/jdrafting/gui/controller/mouse/ParallelListener.java new file mode 100644 index 0000000..3bc6d6d --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/ParallelListener.java @@ -0,0 +1,166 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.geom.JDMath.adjustVectorToSize; +import static jdrafting.geom.JDMath.linesIntersection; +import static jdrafting.geom.JDMath.midpoint; +import static jdrafting.geom.JDMath.normal; +import static jdrafting.geom.JDMath.sumVectors; +import static jdrafting.geom.JDMath.vector; +import static jdrafting.gui.JDUtils.elvis; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.util.List; + +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +public class ParallelListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "parallel_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private JDraftingShape segment; + private Point2D start; + + public ParallelListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_para1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( segment == null ) + { + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + canvas.setCursor( jdshape != null && jdshape.isSegment( jdshape.getVertex() ) + ? new Cursor( Cursor.HAND_CURSOR ) + : CURSOR ); + } + else + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + if ( segment == null ) + { + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + + if ( jdshape != null && jdshape.isSegment( jdshape.getVertex() ) ) + { + segment = jdshape; + canvas.setCursor( CURSOR ); + app.setStatusText( getLocaleText( "txt_para2" ) ); + canvas.repaint(); + } + } + else + { + // mouse logic position + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // set start + if ( start == null ) + { + start = getParallel( logicMouse ).getP1(); + app.setStatusText( getLocaleText( "txt_para3" ) ); + canvas.repaint(); + } + // set parallel + else + { + final String descHtml = String.format( "[%s]", + Application.HTML_SHAPE_NAMES_COL, + elvis( segment.getName(), "?" ) ); + + //////////////////////////// TRANSACTION //////////////////////////// + final JDCompoundEdit transaction = new JDCompoundEdit( + getLocaleText( "para" ) + " " + descHtml ); + + // add parallel to exercise + app.addShapeFromIterator( getParallel( logicMouse ).getPathIterator( null ), "", + getLocaleText( "new_parallel" ) + " " + descHtml, + app.getColor(), null, app.getStroke(), transaction ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( segment != null ) + { + // mouse logic position + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // draw parallel + g2.setStroke( new BasicStroke( 1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) ); + g2.setColor( Application.toolMainColor ); + g2.draw( canvas.getTransform().createTransformedShape( getParallel( logicMouse ) ) ); + } + } + + // --- HELPERS --- + + private Line2D getParallel( Point2D logicMouse ) + { + final List vertex = segment.getVertex(); + final Point2D vertex1 = vertex.get( 0 ), + vertex2 = vertex.get( 1 ), + vector = vector( vertex1, vertex2 ), + normal = normal( vector ), + + point = app.isUsingRuler() + ? getFixedDistanceStart( logicMouse , app.getDistance() ) + : logicMouse, + p1 = start == null ? point : start, + p2 = sumVectors( p1, vector ), + q1 = start == null ? midpoint( vertex1, vertex2 ) : logicMouse, + q2 = sumVectors( q1, normal ); + + return new Line2D.Double( p1, linesIntersection( p1, p2, q1, q2 ) ); + } + + private Point2D getFixedDistanceStart( Point2D logicMouse, double distance ) + { + final List vertex = segment.getVertex(); + final Point2D vertex1 = vertex.get( 0 ), + vertex2 = vertex.get( 1 ), + normal = normal( vector( vertex1, vertex2 ) ), + + foot = linesIntersection( vertex1, vertex2, logicMouse, + sumVectors( logicMouse, normal ) ); + + return sumVectors( foot, adjustVectorToSize( vector( foot, logicMouse ), distance ) ); + } +} diff --git a/src/jdrafting/gui/controller/mouse/PasteStyleListener.java b/src/jdrafting/gui/controller/mouse/PasteStyleListener.java new file mode 100644 index 0000000..5dba679 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/PasteStyleListener.java @@ -0,0 +1,144 @@ +package jdrafting.gui.controller.mouse; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseEvent; + +import javax.swing.undo.AbstractUndoableEdit; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; + +import jdrafting.geom.JDStrokes; +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; + +public class PasteStyleListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "paste_style_cursor.png" ); + private Application app; + private CanvasPanel canvas; + + public PasteStyleListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + app.setStatusText( JDUtils.getLocaleText( "txt_paste_style1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( canvas.getShapeAtCanvasPoint( e.getPoint() ) == null ) + canvas.setCursor( CURSOR ); + else + canvas.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // get shape + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + if ( jdshape == null ) return; + + // set color and stroke + final boolean isPoint = jdshape.isPoint( jdshape.getVertex() ); + Color newColor, oldColor = jdshape.getColor(), newFill = null, oldFill = jdshape.getFill(); + BasicStroke newStroke, oldStroke = jdshape.getStroke(); + if ( isPointStyle() ) + { + jdshape.setColor( newColor = app.getPointColor() ); + jdshape.setStroke( newStroke = isPoint + ? app.getPointStroke() + : JDStrokes.getStroke( JDStrokes.PLAIN_ROUND.getStroke(), + app.getPointStroke().getLineWidth() ) ); + } + else + { + jdshape.setColor( newColor = app.getColor() ); + jdshape.setFill( newFill = isFill() ? app.getFill() : null ); + jdshape.setStroke( newStroke = isPoint + ? JDStrokes.cloneStrokeStyle( app.getStroke().getLineWidth(), + app.getPointStroke() ) + : app.getStroke() ); + } + + // add undo/redo edit + app.undoRedoSupport.postEdit( new EditPasteStyle( + jdshape, newColor, oldColor, newFill, oldFill, newStroke, oldStroke ) ); + + // refresh + app.scrollList.repaint(); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + + + // --- HELPERS + private boolean isPointStyle() { return mouse().isShiftDown(); } + private boolean isFill() { return !mouse().isControlDown(); } + + @SuppressWarnings("serial") + private class EditPasteStyle extends AbstractUndoableEdit + { + private JDraftingShape jdshape; + private Color newColor, oldColor, newFill, oldFill; + private BasicStroke newStroke, oldStroke; + + public EditPasteStyle( JDraftingShape jdshape, Color newColor, + Color oldColor, Color newFill, Color oldFill, + BasicStroke newStroke, BasicStroke oldStroke ) + { + this.jdshape = jdshape; + this.newColor = newColor; + this.oldColor = oldColor; + this.newFill = newFill; + this.oldFill = oldFill; + this.newStroke = newStroke; + this.oldStroke = oldStroke; + } + + @Override + public void undo() throws CannotUndoException + { + jdshape.setColor( oldColor ); + jdshape.setFill( oldFill ); + jdshape.setStroke( oldStroke ); + } + + @Override + public void redo() throws CannotRedoException + { + jdshape.setColor( newColor ); + jdshape.setFill( newFill ); + jdshape.setStroke( newStroke ); + } + + @Override + public boolean canRedo() { return true; } + @Override + public boolean canUndo() { return true; } + + @Override + public String getUndoPresentationName() + { + return "Undo paste style"; + } + @Override + public String getRedoPresentationName() + { + return "Redo paste style"; + } + } +} diff --git a/src/jdrafting/gui/controller/mouse/PerpendicularListener.java b/src/jdrafting/gui/controller/mouse/PerpendicularListener.java new file mode 100644 index 0000000..b338f1f --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/PerpendicularListener.java @@ -0,0 +1,155 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.geom.JDMath.linesIntersection; +import static jdrafting.geom.JDMath.normal; +import static jdrafting.geom.JDMath.sumVectors; +import static jdrafting.geom.JDMath.vector; +import static jdrafting.gui.JDUtils.elvis; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.util.List; + +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +/** + * Creates a perpendicular segment using mouse control + */ +public class PerpendicularListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "perpendicular_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private JDraftingShape segment; + private Point2D start; + + public PerpendicularListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_perp1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( segment == null ) + { + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + canvas.setCursor( jdshape != null && jdshape.isSegment( jdshape.getVertex() ) + ? new Cursor( Cursor.HAND_CURSOR ) + : CURSOR ); + } + else + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + if ( segment == null ) + { + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + if ( jdshape != null && jdshape.isSegment( jdshape.getVertex() ) ) + { + segment = jdshape; + canvas.setCursor( CURSOR ); + app.setStatusText( getLocaleText( "txt_perp2" ) ); + canvas.repaint(); + } + } + else + { + // get mouse logic position + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // set start point + if ( start == null ) + { + start = getPerpendicular( logicMouse ).getP1(); + app.setStatusText( getLocaleText( "txt_perp3" ) ); + canvas.repaint(); + } + // set perpendicular segment + else + { + final String descHtml = String.format( "[%s]", + Application.HTML_SHAPE_NAMES_COL, + elvis( segment.getName(), "?" ) ); + + //////////////////////////// TRANSACTION //////////////////////////// + final JDCompoundEdit transaction = new JDCompoundEdit( + getLocaleText( "perp" ) + " " + descHtml ); + + app.addShapeFromIterator( getPerpendicular( logicMouse ).getPathIterator( null ), + "", getLocaleText( "new_perpendicular" ) + " " + descHtml, + app.getColor(), null, app.getStroke(), transaction ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( segment != null ) + { + // get logic mouse position + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // draw perpendicular + g2.setStroke( new BasicStroke( 1f ) ); + g2.setColor( Application.toolMainColor ); + g2.draw( canvas.getTransform().createTransformedShape( + getPerpendicular( logicMouse ) ) ); + } + } + + // --- HELPERS --- + + /** + * Gets a perpendicular segment to another in the logic viewport + * @param logicMouse + * @return a perpendicular segment + */ + private Line2D getPerpendicular( Point2D logicMouse ) + { + final List vertex = segment.getVertex(); + final Point2D vertex1 = vertex.get( 0 ), + vertex2 = vertex.get( 1 ), + vector = vector( vertex1, vertex2 ), + normal = normal( vector ), + + p1 = start == null ? logicMouse : start, + p2 = sumVectors( p1, normal ), + q1 = start == null ? vertex1 : logicMouse, + q2 = sumVectors( q1, vector ); + + return new Line2D.Double( p1, linesIntersection( p1, p2, q1, q2 ) ); + } +} diff --git a/src/jdrafting/gui/controller/mouse/PointListener.java b/src/jdrafting/gui/controller/mouse/PointListener.java new file mode 100644 index 0000000..76f551a --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/PointListener.java @@ -0,0 +1,109 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; + +import jdrafting.geom.JDPoint; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; + + +public class PointListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "point_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + public PointListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( JDUtils.getLocaleText( "txt_point" ) ); + } + + @Override + public void mouseMoved(MouseEvent e) + { + super.mouseMoved(e); + + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // create point + JDPoint point; + + // force free point + if ( isFreePoint() ) + point = new JDPoint( canvas.getInverseTransform().transform( e.getPoint(), null ) ); + // try to adjust to intersection or vertex + else + { + Point2D p = canvas.adjustPointToIntersection( e.getPoint() ); + p = p == null ? canvas.adjustToVertex( e.getPoint() ) : p; + if ( p != null ) + point = new JDPoint( p ); + else + point = new JDPoint( canvas.getInverseTransform().transform( e.getPoint(), null ) ); + } + + // add point to exercise + final Color color = isLineStyle() + ? app.getColor() + : app.getPointColor(); + final BasicStroke stroke = isLineStyle() + ? app.getStroke() + : app.getPointStroke(); + app.addShapeFromIterator( point.getPathIterator( null ), + "", getLocaleText( "new_point" ), color, null, stroke ); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + + @Override + public void paintTool( Graphics2D g2 ) + { + int crossSize = 7; + Point2D point = mouse().getPoint(); + // adjust to intersection or vertex + if ( !isFreePoint() ) + { + Point2D p = canvas.adjustPointToIntersection( mouse().getPoint() ); + p = p == null ? canvas.adjustToVertex( mouse().getPoint() ) : p; + if ( p != null ) + { + point = canvas.getTransform().transform( p, null ); + crossSize = 12; + } + } + + g2.setColor( Application.toolMainColor ); + g2.setStroke( new BasicStroke( 1f ) ); + g2.drawOval( (int) Math.round( point.getX() - crossSize / 2. ), + (int) Math.round( point.getY() - crossSize / 2. ), + crossSize, crossSize ); + } + + // --- HELPERS + + // check modifiers + private boolean isFreePoint() { return mouse().isControlDown(); } + private boolean isLineStyle() { return mouse().isShiftDown(); } +} diff --git a/src/jdrafting/gui/controller/mouse/PolygonListener.java b/src/jdrafting/gui/controller/mouse/PolygonListener.java new file mode 100644 index 0000000..bfd514d --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/PolygonListener.java @@ -0,0 +1,127 @@ +package jdrafting.gui.controller.mouse; + +import java.awt.BasicStroke; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; + +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; + +import static jdrafting.gui.JDUtils.getLocaleText; + +/** + * Creates a polygon or polyline using mouse control + */ +public class PolygonListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR_POLYGON = JDUtils.getCustomCursor( "polygon_cursor.png" ); + private static final Cursor CURSOR_POLYLINE = JDUtils.getCustomCursor( "polyline_cursor.png" ); + private final Cursor CURSOR; + private CanvasPanel canvas; + private Application app; + + private Path2D polygon = new Path2D.Double(); + private Point2D start; + private boolean closed; + + public PolygonListener( CanvasPanel canvas, boolean closed ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + this.closed = closed; + + CURSOR = closed ? CURSOR_POLYGON : CURSOR_POLYLINE; + canvas.setCursor( CURSOR ); + + app.setStatusText( JDUtils.getLocaleText( "txt_poly" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( start != null ) + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // finish polygon capture + if ( e.getClickCount() == 2 ) + { + // add start point to end point (needed for some operations) + if ( closed ) + { + polygon.lineTo( start.getX(), start.getY() ); + //polygon.closePath(); // "Fragment" would ignore last side + } + + // add polygon to exercise + app.addShapeFromIterator( polygon.getPathIterator( null ), "", + getLocaleText( closed ? "new_polygon" : "new_polyline" ), + app.getColor(), null, app.getStroke() ); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + // add new point to polygon + else + { + if ( polygon.getCurrentPoint() != null ) + polygon.lineTo( logicMouse.getX(), logicMouse.getY() ); + else // first point + { + polygon.moveTo( logicMouse.getX(), logicMouse.getY() ); + start = logicMouse; + } + } + + canvas.repaint(); + } + + @Override + public void paintTool( Graphics2D g2 ) + { + final AffineTransform transform = canvas.getTransform(); + + // mouse position on logic viewport + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + g2.setStroke( new BasicStroke( 1f ) ); + g2.setColor( Application.toolMainColor ); + + g2.draw( transform.createTransformedShape( polygon ) ); + + final PathIterator path = polygon.getPathIterator( null ); + final double[] coords = new double[6]; + + int sides = 0; + while ( !path.isDone() ) + { + sides++; + path.currentSegment( coords ); + path.next(); + } + + if ( sides > 0 ) + g2.draw( transform.createTransformedShape( new Line2D.Double( + coords[0], coords[1], + logicMouse.getX(), logicMouse.getY() ) ) ); + } +} diff --git a/src/jdrafting/gui/controller/mouse/ProtractorListener.java b/src/jdrafting/gui/controller/mouse/ProtractorListener.java new file mode 100644 index 0000000..aaed38d --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/ProtractorListener.java @@ -0,0 +1,145 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.geom.JDMath.lineAng; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Arc2D; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; + +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; + +/** + * Capture an angle using mouse control + */ +public class ProtractorListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "protractor_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private Point2D vertex, p1; + + public ProtractorListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_prot1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( vertex != null ) + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // put vertex + if ( vertex == null ) + { + vertex = logicMouse; + app.setStatusText( getLocaleText( "txt_prot2" ) ); + } + // put first angle side + else if ( p1 == null ) + { + p1 = logicMouse; + app.setStatusText( getLocaleText( "txt_prot3" ) ); + } + // put second angle side + else + { + final Point2D p2 = logicMouse; + + // set captured angle + final double ang = lineAng( new Line2D.Double( vertex, p1 ), + new Line2D.Double( vertex, p2 ) ); + app.setAngle( Math.toDegrees( ang ) ); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + + canvas.repaint(); + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( vertex != null ) + { + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // get transform + final AffineTransform transform = canvas.getTransform(); + + // set tool style + g2.setStroke( new BasicStroke( 1f ) ); + g2.setColor( Application.toolMainColor ); + + // draw first side + Line2D line1 = new Line2D.Double( vertex, p1 == null ? logicMouse : p1 ); + g2.draw( transform.createTransformedShape( line1 ) ); + + if ( p1 != null ) + { + // draw second side + final Line2D line2 = new Line2D.Double( vertex, logicMouse ); + g2.draw( transform.createTransformedShape( line2 ) ); + + // draw angle + final double ang = Math.toDegrees( lineAng( line1, line2 ) ), + dist = Math.min( vertex.distance( p1 ), + vertex.distance( logicMouse ) ) / 2.; + double offang = Math.toDegrees( Math.atan2( + p1.getY() - vertex.getY(), p1.getX() - vertex.getX() ) ); + + final Arc2D arc = new Arc2D.Double( + vertex.getX() - dist / 2, vertex.getY() - dist / 2, + dist, dist, + -offang, line1.relativeCCW( logicMouse ) <= 0 ? -ang : ang, + Arc2D.PIE ); + + g2.setColor( Color.MAGENTA ); + g2.setStroke( new BasicStroke( 2f, BasicStroke.CAP_ROUND, + BasicStroke.JOIN_ROUND, 0f, new float[] { 3f, 5f }, + (float) Math.random() * 4 ) ); + g2.draw( transform.createTransformedShape( arc ) ); + + // draw angle info + final int mouseX = mouse().getX(), mouseY = mouse().getY(); + final String angInfo = String.format( "%.2f", ang ) + "º"; + g2.setFont( new Font( Font.SERIF, Font.BOLD, 16 ) ); + g2.setColor( new Color( 40, 40, 180 ) ); + g2.drawString( angInfo, mouseX + 21, mouseY - 9 ); + g2.setColor( Color.LIGHT_GRAY ); + g2.drawString( angInfo, mouseX + 20, mouseY - 10 ); + } + } + } +} diff --git a/src/jdrafting/gui/controller/mouse/RectangleListener.java b/src/jdrafting/gui/controller/mouse/RectangleListener.java new file mode 100644 index 0000000..0d7ae6a --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/RectangleListener.java @@ -0,0 +1,140 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; + +/** + * Create a rectangle by mouse control + */ +public class RectangleListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "rectangle_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private Point2D start; + + public RectangleListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_rect1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( start == null ) return; + + // dynamic cursor + final Point2D pos = canvas.getInverseTransform().transform( e.getPoint(), null ); + canvas.setCursor( new Cursor( (pos.getX() - start.getX()) * (pos.getY() - start.getY()) > 0 + ? Cursor.NE_RESIZE_CURSOR + : Cursor.NW_RESIZE_CURSOR ) ); + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // put first corner + if ( start == null ) + { + start = logicMouse; + app.setStatusText( getLocaleText( "txt_rect2" ) ); + } + // finish rectangle + else + { + // add rectangle to exercise + app.addShapeFromIterator( JDUtils.removeUnnecessarySegments( + getRectangle( logicMouse ) ).getPathIterator( null ), + "", getLocaleText( isSquare() ? "new_square" : "new_rectangle" ), + app.getColor(), null, app.getStroke() ); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + + canvas.repaint(); + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( start != null ) + { + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // set style + g2.setStroke( new BasicStroke( 1f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL ) ); + g2.setColor( Application.toolMainColor ); + + // draw rectangle + g2.draw( canvas.getTransform().createTransformedShape( getRectangle( logicMouse ) ) ); + } + } + + // --- HELPERS --- + + // check modifiers + private boolean isSquare() { return mouse().isShiftDown(); } + + /** + * Gets the rectangle + * @param logicMouse + * @return the rectangle in the logic viewport + */ + private Rectangle2D getRectangle( Point2D logicMouse ) + { + // calculate coords + final double x = app.isUsingRuler() + ? start.getX() > logicMouse.getX() + ? start.getX() - app.getDistance() + : start.getX() + app.getDistance() + : logicMouse.getX(), + minX = Math.min( start.getX(), x ), + maxX = Math.max( start.getX(), x ), + minY = Math.min( start.getY(), logicMouse.getY() ), + maxY = Math.max( start.getY(), logicMouse.getY() ); + + // create rectangle + Rectangle2D rect = new Rectangle2D.Double( minX, minY, maxX - minX, maxY - minY ); + if ( isSquare() ) // create square + { + final double size = Math.min( rect.getWidth(), rect.getHeight() ), + px = logicMouse.getX() < start.getX() + ? start.getX() - size + : start.getX(), + py = logicMouse.getY() < start.getY() + ? start.getY() - size + : start.getY(); + rect = new Rectangle2D.Double( px, py, size, size ); + } + + return rect; + } +} diff --git a/src/jdrafting/gui/controller/mouse/RegularPolygonListener.java b/src/jdrafting/gui/controller/mouse/RegularPolygonListener.java new file mode 100644 index 0000000..070b825 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/RegularPolygonListener.java @@ -0,0 +1,196 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; +import java.util.HashMap; +import java.util.Map; + +import jdrafting.geom.JDMath; +import jdrafting.geom.JDPoint; +import jdrafting.geom.JDStrokes; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +/** + * Create regular polygon by mouse control + */ +public class RegularPolygonListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "reg_poly_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private int n; + private Point2D center; + + public RegularPolygonListener( CanvasPanel canvas, int n ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + this.n = n; + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_reg_poly1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( center != null || app.isUsingRuler() ) + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // put center + if ( center == null && !app.isUsingRuler() ) + { + center = logicMouse; + app.setStatusText( getLocaleText( "txt_reg_poly2" ) ); + canvas.repaint(); + } + // add polygon + else + { + if ( addMore() ) // add complete transaction + { + //////////////////////////// TRANSACTION //////////////////////////// + final JDCompoundEdit transaction = new JDCompoundEdit( + getLocaleText( "reg_poly" ) + " (& center & circumscribed)" ); + + final Map map = getPolygon( logicMouse ); + // add polygon + app.addShapeFromIterator( ( (Path2D) map.get( "polygon" ) ).getPathIterator( null ), + "", getLocaleText( "new_regular_polygon" ), + app.getColor(), null, app.getStroke(), transaction ); + // add center + app.addShapeFromIterator( ( new JDPoint( (Point2D) map.get( "center" ) ) ) + .getPathIterator( null ), + "", getLocaleText( "new_center_reg" ), + app.getPointColor(), null, app.getPointStroke(), + transaction ); + // add circumference circumscribed + final double flatness = + ( JDMath.length( (Ellipse2D) map.get( "circumscribed" ), null ) ) + / app.getFlatnessValue(); + app.addShapeFromIterator( ( (Ellipse2D) map.get( "circumscribed" ) ) + .getPathIterator( null, flatness ), + "", getLocaleText( "new_circumscribed" ), app.getColor(), + null, app.getStroke(), transaction ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + } + else + // add polygon to exercise + app.addShapeFromIterator( ( (Path2D) getPolygon( logicMouse ).get( "polygon" ) ) + .getPathIterator( null ), "", + getLocaleText( "new_regular_polygon" ), + app.getColor(), null, app.getStroke() ); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( center != null || app.isUsingRuler() ) + { + final AffineTransform transform = canvas.getTransform(); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // set style + g2.setStroke( new BasicStroke( 1f ) ); + g2.setColor( Application.toolMainColor ); + + final Map map = getPolygon( logicMouse ); + + // draw polygon + g2.draw( transform.createTransformedShape( + (Path2D) map.get( "polygon" ) ) ); + // draw center + g2.setStroke( new BasicStroke( + 8f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) ); + g2.draw( transform.createTransformedShape( + new JDPoint( (Point2D) map.get( "center" ) ) ) ); + // draw circumference circumscribed + g2.setStroke( + JDStrokes.getStroke( JDStrokes.DASHED.getStroke(), 1f ) ); + g2.draw( transform.createTransformedShape( + (Ellipse2D) map.get( "circumscribed" ) ) ); + } + } + + // --- HELPERS --- + + // check modifiers + private boolean addMore() { return mouse().isControlDown(); } + + /** + * Gets the polygon + * @param logicMouse + * @return the polygon in the logic viewport + */ + private Map getPolygon( Point2D logicMouse ) + { + // calculate radius and center + final double radius = app.isUsingRuler() + ? app.getDistance() + : center.distance( logicMouse ); + final Point2D center = this.center != null ? this.center : logicMouse; + + // create polygon + final Path2D polygon = new Path2D.Double(); + final double sideAng = 2 * Math.PI / n, + offsetAng = Math.PI * ( 3. / 2. - 1. / n ) % sideAng; + double ang = offsetAng; + + polygon.moveTo( center.getX() + radius * Math.cos( ang ), + center.getY() + radius * Math.sin( ang ) ); + + for ( int i = 1; i < n; i++ ) + { + ang += sideAng; + polygon.lineTo( center.getX() + radius * Math.cos( ang ), + center.getY() + radius * Math.sin( ang ) ); + } + polygon.lineTo( center.getX() + radius * Math.cos( offsetAng ), + center.getY() + radius * Math.sin( offsetAng ) ); + //polygon.closePath(); + + // return results + final Map map = new HashMap<>(); + map.put( "polygon", polygon ); + map.put( "center", center ); + map.put( "circumscribed", new Ellipse2D.Double( + center.getX() - radius, center.getY() - radius, + 2 * radius, 2 * radius ) ); + return map; + } +} diff --git a/src/jdrafting/gui/controller/mouse/RotationListener.java b/src/jdrafting/gui/controller/mouse/RotationListener.java new file mode 100644 index 0000000..9df6fd2 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/RotationListener.java @@ -0,0 +1,217 @@ +package jdrafting.gui.controller.mouse; + +import static java.lang.Math.atan2; +import static java.lang.Math.rint; +import static java.lang.Math.toDegrees; +import static java.lang.Math.toRadians; +import static jdrafting.geom.JDMath.nearInt; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.util.HashSet; +import java.util.Set; + +import javax.swing.undo.AbstractUndoableEdit; + +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; + +/** + * Rotate selected shapes using mouse control + */ +public class RotationListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "rotation_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private Point2D center; + + public RotationListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_rotation1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( center != null ) + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // put center + if ( center == null ) + { + center = logicMouse; + app.setStatusText( getLocaleText( "txt_rotation2" ) ); + } + // modify shapes by rotation + else + { + final double ang = getAngle( logicMouse ); + final Shape[] rotatedShapes = getRotated( app.getSelectedShapes(), ang ); + int index = 0; + for ( final JDraftingShape jdshape : app.getSelectedShapes() ) + jdshape.setShape( rotatedShapes[index++] ); + + app.undoRedoSupport.postEdit( new EditRotation( + new HashSet<>( app.getSelectedShapes() ), ang ) ); + + app.scrollList.repaint(); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( center != null ) + { + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // get transform + final AffineTransform transform = canvas.getTransform(); + + // set tool style + g2.setStroke( new BasicStroke( 1f ) ); + g2.setColor( Application.toolMainColor ); + + // draw shapes rotated and center + final Point2D canCenter = transform.transform( center, null ); + g2.fillOval( nearInt( canCenter.getX() - 4 ), + nearInt( canCenter.getY() - 4 ), 8, 8 ); + for ( final Shape rotated : getRotated(app.getSelectedShapes(), getAngle(logicMouse)) ) + g2.draw( transform.createTransformedShape( rotated ) ); + + // draw angle info + final double ang = toDegrees( getAngle( logicMouse ) ); + if ( !Double.isNaN( ang ) ) + { + final int mouseX = mouse().getX(), mouseY = mouse().getY(); + final String angInfo = String.format( "%.2f", ang ) + "º"; + g2.setFont( new Font( Font.SERIF, Font.BOLD, 16 ) ); + g2.setColor( new Color( 40, 40, 180 ) ); + g2.drawString( angInfo, mouseX + 21, mouseY - 9 ); + g2.setColor( Color.LIGHT_GRAY ); + g2.drawString( angInfo, mouseX + 20, mouseY - 10 ); + } + } + } + + + // --- HELPERS --- + + // check modifiers + private boolean isFixedAngle() { return mouse().isShiftDown(); } + private boolean isIntegerAngle() { return mouse().isControlDown(); } + + /** + * Get rotation angle + * @param logicMouse + * @return the angle in radians (-pi to pi) + */ + private double getAngle( Point2D logicMouse ) + { + double ang = isFixedAngle() + ? toRadians( app.getAngle() ) + : atan2( logicMouse.getY() - center.getY(), + logicMouse.getX() - center.getX() ); + if ( isIntegerAngle() ) + ang = toRadians( rint( toDegrees( ang ) ) ); + + return ang; + } + + /** + * Get rotated shapes + * @param logicMouse mouse logic position + * @return a list of new shapes + */ + private Shape[] getRotated( Set selected, double ang ) + { + final AffineTransform rotation = AffineTransform.getRotateInstance( + ang, center.getX(), center.getY() ); + return selected + .stream() + .map( jdshape -> rotation.createTransformedShape( jdshape.getShape() ) ) + .toArray( Shape[]::new ); + } + + /** + * UndoableEdit for undo/redo rotations + */ + @SuppressWarnings("serial") + private class EditRotation extends AbstractUndoableEdit + { + private Set selected; + private double ang; + + private EditRotation( Set selected, double ang ) + { + this.selected = selected; + this.ang = ang; + } + + @Override + public void undo() + { + final Shape[] rotatedShapes = getRotated( selected, -ang ); + int index = 0; + for ( final JDraftingShape jdshape : selected ) + jdshape.setShape( rotatedShapes[index++] ); + } + + @Override + public void redo() + { + final Shape[] rotatedShapes = getRotated( selected, ang ); + int index = 0; + for ( final JDraftingShape jdshape : selected ) + jdshape.setShape( rotatedShapes[index++] ); + } + + @Override + public boolean canRedo() { return true; } + @Override + public boolean canUndo() { return true; } + + @Override + public String getPresentationName() + { + return String.format( "%s (%s shapes) (%.1fº)", + getLocaleText( "rotation" ), + selected.size(), + Math.toDegrees( ang ) ); + } + } +} diff --git a/src/jdrafting/gui/controller/mouse/RulerListener.java b/src/jdrafting/gui/controller/mouse/RulerListener.java new file mode 100644 index 0000000..47caaf4 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/RulerListener.java @@ -0,0 +1,97 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; + +import jdrafting.geom.JDStrokes; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; + +/** + * Capture a distance by mouse control + */ +public class RulerListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "ruler_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private Point2D start; + + public RulerListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_ruler1" ) ); + } + + @Override + public void mouseMoved(MouseEvent e) + { + super.mouseMoved( e ); + + if ( start != null ) + canvas.repaint(); + } + + @Override + public void mouseReleased(MouseEvent e) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + if ( start == null ) + { + start = logicMouse; + app.setStatusText( getLocaleText( "txt_ruler2" ) ); + } + else + { + // capture distance with ruler + double distance = start.distance( logicMouse ); + if ( add() ) + distance += app.getDistance(); + else if ( sub() ) + distance = Math.max( app.getDistance() - distance, Math.ulp( 0 ) ); + app.setDistance( distance ); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( start != null ) + { + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // draw line + g2.setStroke( JDStrokes.getStroke( JDStrokes.DASHED.getStroke(), 1f ) ); + g2.setColor( Application.toolMainColor ); + g2.draw( canvas.getTransform().createTransformedShape( + new Line2D.Double( start, logicMouse ) ) ); + } + } + + // --- HELPERS + + // check modifiers + private boolean add() { return mouse().isShiftDown(); } + private boolean sub() { return mouse().isControlDown(); } +} diff --git a/src/jdrafting/gui/controller/mouse/SegmentListener.java b/src/jdrafting/gui/controller/mouse/SegmentListener.java new file mode 100644 index 0000000..7b85a61 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/SegmentListener.java @@ -0,0 +1,158 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.geom.JDMath.adjustVectorToSize; +import static jdrafting.geom.JDMath.pointRelativeToCenter; +import static jdrafting.geom.JDMath.sumVectors; +import static jdrafting.geom.JDMath.vector; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; + +import jdrafting.geom.JDPoint; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +/** + * Create a segment using mouse control + */ +public class SegmentListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "segment_cursor.png" ); + private static final double ANGLE_INTERVAL = Math.PI / 4.; + private CanvasPanel canvas; + private Application app; + + private Point2D start; + + public SegmentListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_seg1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( start != null ) + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + if ( start == null ) + { + start = logicMouse; + app.setStatusText( getLocaleText( "txt_seg2" ) ); + canvas.repaint(); + } + else + { + // add shape to exercise + final Line2D line = getSegment( logicMouse ); + if ( addExtremes() ) + { + //////////////////////////// TRANSACTION //////////////////////////// + final JDCompoundEdit transaction = new JDCompoundEdit( + getLocaleText( "segment" ) + + (addExtremes() ? " (& extremes)" : "") ); + + // add segment + app.addShapeFromIterator( line.getPathIterator( null ), "", + getLocaleText( "new_segment" ), + app.getColor(), null, app.getStroke(), transaction ); + // add extremes + app.addShapeFromIterator( new JDPoint( line.getP1() ).getPathIterator( null ), "", + getLocaleText( "new_segment_extreme" ) + " 1", + app.getPointColor(), null, app.getPointStroke(), + transaction ); + app.addShapeFromIterator( new JDPoint( line.getP2() ).getPathIterator( null ), "", + getLocaleText( "new_segment_extreme" ) + " 2", + app.getPointColor(), null, app.getPointStroke(), + transaction ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + } + else // add segment + app.addShapeFromIterator( line.getPathIterator( null ), "", + getLocaleText( "new_segment" ), + app.getColor(), null, app.getStroke() ); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( start != null ) + { + // mouse position in logic viewport + Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // draw segment + g2.setStroke( new BasicStroke( 1f ) ); + g2.setColor( Application.toolMainColor ); + + g2.draw( canvas.getTransform().createTransformedShape( + getSegment( logicMouse ) ) ); + } + } + + + // --- HELPERS --- + + // check modifiers + private boolean isFixedAngle() { return mouse().isShiftDown(); } + private boolean addExtremes() { return mouse().isControlDown(); } + + /** + * Get segment in logic viewport + * @param logicMouse + * @return the segment + */ + private Line2D getSegment( Point2D logicMouse ) + { + Point2D end = logicMouse; + + // adjust to basic main angles + if ( isFixedAngle() ) + { + final double ang = Math.atan2( end.getY() - start.getY(), + end.getX() - start.getX() ), + newAng = ANGLE_INTERVAL * Math.round( ang / ANGLE_INTERVAL ); + end = pointRelativeToCenter( start, newAng, start.distance( end ) ); + } + + // fixed distance + if ( app.isUsingRuler() ) + return new Line2D.Double( start, + sumVectors( start, adjustVectorToSize( vector( start, end ), + app.getDistance() ) ) ); + // free distance + return new Line2D.Double( start, end ); + } +} diff --git a/src/jdrafting/gui/controller/mouse/SelectionListener.java b/src/jdrafting/gui/controller/mouse/SelectionListener.java new file mode 100644 index 0000000..e0673e7 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/SelectionListener.java @@ -0,0 +1,125 @@ +package jdrafting.gui.controller.mouse; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.HashSet; +import java.util.Set; + +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; + +/** + * Rectangular selection by mouse control + */ +public class SelectionListener extends AbstractCanvasMouseListener +{ + private CanvasPanel canvas; + private Application app; + + private Set originalSelection = null; + private Point2D start = null; + private Rectangle2D recSelection = null; + + public SelectionListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( new Cursor( Cursor.CROSSHAIR_CURSOR ) ); + + app.setStatusText( JDUtils.getLocaleText( "txt_sel1" ) ); + } + + @Override + public void mousePressed( MouseEvent e ) + { + super.mousePressed( e ); + + // set start and keep original selection or new + start = canvas.getInverseTransform().transform( e.getPoint(), null ); + originalSelection = e.isShiftDown() // add to previous or not + ? new HashSet<>( app.getSelectedShapes() ) + : new HashSet<>(); + } + + @Override + public void mouseDragged( MouseEvent e ) + { + super.mouseDragged( e ); + + // mouse position in logic viewport + final Point2D position = canvas.getInverseTransform().transform( e.getPoint(), null ); + + // get selection rectangle + final double minX = Math.min( start.getX(), position.getX() ), + maxX = Math.max( start.getX(), position.getX() ), + minY = Math.min( start.getY(), position.getY() ), + maxY = Math.max( start.getY(), position.getY() ); + recSelection = new Rectangle2D.Double( minX, minY, maxX - minX, maxY - minY ); + + // get selected shapes + app.setSelectedShapes( new HashSet<>( originalSelection ) ); + for ( final JDraftingShape jdshape : app.getExercise().getFramesUntilIndex() ) + { + // select if all area contained inside selection + if ( allArea() ) + { + if ( recSelection.contains( jdshape.getShape().getBounds2D() ) ) + app.getSelectedShapes().add( jdshape ); + } + // select if shape intersects selection + else + for ( final Line2D segment : jdshape.getSegments() ) + if ( segment.intersects( recSelection ) ) + { + app.getSelectedShapes().add( jdshape ); + break; + } + } + + // refresh cursor and canvas + canvas.setCursor( (position.getX() - start.getX()) * (position.getY() - start.getY()) > 0 + ? new Cursor( Cursor.NE_RESIZE_CURSOR ) + : new Cursor( Cursor.NW_RESIZE_CURSOR ) ); + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + app.scrollList.repaint(); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + + @Override + public void paintTool( Graphics2D g2 ) + { + // draw selection rectangle + if ( recSelection != null ) + { + g2.setColor( RECTANGLE_COLOR ); + g2.setStroke( RECTANGLE_STROKE ); + g2.draw( canvas.getTransform().createTransformedShape( recSelection ) ); + } + } + + private boolean allArea() { return mouse().isControlDown(); } + + private static final Color RECTANGLE_COLOR = new Color( 10, 160, 10 ); + private static final BasicStroke RECTANGLE_STROKE = new BasicStroke( + 1.5f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 1f, new float[] { 7f, 5f }, 0f ); +} diff --git a/src/jdrafting/gui/controller/mouse/SplineListener.java b/src/jdrafting/gui/controller/mouse/SplineListener.java new file mode 100644 index 0000000..bc612c5 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/SplineListener.java @@ -0,0 +1,202 @@ +package jdrafting.gui.controller.mouse; + +import static java.lang.Math.PI; +import static jdrafting.geom.JDMath.mulVector; +import static jdrafting.geom.JDMath.pointRelativeToCenter; +import static jdrafting.geom.JDMath.sumVectors; +import static jdrafting.geom.JDMath.vector; +import static jdrafting.geom.JDMath.vectorArg; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.CubicCurve2D; +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; +import java.util.LinkedList; +import java.util.List; + +import jdrafting.geom.JDMath; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; + +/** + * Creates a cubic spline using mouse control + * @version 0.1.11.1 + */ +public class SplineListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "spline_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private List points = new LinkedList<>(); + + public SplineListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( JDUtils.getLocaleText( "txt_poly" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // finish curve capture + if ( e.getClickCount() == 2 ) + { + final Object[] data = getSpline( points ); + final Path2D spline = (Path2D) data[0]; + + // add spline to exercise + final double flatness = JDMath.length( spline, null ) / app.getFlatnessValue(); + app.addShapeFromIterator( spline.getPathIterator( null, flatness ), "", + getLocaleText( "new_spline" ), app.getColor(), null, + app.getStroke() ); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + // add new point to curve + else + points.add( logicMouse ); + + canvas.repaint(); + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( points.isEmpty() ) return; + + final AffineTransform transform = canvas.getTransform(); + + // mouse position on logic viewport + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // knots plus logicMouse + final List _points = new LinkedList<>( points ); + _points.add( logicMouse ); + + // set style + g2.setStroke( new BasicStroke( 1f ) ); + g2.setColor( Application.toolMainColor ); + + // draw spline + final Object[] data = getSpline( _points ); + g2.draw( transform.createTransformedShape( (Path2D) data[0] ) ); + /* + // draw control points + g2.setColor( Color.LIGHT_GRAY ); + for ( int i = 1; i < points.size(); i++ ) + g2.draw( transform.createTransformedShape( new Line2D.Double( points.get( i - 1 ), points.get( i ) ) ) ); + Point2D[][] ctrlPoints = (Point2D[][]) data[1]; + Iterator it = _points.iterator(); + for ( Point2D[] pair : ctrlPoints ) + { + Point2D p; + Point2D point = transform.transform( it.next(), null ); + if ( pair[0] != null ) + { + p = transform.transform( pair[0], null ); + g2.setColor( Color.BLACK ); + g2.draw( new Line2D.Double( point.getX(), point.getY(), p.getX(), p.getY() ) ); + g2.setColor( Color.RED ); + g2.fill( new Rectangle2D.Double( p.getX() - 3, p.getY() - 3, 6, 6 ) ); + } + if ( pair[1] != null ) + { + p = transform.transform( pair[1], null ); + g2.setColor( Color.BLACK ); + g2.draw( new Line2D.Double( point.getX(), point.getY(), p.getX(), p.getY() ) ); + g2.setColor( Color.RED ); + g2.fill( new Ellipse2D.Double( p.getX() - 4, p.getY() - 4, 8, 8 ) ); + } + }*/ + } + + + // --- HELPERS + + /** + * Create an spline from a set of points + * Note: the spline doesn't adjust control points globally + * and it doesn't follow any known algorythm (I think) + * TODO improve the spline algorythm + * @param points knots + * @return the spline as a path and an nx2 array with control points + */ + private Object[] getSpline( List points ) + { + final Path2D path = new Path2D.Double(); + final CubicCurve2D arc = new CubicCurve2D.Double(); + final double ratio = 3.; + final int n = points.size(); + final Point2D[][] ctrlPoints = new Point2D[n][2]; + + ctrlPoints[0] = new Point2D[] { + null, + sumVectors( points.get( 0 ), mulVector( + vector( points.get( 0 ), points.get( 1 ) ), 1. / ratio ) ) }; + ctrlPoints[ n - 1 ] = new Point2D[] { + sumVectors( points.get( n - 1 ), mulVector( + vector( points.get( n - 2 ), + points.get( n - 1 ) ), -1. / ratio ) ), + null }; + + for ( int i = 1; i < n - 1; i++ ) + { + // three consecutive points + final Point2D p[] = new Point2D[] { points.get( i - 1 ), points.get( i ), + points.get( i + 1 ) }; + + // calculate control points + final Point2D v1 = vector( p[0], p[1] ), + v2 = vector( p[1], p[2] ); + final double ang1 = vectorArg( v1 ), + ang2 = vectorArg( v2 ); + double ang; + if ( Math.abs( ang2 - ang1 ) > PI ) + ang = ( ang1 + ang2 ) / 2. + PI; + else + ang = ( ang1 + ang2 ) / 2.; + ctrlPoints[i][0] = pointRelativeToCenter( + p[1], ang + PI, v1.distance( 0, 0 ) / ratio ); + ctrlPoints[i][1] = pointRelativeToCenter( + p[1], ang, v2.distance( 0, 0 ) / ratio ); + + // add cubic arc to path + arc.setCurve( p[0], ctrlPoints[ i - 1 ][1], ctrlPoints[ i ][0], p[1] ); + path.append( arc.getPathIterator(null), true ); + } + // add last cubic arc + arc.setCurve( points.get( n - 2 ), ctrlPoints[ n - 2 ][1], + ctrlPoints[ n - 1 ][0], points.get( n - 1 ) ); + path.append( arc.getPathIterator(null), true ); + + return new Object[] { path, ctrlPoints }; + } + +} diff --git a/src/jdrafting/gui/controller/mouse/TextBoxListener.java b/src/jdrafting/gui/controller/mouse/TextBoxListener.java new file mode 100644 index 0000000..3f28d9a --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/TextBoxListener.java @@ -0,0 +1,269 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.BorderLayout; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.GraphicsEnvironment; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.KeyStroke; + +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; + +/** + * Create a text box for comments + */ +public class TextBoxListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "text_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private Point2D start, // upper-left corner + end; // lower-right corner + + public TextBoxListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_comment1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( start == null ) return; + + // dynamic cursor + final Point2D pos = canvas.getInverseTransform().transform( e.getPoint(), null ); + canvas.setCursor( new Cursor( + ( pos.getX() - start.getX() ) * ( pos.getY() - start.getY() ) > 0 + ? Cursor.NE_RESIZE_CURSOR + : Cursor.NW_RESIZE_CURSOR ) ); + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // put first corner + if ( start == null ) + { + start = logicMouse; + //app.setStatusText( getLocaleText( "txt_rect2" ) ); + } + // finish rectangle + else + { + end = logicMouse; + showTxtDialog(); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + + canvas.repaint(); + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( start != null ) + { + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // set style + g2.setStroke( new BasicStroke( 1f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL ) ); + g2.setColor( Application.toolMainColor ); + + // draw rectangle + g2.draw( canvas.getTransform().createTransformedShape( getRectangle( logicMouse ) ) ); + } + } + + // --- HELPERS --- + + // check modifiers + //private boolean isSquare() { return mouse().isShiftDown(); } + + @SuppressWarnings("serial") + private void showTxtDialog() + { + // dialog for comment and font family-style + final JDialog dialog = new JDialog( app, true ); + dialog.setTitle( getLocaleText( "comment_des" ) ); + + // content panel + final JPanel panel = new JPanel( new BorderLayout() ); + panel.setBorder( BorderFactory.createEmptyBorder( 6, 6, 6, 6 ) ); + dialog.getContentPane().add( panel ); + + final Box box = Box.createVerticalBox(); + panel.add( box ); + + // text area for comment + final JTextArea textArea = new JTextArea( 8, 50 ); + JScrollPane scrollArea = new JScrollPane( textArea ); + scrollArea.setAlignmentX( JLabel.LEFT_ALIGNMENT ); + box.add( scrollArea ); + + box.add( Box.createRigidArea( new Dimension( 0, 10 ) ) ); + + // family-style panel + final JPanel formatPanel = new JPanel( new FlowLayout( FlowLayout.CENTER ) ); + formatPanel.setAlignmentX( JLabel.LEFT_ALIGNMENT ); + formatPanel.setMaximumSize( + new Dimension( Integer.MAX_VALUE, scrollArea.getPreferredSize().height ) ); + box.add( formatPanel ); + + // get font family names + final GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment(); + final String[] fontNames = genv.getAvailableFontFamilyNames( app.getLocale() ); + + // combo box for family names + final JComboBox comboFont = new JComboBox<>(); + for ( final String fontName : fontNames ) + comboFont.addItem( fontName ); + comboFont.setSelectedItem( "Monospaced" ); + formatPanel.add( comboFont ); + + // checkboxes for bold & italic + final JCheckBox checkBold = new JCheckBox( "Bold" ), + checkItalic = new JCheckBox( "Italic" ); + formatPanel.add( checkBold ); + formatPanel.add( checkItalic ); + + box.add( Box.createRigidArea( new Dimension( 0, 10 ) ) ); + + // panel for ok/cancel buttons + final JPanel btnPanel = new JPanel( new FlowLayout( FlowLayout.CENTER ) ); + btnPanel.setAlignmentX( JLabel.LEFT_ALIGNMENT ); + btnPanel.setMaximumSize( + new Dimension( Integer.MAX_VALUE, scrollArea.getPreferredSize().height ) ); + + box.add( btnPanel ); + + // ok button + final JButton btnOk = new JButton( getLocaleText( "save_close" ) ); + btnPanel.add( btnOk ); + Action okAction = new AbstractAction() { + @Override + public void actionPerformed( ActionEvent e ) + { + // create new comment + final JDraftingShape shape = app.addShapeFromIterator( + getRectangle(end).getPathIterator( null ), + "", textArea.getText(), + app.getColor(), null, app.getStroke() ); + shape.setAsText( true ); + // get style + int fontStyle = Font.PLAIN; + if ( checkBold.isSelected() ) + { + if ( checkItalic.isSelected() ) + fontStyle = Font.BOLD | Font.ITALIC; + else + fontStyle = Font.BOLD; + } + else if ( checkItalic.isSelected() ) + fontStyle = Font.ITALIC; + // set font from dialog + shape.setFont( new Font(comboFont.getSelectedItem().toString(), fontStyle, 1000) ); + // close dialog + dialog.setVisible( false ); + dialog.dispose(); + } + }; + btnOk.addActionListener( okAction ); + // cancel button + final JButton btnCancel = new JButton( getLocaleText( "cancel" ) ); + btnPanel.add( btnCancel ); + final Action cancelAction = new AbstractAction() { + @Override + public void actionPerformed( ActionEvent e ) + { + dialog.setVisible( false ); + dialog.dispose(); + } + }; + btnCancel.addActionListener( cancelAction ); + + // ESCAPE + dialog.getRootPane().getInputMap( JComponent.WHEN_IN_FOCUSED_WINDOW ) + .put( KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, 0 ), "ESCAPE" ); + dialog.getRootPane().getActionMap().put( "ESCAPE", cancelAction ); + // ENTER + dialog.getRootPane().getInputMap( JComponent.WHEN_IN_FOCUSED_WINDOW ) + .put( KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0 ), "ENTER" ); + dialog.getRootPane().getActionMap().put( "ENTER", okAction ); + + // window + dialog.setDefaultCloseOperation( JDialog.DISPOSE_ON_CLOSE ); + dialog.pack(); + dialog.setVisible( true ); + dialog.setLocationRelativeTo( app ); + } + + /** + * Gets the rectangle + * @param logicMouse + * @return the rectangle in the logic viewport + */ + private Rectangle2D getRectangle( Point2D logicMouse ) + { + // calculate coords + double x = app.isUsingRuler() + ? start.getX() > logicMouse.getX() + ? start.getX() - app.getDistance() + : start.getX() + app.getDistance() + : logicMouse.getX(), + minX = Math.min( start.getX(), x ), + maxX = Math.max( start.getX(), x ), + minY = Math.min( start.getY(), logicMouse.getY() ), + maxY = Math.max( start.getY(), logicMouse.getY() ); + + // create rectangle + final Rectangle2D rect = new Rectangle2D.Double( minX, minY, maxX - minX, maxY - minY ); + + return rect; + } +} diff --git a/src/jdrafting/gui/controller/mouse/TranslationListener.java b/src/jdrafting/gui/controller/mouse/TranslationListener.java new file mode 100644 index 0000000..5150c50 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/TranslationListener.java @@ -0,0 +1,195 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.geom.JDMath.adjustVectorToSize; +import static jdrafting.geom.JDMath.sumVectors; +import static jdrafting.geom.JDMath.vector; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.util.HashSet; +import java.util.Set; + +import javax.swing.undo.AbstractUndoableEdit; + +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; + +/** + * Translate selected shapes using mouse control + */ +public class TranslationListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "translation_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private Point2D start; + + public TranslationListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_translate1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( start != null ) + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // put start + if ( start == null ) + { + start = logicMouse; + app.setStatusText( getLocaleText( "txt_translate2" ) ); + } + // modify shapes by translation + else + { + final Point2D vt = getVector( logicMouse ); + final Shape[] translatedShapes = + getTranslated( app.getSelectedShapes(), vt.getX(), vt.getY() ); + int index = 0; + for ( final JDraftingShape jdshape : app.getSelectedShapes() ) + jdshape.setShape( translatedShapes[index++] ); + + app.undoRedoSupport.postEdit( new EditTranslation( + new HashSet<>( app.getSelectedShapes() ), + vt.getX(), vt.getY() ) ); + + app.scrollList.repaint(); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( start != null ) + { + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // get transform + final AffineTransform transform = canvas.getTransform(); + + // set tool style + g2.setStroke( new BasicStroke( 1f ) ); + g2.setColor( Color.RED ); + + // draw shapes translated and direction vector + final Point2D vt = getVector( logicMouse ); + g2.draw( transform.createTransformedShape( + new Line2D.Double( start, sumVectors( start, vt ) ) ) ); + g2.setColor( Application.toolMainColor ); + for ( final Shape translated : getTranslated( app.getSelectedShapes(), + vt.getX(), vt.getY() ) ) + g2.draw( transform.createTransformedShape( translated ) ); + } + } + + + // --- HELPERS --- + + /** + * Get translation vector + * @param logicMouse + * @return the translation vector + */ + private Point2D getVector( Point2D logicMouse ) + { + return app.isUsingRuler() + ? adjustVectorToSize( vector( start, logicMouse ), app.getDistance() ) + : vector( start, logicMouse ); + } + + /** + * Get rotated shapes + * @param logicMouse mouse logic position + * @return a list of new shapes + */ + private Shape[] getTranslated( Set selected, double tx, double ty ) + { + final AffineTransform translation = AffineTransform.getTranslateInstance( tx, ty ); + + return selected + .stream() + .map( jdshape -> translation.createTransformedShape( jdshape.getShape() ) ) + .toArray( Shape[]::new ); + } + + /** + * UndoableEdit for undo/redo rotations + */ + @SuppressWarnings("serial") + private class EditTranslation extends AbstractUndoableEdit + { + private Set selected; + private double tx, ty; + + private EditTranslation( Set selected, double tx, double ty ) + { + this.selected = selected; + this.tx = tx; + this.ty = ty; + } + + @Override + public void undo() + { + final Shape[] rotatedShapes = getTranslated( selected, -tx, -ty ); + int index = 0; + for ( final JDraftingShape jdshape : selected ) + jdshape.setShape( rotatedShapes[index++] ); + } + + @Override + public void redo() + { + final Shape[] rotatedShapes = getTranslated( selected, tx, ty ); + int index = 0; + for ( final JDraftingShape jdshape : selected ) + jdshape.setShape( rotatedShapes[index++] ); + } + + @Override + public boolean canRedo() { return true; } + @Override + public boolean canUndo() { return true; } + + @Override + public String getPresentationName() + { + return getLocaleText( "translation" ) + " (" + selected.size() + " shapes)"; + } + } +} diff --git a/src/jdrafting/gui/controller/mouse/TriangleListener.java b/src/jdrafting/gui/controller/mouse/TriangleListener.java new file mode 100644 index 0000000..7f0efe1 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/TriangleListener.java @@ -0,0 +1,153 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.geom.JDMath.adjustVectorToSize; +import static jdrafting.geom.JDMath.linesIntersection; +import static jdrafting.geom.JDMath.midpoint; +import static jdrafting.geom.JDMath.normal; +import static jdrafting.geom.JDMath.sumVectors; +import static jdrafting.geom.JDMath.vector; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; + +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; + +/** + * Create triangle using mouse control + */ +public class TriangleListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "triangle_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + private Point2D A, B; + + public TriangleListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_triangle_points1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( A != null ) + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + // first vertex + if ( A == null ) + A = logicMouse; + // second vertex + else if ( B == null ) + B = logicMouse; + else + { + // create triangle + final Path2D triangle = getTriangle( logicMouse ); + + // add triangle to exercise + app.addShapeFromIterator( triangle.getPathIterator( null ), "", + getLocaleText( "new_triangle" ), + app.getColor(), null, app.getStroke() ); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( A == null ) return; + + final AffineTransform transform = canvas.getTransform(); + + // mouse position on logic viewport + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + // set tool style + g2.setColor( Application.toolMainColor ); + g2.setStroke( new BasicStroke( 1f ) ); + + // draw triangle + g2.draw( transform.createTransformedShape( getTriangle( logicMouse ) ) ); + } + + // --- HELPERS + + // check modifiers + private boolean isRectangle() { return mouse().isControlDown(); } + private boolean isEquilateral() { return mouse().isShiftDown(); } + + private Path2D getTriangle( Point2D logicMouse ) + { + if ( A == null ) return null; + + final Path2D triangle = new Path2D.Double(); + + triangle.moveTo( A.getX(), A.getY() ); + if ( B == null ) + { + triangle.lineTo( logicMouse.getX(), logicMouse.getY() ); + return triangle; // return line (A,mouse) + } + triangle.lineTo( B.getX(), B.getY() ); + if ( isRectangle() ) + { + final Point2D v = vector( A, B ), + n = normal( v ); + Point2D p; + if ( logicMouse.distance( A ) < logicMouse.distance( B ) ) + p = A; + else + p = B; + final Point2D C = linesIntersection( p, sumVectors( p, n ), logicMouse, + sumVectors( logicMouse, v ) ); + triangle.lineTo( C.getX(), C.getY() ); + } + else if ( isEquilateral() ) + { + final double factor = Line2D.Double.relativeCCW( + A.getX(), A.getY(), B.getX(), B.getY(), + logicMouse.getX(), logicMouse.getY() ) < 0 + ? Math.sqrt( 3 ) / 2. + : -Math.sqrt( 3 ) / 2.; + final Point2D v = vector( A, B ), + n = adjustVectorToSize( normal( v ), v.distance( 0, 0 ) * factor ), + C = sumVectors( midpoint( A, B ), n ); + triangle.lineTo( C.getX(), C.getY() ); + } + else + triangle.lineTo( logicMouse.getX(), logicMouse.getY() ); + triangle.lineTo( A.getX(), A.getY() ); + + return triangle; + } +} diff --git a/src/jdrafting/gui/controller/mouse/TrianglePointsListener.java b/src/jdrafting/gui/controller/mouse/TrianglePointsListener.java new file mode 100644 index 0000000..9431b14 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/TrianglePointsListener.java @@ -0,0 +1,184 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.geom.JDMath.baricenter; +import static jdrafting.geom.JDMath.circumcenter; +import static jdrafting.geom.JDMath.incenter; +import static jdrafting.geom.JDMath.ortocenter; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.JOptionPane; + +import jdrafting.geom.JDPoint; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDUtils; + +/** + * Create bari/orto/circum/in-center using mouse control + */ +public class TrianglePointsListener extends AbstractCanvasMouseListener +{ + private static final Map CURSOR; + public static final int BARICENTER = 0; + public static final int INCENTER = 1; + public static final int CIRCUMCENTER = 2; + public static final int ORTOCENTER = 3; + private CanvasPanel canvas; + private Application app; + + private int type; + private Point2D A, B; + + static { + CURSOR = new HashMap<>(); + CURSOR.put( BARICENTER, JDUtils.getCustomCursor( "baricenter_cursor.png" ) ); + CURSOR.put( INCENTER, JDUtils.getCustomCursor( "incenter_cursor.png" ) ); + CURSOR.put( CIRCUMCENTER, JDUtils.getCustomCursor( "circumcenter_cursor.png" ) ); + CURSOR.put( ORTOCENTER, JDUtils.getCustomCursor( "ortocenter_cursor.png" ) ); + } + + public TrianglePointsListener( CanvasPanel canvas, int type ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + this.type = type; + + canvas.setCursor( CURSOR.get( type ) ); + + app.setStatusText( getLocaleText( "txt_triangle_points2" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + if ( A != null ) + canvas.repaint(); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + // mouse position in logic viewport + final Point2D logicMouse = canvas.adjustToPoint( e.getPoint() ); + + if ( A == null ) + A = logicMouse; + else if ( B == null ) + B = logicMouse; + else + { + final Point2D point = getPoint( logicMouse ); + if ( point == null ) + JOptionPane.showMessageDialog( app, "null point", + "Error in triangle", JOptionPane.ERROR_MESSAGE ); + else + { + final Color color = isPointColor() + ? app.getPointColor() + : app.getColor(); + final BasicStroke stroke = isPointColor() + ? app.getPointStroke() + : app.getStroke(); + + app.addShapeFromIterator( new JDPoint( point ).getPathIterator( null ), "", + "> " + getName( type ), color, null, stroke ); + } + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + + @Override + public void paintTool( Graphics2D g2 ) + { + if ( A == null ) return; + + final AffineTransform transform = canvas.getTransform(); + + // mouse position on logic viewport + final Point2D logicMouse = canvas.adjustToPoint( mouse().getPoint() ); + + g2.setColor( Application.toolMainColor ); + g2.setStroke( new BasicStroke( 1f ) ); + + if ( B == null ) + { + g2.draw( transform.createTransformedShape( new Line2D.Double( A, logicMouse ) ) ); + return; + } + else + g2.draw( transform.createTransformedShape( new Line2D.Double( A, B ) ) ); + + g2.draw( transform.createTransformedShape( new Line2D.Double( A, logicMouse ) ) ); + g2.draw( transform.createTransformedShape( new Line2D.Double( B, logicMouse ) ) ); + + // draw temporary triangle point + final Point2D point = getPoint( logicMouse ); + if ( point != null ) + { + transform.transform( point, point ); + g2.fill( new Ellipse2D.Double( point.getX() - 4, point.getY() - 4, 8, 8 ) ); + } + } + + // --- HELPERS --- + + private boolean isPointColor() { return !mouse().isShiftDown(); } + + /** + * Get action triangle point + * @param logicMouse mouse logic position + * @return the suitable point + */ + private Point2D getPoint( Point2D logicMouse ) + { + switch( type ) + { + case INCENTER: return incenter( A, B, logicMouse ); + case ORTOCENTER: return ortocenter( A, B, logicMouse ); + case BARICENTER: return baricenter( A, B, logicMouse ); + case CIRCUMCENTER: return circumcenter( A, B, logicMouse ); + default: return null; + } + } + + /** + * Get action name + * @param type point type (defined constants) + * @return an action string name in locale lenguage + */ + public static String getName( int type ) + { + switch ( type ) + { + case TrianglePointsListener.INCENTER: + return getLocaleText( "incenter" ); + case TrianglePointsListener.ORTOCENTER: + return getLocaleText( "ortocenter" ); + case TrianglePointsListener.CIRCUMCENTER: + return getLocaleText( "circumcenter" ); + case TrianglePointsListener.BARICENTER: + return getLocaleText( "baricenter" ); + default: return "Triangle notable points"; + } + } +} diff --git a/src/jdrafting/gui/controller/mouse/VertexListener.java b/src/jdrafting/gui/controller/mouse/VertexListener.java new file mode 100644 index 0000000..3628eb8 --- /dev/null +++ b/src/jdrafting/gui/controller/mouse/VertexListener.java @@ -0,0 +1,105 @@ +package jdrafting.gui.controller.mouse; + +import static jdrafting.gui.JDUtils.elvis; +import static jdrafting.gui.JDUtils.getLocaleText; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; +import java.util.List; + +import jdrafting.geom.JDPoint; +import jdrafting.geom.JDraftingShape; +import jdrafting.gui.Application; +import jdrafting.gui.CanvasPanel; +import jdrafting.gui.JDCompoundEdit; +import jdrafting.gui.JDUtils; + +public class VertexListener extends AbstractCanvasMouseListener +{ + private static final Cursor CURSOR = JDUtils.getCustomCursor( "vertex_cursor.png" ); + private CanvasPanel canvas; + private Application app; + + public VertexListener( CanvasPanel canvas ) + { + super( canvas ); + + this.canvas = canvas; + app = canvas.getApplication(); + + canvas.setCursor( CURSOR ); + + app.setStatusText( getLocaleText( "txt_vtx1" ) ); + } + + @Override + public void mouseMoved( MouseEvent e ) + { + super.mouseMoved( e ); + + canvas.setCursor( canvas.getShapeAtCanvasPoint( e.getPoint() ) == null + ? CURSOR + : new Cursor( Cursor.HAND_CURSOR ) ); + } + + @Override + public void mouseReleased( MouseEvent e ) + { + super.mouseReleased( e ); + + final JDraftingShape jdshape = canvas.getShapeAtCanvasPoint( e.getPoint() ); + + if ( jdshape != null ) + { + // point style + final Color color = isPointStyle() + ? app.getPointColor() + : app.getColor(); + final BasicStroke stroke = isPointStyle() + ? app.getPointStroke() + : app.getStroke(); + + // get shape vertex + List vertex = jdshape.getVertex(); + if ( jdshape.isClosed( vertex ) ) + vertex = vertex.subList( 0, vertex.size() - 1 ); + + final String descHtml = String.format( "[%s]", + Application.HTML_SHAPE_NAMES_COL, + elvis( jdshape.getName(), "?" ) ); + + //////////////////////////// TRANSACTION //////////////////////////// + @SuppressWarnings("serial") + final JDCompoundEdit transaction = new JDCompoundEdit() { + public String getPresentationName() + { + return getLocaleText( "vertex" ) + " " + descHtml + + " (" + edits.size() + " points)"; + } + }; + + // add points to every vertex + vertex + .stream() + .forEach( point -> app.addShapeFromIterator( + new JDPoint( point ).getPathIterator( null ), "", + getLocaleText( "new_vertex" ) + " " + descHtml, color, null, + stroke, transaction ) ); + + transaction.end(); + app.undoRedoSupport.postEdit( transaction ); + ///////////////////////////////////////////////////////////////////// + + // refresh + app.scrollList.repaint(); + + // back to select mode + canvas.setCanvasListener( new HandListener( canvas ) ); + } + } + + private boolean isPointStyle() { return !mouse().isShiftDown(); } +} diff --git a/src/jdrafting/resources/images/angle.png b/src/jdrafting/resources/images/angle.png new file mode 100644 index 0000000..7615a1c Binary files /dev/null and b/src/jdrafting/resources/images/angle.png differ diff --git a/src/jdrafting/resources/images/arc.png b/src/jdrafting/resources/images/arc.png new file mode 100644 index 0000000..257034f Binary files /dev/null and b/src/jdrafting/resources/images/arc.png differ diff --git a/src/jdrafting/resources/images/area_intersection.png b/src/jdrafting/resources/images/area_intersection.png new file mode 100644 index 0000000..109589f Binary files /dev/null and b/src/jdrafting/resources/images/area_intersection.png differ diff --git a/src/jdrafting/resources/images/area_substract.png b/src/jdrafting/resources/images/area_substract.png new file mode 100644 index 0000000..db15807 Binary files /dev/null and b/src/jdrafting/resources/images/area_substract.png differ diff --git a/src/jdrafting/resources/images/area_symmetric_substract.png b/src/jdrafting/resources/images/area_symmetric_substract.png new file mode 100644 index 0000000..aa8c787 Binary files /dev/null and b/src/jdrafting/resources/images/area_symmetric_substract.png differ diff --git a/src/jdrafting/resources/images/area_union.png b/src/jdrafting/resources/images/area_union.png new file mode 100644 index 0000000..8df53f3 Binary files /dev/null and b/src/jdrafting/resources/images/area_union.png differ diff --git a/src/jdrafting/resources/images/arrow.png b/src/jdrafting/resources/images/arrow.png new file mode 100644 index 0000000..4f973a0 Binary files /dev/null and b/src/jdrafting/resources/images/arrow.png differ diff --git a/src/jdrafting/resources/images/axial_symmetry.png b/src/jdrafting/resources/images/axial_symmetry.png new file mode 100644 index 0000000..942eabf Binary files /dev/null and b/src/jdrafting/resources/images/axial_symmetry.png differ diff --git a/src/jdrafting/resources/images/backcolor.png b/src/jdrafting/resources/images/backcolor.png new file mode 100644 index 0000000..0a9a461 Binary files /dev/null and b/src/jdrafting/resources/images/backcolor.png differ diff --git a/src/jdrafting/resources/images/backward.png b/src/jdrafting/resources/images/backward.png new file mode 100644 index 0000000..4d83266 Binary files /dev/null and b/src/jdrafting/resources/images/backward.png differ diff --git a/src/jdrafting/resources/images/baricenter.png b/src/jdrafting/resources/images/baricenter.png new file mode 100644 index 0000000..3f797bf Binary files /dev/null and b/src/jdrafting/resources/images/baricenter.png differ diff --git a/src/jdrafting/resources/images/bisectrix.png b/src/jdrafting/resources/images/bisectrix.png new file mode 100644 index 0000000..87f0f53 Binary files /dev/null and b/src/jdrafting/resources/images/bisectrix.png differ diff --git a/src/jdrafting/resources/images/bounds.png b/src/jdrafting/resources/images/bounds.png new file mode 100644 index 0000000..b016a64 Binary files /dev/null and b/src/jdrafting/resources/images/bounds.png differ diff --git a/src/jdrafting/resources/images/capable_arc.png b/src/jdrafting/resources/images/capable_arc.png new file mode 100644 index 0000000..f22f4a7 Binary files /dev/null and b/src/jdrafting/resources/images/capable_arc.png differ diff --git a/src/jdrafting/resources/images/central_symmetry.png b/src/jdrafting/resources/images/central_symmetry.png new file mode 100644 index 0000000..c294a85 Binary files /dev/null and b/src/jdrafting/resources/images/central_symmetry.png differ diff --git a/src/jdrafting/resources/images/circumcenter.png b/src/jdrafting/resources/images/circumcenter.png new file mode 100644 index 0000000..ff6382e Binary files /dev/null and b/src/jdrafting/resources/images/circumcenter.png differ diff --git a/src/jdrafting/resources/images/circumference.png b/src/jdrafting/resources/images/circumference.png new file mode 100644 index 0000000..bd45c85 Binary files /dev/null and b/src/jdrafting/resources/images/circumference.png differ diff --git a/src/jdrafting/resources/images/color.png b/src/jdrafting/resources/images/color.png new file mode 100644 index 0000000..00c056b Binary files /dev/null and b/src/jdrafting/resources/images/color.png differ diff --git a/src/jdrafting/resources/images/conics.png b/src/jdrafting/resources/images/conics.png new file mode 100644 index 0000000..e45096f Binary files /dev/null and b/src/jdrafting/resources/images/conics.png differ diff --git a/src/jdrafting/resources/images/copy.png b/src/jdrafting/resources/images/copy.png new file mode 100644 index 0000000..839bb99 Binary files /dev/null and b/src/jdrafting/resources/images/copy.png differ diff --git a/src/jdrafting/resources/images/cursors/angle_cursor.png b/src/jdrafting/resources/images/cursors/angle_cursor.png new file mode 100644 index 0000000..1b1a488 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/angle_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/arc_cursor.png b/src/jdrafting/resources/images/cursors/arc_cursor.png new file mode 100644 index 0000000..6452dda Binary files /dev/null and b/src/jdrafting/resources/images/cursors/arc_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/area_substract_cursor.png b/src/jdrafting/resources/images/cursors/area_substract_cursor.png new file mode 100644 index 0000000..5c714bf Binary files /dev/null and b/src/jdrafting/resources/images/cursors/area_substract_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/arrow_cursor.png b/src/jdrafting/resources/images/cursors/arrow_cursor.png new file mode 100644 index 0000000..71bacda Binary files /dev/null and b/src/jdrafting/resources/images/cursors/arrow_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/axial_symmetry_cursor.png b/src/jdrafting/resources/images/cursors/axial_symmetry_cursor.png new file mode 100644 index 0000000..8accd17 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/axial_symmetry_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/baricenter_cursor.png b/src/jdrafting/resources/images/cursors/baricenter_cursor.png new file mode 100644 index 0000000..56674a3 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/baricenter_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/bisectrix_cursor.png b/src/jdrafting/resources/images/cursors/bisectrix_cursor.png new file mode 100644 index 0000000..f63c35c Binary files /dev/null and b/src/jdrafting/resources/images/cursors/bisectrix_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/bounds_cursor.png b/src/jdrafting/resources/images/cursors/bounds_cursor.png new file mode 100644 index 0000000..de1d49b Binary files /dev/null and b/src/jdrafting/resources/images/cursors/bounds_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/capable_arc_cursor.png b/src/jdrafting/resources/images/cursors/capable_arc_cursor.png new file mode 100644 index 0000000..bb83284 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/capable_arc_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/central_symmetry_cursor.png b/src/jdrafting/resources/images/cursors/central_symmetry_cursor.png new file mode 100644 index 0000000..1c15aab Binary files /dev/null and b/src/jdrafting/resources/images/cursors/central_symmetry_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/circumcenter_cursor.png b/src/jdrafting/resources/images/cursors/circumcenter_cursor.png new file mode 100644 index 0000000..861207d Binary files /dev/null and b/src/jdrafting/resources/images/cursors/circumcenter_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/circumference_cursor.png b/src/jdrafting/resources/images/cursors/circumference_cursor.png new file mode 100644 index 0000000..d877f7e Binary files /dev/null and b/src/jdrafting/resources/images/cursors/circumference_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/divisions_cursor.png b/src/jdrafting/resources/images/cursors/divisions_cursor.png new file mode 100644 index 0000000..c191929 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/divisions_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/dragging_cursor.png b/src/jdrafting/resources/images/cursors/dragging_cursor.png new file mode 100644 index 0000000..7752229 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/dragging_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/ellipse_cursor.png b/src/jdrafting/resources/images/cursors/ellipse_cursor.png new file mode 100644 index 0000000..a9c82f2 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/ellipse_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/extremes_cursor.png b/src/jdrafting/resources/images/cursors/extremes_cursor.png new file mode 100644 index 0000000..694d99d Binary files /dev/null and b/src/jdrafting/resources/images/cursors/extremes_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/eyedropper_cursor.png b/src/jdrafting/resources/images/cursors/eyedropper_cursor.png new file mode 100644 index 0000000..b8f9a5f Binary files /dev/null and b/src/jdrafting/resources/images/cursors/eyedropper_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/free_hand_down_cursor.png b/src/jdrafting/resources/images/cursors/free_hand_down_cursor.png new file mode 100644 index 0000000..bfd0217 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/free_hand_down_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/free_hand_up_cursor.png b/src/jdrafting/resources/images/cursors/free_hand_up_cursor.png new file mode 100644 index 0000000..88a9c8e Binary files /dev/null and b/src/jdrafting/resources/images/cursors/free_hand_up_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/hammer_cursor.png b/src/jdrafting/resources/images/cursors/hammer_cursor.png new file mode 100644 index 0000000..a9f1285 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/hammer_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/hand_cursor.png b/src/jdrafting/resources/images/cursors/hand_cursor.png new file mode 100644 index 0000000..c01a690 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/hand_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/homothety_cursor.png b/src/jdrafting/resources/images/cursors/homothety_cursor.png new file mode 100644 index 0000000..8758662 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/homothety_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/hyperbola_cursor.png b/src/jdrafting/resources/images/cursors/hyperbola_cursor.png new file mode 100644 index 0000000..d84e69c Binary files /dev/null and b/src/jdrafting/resources/images/cursors/hyperbola_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/incenter_cursor.png b/src/jdrafting/resources/images/cursors/incenter_cursor.png new file mode 100644 index 0000000..abd59b0 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/incenter_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/jme_cursor.png b/src/jdrafting/resources/images/cursors/jme_cursor.png new file mode 100644 index 0000000..156a1b6 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/jme_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/mediatrix_cursor.png b/src/jdrafting/resources/images/cursors/mediatrix_cursor.png new file mode 100644 index 0000000..6c238c6 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/mediatrix_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/midpoint_cursor.png b/src/jdrafting/resources/images/cursors/midpoint_cursor.png new file mode 100644 index 0000000..473397e Binary files /dev/null and b/src/jdrafting/resources/images/cursors/midpoint_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/modify_segment_cursor.png b/src/jdrafting/resources/images/cursors/modify_segment_cursor.png new file mode 100644 index 0000000..d6a0dae Binary files /dev/null and b/src/jdrafting/resources/images/cursors/modify_segment_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/ortocenter_cursor.png b/src/jdrafting/resources/images/cursors/ortocenter_cursor.png new file mode 100644 index 0000000..6d361a3 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/ortocenter_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/paint_cursor.png b/src/jdrafting/resources/images/cursors/paint_cursor.png new file mode 100644 index 0000000..c4315f9 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/paint_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/parabola_cursor.png b/src/jdrafting/resources/images/cursors/parabola_cursor.png new file mode 100644 index 0000000..d6210fd Binary files /dev/null and b/src/jdrafting/resources/images/cursors/parabola_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/parallel_cursor.png b/src/jdrafting/resources/images/cursors/parallel_cursor.png new file mode 100644 index 0000000..5e1cfef Binary files /dev/null and b/src/jdrafting/resources/images/cursors/parallel_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/paste_style_cursor.png b/src/jdrafting/resources/images/cursors/paste_style_cursor.png new file mode 100644 index 0000000..3aa7f01 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/paste_style_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/perpendicular_cursor.png b/src/jdrafting/resources/images/cursors/perpendicular_cursor.png new file mode 100644 index 0000000..5f68e51 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/perpendicular_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/point_cursor.png b/src/jdrafting/resources/images/cursors/point_cursor.png new file mode 100644 index 0000000..f24fa86 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/point_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/polygon_cursor.png b/src/jdrafting/resources/images/cursors/polygon_cursor.png new file mode 100644 index 0000000..66b26c1 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/polygon_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/polyline_cursor.png b/src/jdrafting/resources/images/cursors/polyline_cursor.png new file mode 100644 index 0000000..6cddb54 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/polyline_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/protractor_cursor.png b/src/jdrafting/resources/images/cursors/protractor_cursor.png new file mode 100644 index 0000000..112465f Binary files /dev/null and b/src/jdrafting/resources/images/cursors/protractor_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/rectangle_cursor.png b/src/jdrafting/resources/images/cursors/rectangle_cursor.png new file mode 100644 index 0000000..efaa399 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/rectangle_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/reg_poly_cursor.png b/src/jdrafting/resources/images/cursors/reg_poly_cursor.png new file mode 100644 index 0000000..5ce64d8 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/reg_poly_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/rotation_cursor.png b/src/jdrafting/resources/images/cursors/rotation_cursor.png new file mode 100644 index 0000000..15c7988 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/rotation_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/ruler_cursor.png b/src/jdrafting/resources/images/cursors/ruler_cursor.png new file mode 100644 index 0000000..5201f89 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/ruler_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/segment_cursor.png b/src/jdrafting/resources/images/cursors/segment_cursor.png new file mode 100644 index 0000000..d683fa9 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/segment_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/spline_cursor.png b/src/jdrafting/resources/images/cursors/spline_cursor.png new file mode 100644 index 0000000..8725c5d Binary files /dev/null and b/src/jdrafting/resources/images/cursors/spline_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/text_cursor.png b/src/jdrafting/resources/images/cursors/text_cursor.png new file mode 100644 index 0000000..92a9d74 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/text_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/translation_cursor.png b/src/jdrafting/resources/images/cursors/translation_cursor.png new file mode 100644 index 0000000..241d3c6 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/translation_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/triangle_cursor.png b/src/jdrafting/resources/images/cursors/triangle_cursor.png new file mode 100644 index 0000000..f5c8e0a Binary files /dev/null and b/src/jdrafting/resources/images/cursors/triangle_cursor.png differ diff --git a/src/jdrafting/resources/images/cursors/vertex_cursor.png b/src/jdrafting/resources/images/cursors/vertex_cursor.png new file mode 100644 index 0000000..298f0e0 Binary files /dev/null and b/src/jdrafting/resources/images/cursors/vertex_cursor.png differ diff --git a/src/jdrafting/resources/images/delete_all.png b/src/jdrafting/resources/images/delete_all.png new file mode 100644 index 0000000..9f63a08 Binary files /dev/null and b/src/jdrafting/resources/images/delete_all.png differ diff --git a/src/jdrafting/resources/images/divisions.png b/src/jdrafting/resources/images/divisions.png new file mode 100644 index 0000000..20a6b0e Binary files /dev/null and b/src/jdrafting/resources/images/divisions.png differ diff --git a/src/jdrafting/resources/images/down.png b/src/jdrafting/resources/images/down.png new file mode 100644 index 0000000..f627a91 Binary files /dev/null and b/src/jdrafting/resources/images/down.png differ diff --git a/src/jdrafting/resources/images/ellipse.png b/src/jdrafting/resources/images/ellipse.png new file mode 100644 index 0000000..fa6b81f Binary files /dev/null and b/src/jdrafting/resources/images/ellipse.png differ diff --git a/src/jdrafting/resources/images/end.png b/src/jdrafting/resources/images/end.png new file mode 100644 index 0000000..a5e38d8 Binary files /dev/null and b/src/jdrafting/resources/images/end.png differ diff --git a/src/jdrafting/resources/images/exit.png b/src/jdrafting/resources/images/exit.png new file mode 100644 index 0000000..1c7d8fb Binary files /dev/null and b/src/jdrafting/resources/images/exit.png differ diff --git a/src/jdrafting/resources/images/extremes.png b/src/jdrafting/resources/images/extremes.png new file mode 100644 index 0000000..c1bc207 Binary files /dev/null and b/src/jdrafting/resources/images/extremes.png differ diff --git a/src/jdrafting/resources/images/eyedropper.png b/src/jdrafting/resources/images/eyedropper.png new file mode 100644 index 0000000..2b1c92e Binary files /dev/null and b/src/jdrafting/resources/images/eyedropper.png differ diff --git a/src/jdrafting/resources/images/fileinfo.png b/src/jdrafting/resources/images/fileinfo.png new file mode 100644 index 0000000..9ef7483 Binary files /dev/null and b/src/jdrafting/resources/images/fileinfo.png differ diff --git a/src/jdrafting/resources/images/fill_color.png b/src/jdrafting/resources/images/fill_color.png new file mode 100644 index 0000000..3ed0781 Binary files /dev/null and b/src/jdrafting/resources/images/fill_color.png differ diff --git a/src/jdrafting/resources/images/forward.png b/src/jdrafting/resources/images/forward.png new file mode 100644 index 0000000..d43c51e Binary files /dev/null and b/src/jdrafting/resources/images/forward.png differ diff --git a/src/jdrafting/resources/images/free_hand.png b/src/jdrafting/resources/images/free_hand.png new file mode 100644 index 0000000..bce68b2 Binary files /dev/null and b/src/jdrafting/resources/images/free_hand.png differ diff --git a/src/jdrafting/resources/images/fusion.png b/src/jdrafting/resources/images/fusion.png new file mode 100644 index 0000000..3088829 Binary files /dev/null and b/src/jdrafting/resources/images/fusion.png differ diff --git a/src/jdrafting/resources/images/github.png b/src/jdrafting/resources/images/github.png new file mode 100644 index 0000000..9b0803d Binary files /dev/null and b/src/jdrafting/resources/images/github.png differ diff --git a/src/jdrafting/resources/images/hammer.png b/src/jdrafting/resources/images/hammer.png new file mode 100644 index 0000000..1f44d93 Binary files /dev/null and b/src/jdrafting/resources/images/hammer.png differ diff --git a/src/jdrafting/resources/images/homepage.png b/src/jdrafting/resources/images/homepage.png new file mode 100644 index 0000000..698af9d Binary files /dev/null and b/src/jdrafting/resources/images/homepage.png differ diff --git a/src/jdrafting/resources/images/homothety.png b/src/jdrafting/resources/images/homothety.png new file mode 100644 index 0000000..ff24000 Binary files /dev/null and b/src/jdrafting/resources/images/homothety.png differ diff --git a/src/jdrafting/resources/images/hyperbola.png b/src/jdrafting/resources/images/hyperbola.png new file mode 100644 index 0000000..12e4914 Binary files /dev/null and b/src/jdrafting/resources/images/hyperbola.png differ diff --git a/src/jdrafting/resources/images/incenter.png b/src/jdrafting/resources/images/incenter.png new file mode 100644 index 0000000..325ca96 Binary files /dev/null and b/src/jdrafting/resources/images/incenter.png differ diff --git a/src/jdrafting/resources/images/intersection.png b/src/jdrafting/resources/images/intersection.png new file mode 100644 index 0000000..f35a016 Binary files /dev/null and b/src/jdrafting/resources/images/intersection.png differ diff --git a/src/jdrafting/resources/images/invert.png b/src/jdrafting/resources/images/invert.png new file mode 100644 index 0000000..deaf165 Binary files /dev/null and b/src/jdrafting/resources/images/invert.png differ diff --git a/src/jdrafting/resources/images/jdrafting.png b/src/jdrafting/resources/images/jdrafting.png new file mode 100644 index 0000000..d3d13e3 Binary files /dev/null and b/src/jdrafting/resources/images/jdrafting.png differ diff --git a/src/jdrafting/resources/images/jme.png b/src/jdrafting/resources/images/jme.png new file mode 100644 index 0000000..18d7c37 Binary files /dev/null and b/src/jdrafting/resources/images/jme.png differ diff --git a/src/jdrafting/resources/images/mediatrix.png b/src/jdrafting/resources/images/mediatrix.png new file mode 100644 index 0000000..6d33241 Binary files /dev/null and b/src/jdrafting/resources/images/mediatrix.png differ diff --git a/src/jdrafting/resources/images/midpoint.png b/src/jdrafting/resources/images/midpoint.png new file mode 100644 index 0000000..3f69f9a Binary files /dev/null and b/src/jdrafting/resources/images/midpoint.png differ diff --git a/src/jdrafting/resources/images/modify_segment.png b/src/jdrafting/resources/images/modify_segment.png new file mode 100644 index 0000000..010a014 Binary files /dev/null and b/src/jdrafting/resources/images/modify_segment.png differ diff --git a/src/jdrafting/resources/images/names.png b/src/jdrafting/resources/images/names.png new file mode 100644 index 0000000..ed17deb Binary files /dev/null and b/src/jdrafting/resources/images/names.png differ diff --git a/src/jdrafting/resources/images/new.png b/src/jdrafting/resources/images/new.png new file mode 100644 index 0000000..212e0d6 Binary files /dev/null and b/src/jdrafting/resources/images/new.png differ diff --git a/src/jdrafting/resources/images/open.png b/src/jdrafting/resources/images/open.png new file mode 100644 index 0000000..9e94fa0 Binary files /dev/null and b/src/jdrafting/resources/images/open.png differ diff --git a/src/jdrafting/resources/images/ortocenter.png b/src/jdrafting/resources/images/ortocenter.png new file mode 100644 index 0000000..6b595cd Binary files /dev/null and b/src/jdrafting/resources/images/ortocenter.png differ diff --git a/src/jdrafting/resources/images/paint.png b/src/jdrafting/resources/images/paint.png new file mode 100644 index 0000000..e78f574 Binary files /dev/null and b/src/jdrafting/resources/images/paint.png differ diff --git a/src/jdrafting/resources/images/parabola.png b/src/jdrafting/resources/images/parabola.png new file mode 100644 index 0000000..18b7ecd Binary files /dev/null and b/src/jdrafting/resources/images/parabola.png differ diff --git a/src/jdrafting/resources/images/parallel.png b/src/jdrafting/resources/images/parallel.png new file mode 100644 index 0000000..a99f0f7 Binary files /dev/null and b/src/jdrafting/resources/images/parallel.png differ diff --git a/src/jdrafting/resources/images/paste.png b/src/jdrafting/resources/images/paste.png new file mode 100644 index 0000000..7e98e70 Binary files /dev/null and b/src/jdrafting/resources/images/paste.png differ diff --git a/src/jdrafting/resources/images/paste_style.png b/src/jdrafting/resources/images/paste_style.png new file mode 100644 index 0000000..8d5dda6 Binary files /dev/null and b/src/jdrafting/resources/images/paste_style.png differ diff --git a/src/jdrafting/resources/images/perpendicular.png b/src/jdrafting/resources/images/perpendicular.png new file mode 100644 index 0000000..0264392 Binary files /dev/null and b/src/jdrafting/resources/images/perpendicular.png differ diff --git a/src/jdrafting/resources/images/point.png b/src/jdrafting/resources/images/point.png new file mode 100644 index 0000000..0d79afe Binary files /dev/null and b/src/jdrafting/resources/images/point.png differ diff --git a/src/jdrafting/resources/images/point_color.png b/src/jdrafting/resources/images/point_color.png new file mode 100644 index 0000000..288308a Binary files /dev/null and b/src/jdrafting/resources/images/point_color.png differ diff --git a/src/jdrafting/resources/images/polygon.png b/src/jdrafting/resources/images/polygon.png new file mode 100644 index 0000000..97a7f01 Binary files /dev/null and b/src/jdrafting/resources/images/polygon.png differ diff --git a/src/jdrafting/resources/images/polygon_popup.png b/src/jdrafting/resources/images/polygon_popup.png new file mode 100644 index 0000000..2b529f2 Binary files /dev/null and b/src/jdrafting/resources/images/polygon_popup.png differ diff --git a/src/jdrafting/resources/images/polyline.png b/src/jdrafting/resources/images/polyline.png new file mode 100644 index 0000000..17c37d2 Binary files /dev/null and b/src/jdrafting/resources/images/polyline.png differ diff --git a/src/jdrafting/resources/images/print.png b/src/jdrafting/resources/images/print.png new file mode 100644 index 0000000..2bd3ad1 Binary files /dev/null and b/src/jdrafting/resources/images/print.png differ diff --git a/src/jdrafting/resources/images/protractor.png b/src/jdrafting/resources/images/protractor.png new file mode 100644 index 0000000..89c8888 Binary files /dev/null and b/src/jdrafting/resources/images/protractor.png differ diff --git a/src/jdrafting/resources/images/rectangle.png b/src/jdrafting/resources/images/rectangle.png new file mode 100644 index 0000000..4067ca9 Binary files /dev/null and b/src/jdrafting/resources/images/rectangle.png differ diff --git a/src/jdrafting/resources/images/redo.png b/src/jdrafting/resources/images/redo.png new file mode 100644 index 0000000..dd6aaa5 Binary files /dev/null and b/src/jdrafting/resources/images/redo.png differ diff --git a/src/jdrafting/resources/images/reg_poly.png b/src/jdrafting/resources/images/reg_poly.png new file mode 100644 index 0000000..b1a7fef Binary files /dev/null and b/src/jdrafting/resources/images/reg_poly.png differ diff --git a/src/jdrafting/resources/images/rewind.png b/src/jdrafting/resources/images/rewind.png new file mode 100644 index 0000000..ef12b13 Binary files /dev/null and b/src/jdrafting/resources/images/rewind.png differ diff --git a/src/jdrafting/resources/images/rotation.png b/src/jdrafting/resources/images/rotation.png new file mode 100644 index 0000000..3b5f5c4 Binary files /dev/null and b/src/jdrafting/resources/images/rotation.png differ diff --git a/src/jdrafting/resources/images/ruler.png b/src/jdrafting/resources/images/ruler.png new file mode 100644 index 0000000..a073906 Binary files /dev/null and b/src/jdrafting/resources/images/ruler.png differ diff --git a/src/jdrafting/resources/images/save.png b/src/jdrafting/resources/images/save.png new file mode 100644 index 0000000..7ae5502 Binary files /dev/null and b/src/jdrafting/resources/images/save.png differ diff --git a/src/jdrafting/resources/images/save_as.png b/src/jdrafting/resources/images/save_as.png new file mode 100644 index 0000000..d04a9f5 Binary files /dev/null and b/src/jdrafting/resources/images/save_as.png differ diff --git a/src/jdrafting/resources/images/save_image.png b/src/jdrafting/resources/images/save_image.png new file mode 100644 index 0000000..98b60f1 Binary files /dev/null and b/src/jdrafting/resources/images/save_image.png differ diff --git a/src/jdrafting/resources/images/segment.png b/src/jdrafting/resources/images/segment.png new file mode 100644 index 0000000..d2fd409 Binary files /dev/null and b/src/jdrafting/resources/images/segment.png differ diff --git a/src/jdrafting/resources/images/select_all.png b/src/jdrafting/resources/images/select_all.png new file mode 100644 index 0000000..ba719b5 Binary files /dev/null and b/src/jdrafting/resources/images/select_all.png differ diff --git a/src/jdrafting/resources/images/selection.png b/src/jdrafting/resources/images/selection.png new file mode 100644 index 0000000..ca49a07 Binary files /dev/null and b/src/jdrafting/resources/images/selection.png differ diff --git a/src/jdrafting/resources/images/spline.png b/src/jdrafting/resources/images/spline.png new file mode 100644 index 0000000..6b72194 Binary files /dev/null and b/src/jdrafting/resources/images/spline.png differ diff --git a/src/jdrafting/resources/images/swap_colors.png b/src/jdrafting/resources/images/swap_colors.png new file mode 100644 index 0000000..35c3e57 Binary files /dev/null and b/src/jdrafting/resources/images/swap_colors.png differ diff --git a/src/jdrafting/resources/images/text.png b/src/jdrafting/resources/images/text.png new file mode 100644 index 0000000..66754a2 Binary files /dev/null and b/src/jdrafting/resources/images/text.png differ diff --git a/src/jdrafting/resources/images/translation.png b/src/jdrafting/resources/images/translation.png new file mode 100644 index 0000000..2758406 Binary files /dev/null and b/src/jdrafting/resources/images/translation.png differ diff --git a/src/jdrafting/resources/images/triangle.png b/src/jdrafting/resources/images/triangle.png new file mode 100644 index 0000000..b6980e7 Binary files /dev/null and b/src/jdrafting/resources/images/triangle.png differ diff --git a/src/jdrafting/resources/images/triangle_popup.png b/src/jdrafting/resources/images/triangle_popup.png new file mode 100644 index 0000000..df94312 Binary files /dev/null and b/src/jdrafting/resources/images/triangle_popup.png differ diff --git a/src/jdrafting/resources/images/undo.png b/src/jdrafting/resources/images/undo.png new file mode 100644 index 0000000..9cc4262 Binary files /dev/null and b/src/jdrafting/resources/images/undo.png differ diff --git a/src/jdrafting/resources/images/up.png b/src/jdrafting/resources/images/up.png new file mode 100644 index 0000000..5be1752 Binary files /dev/null and b/src/jdrafting/resources/images/up.png differ diff --git a/src/jdrafting/resources/images/vertex.png b/src/jdrafting/resources/images/vertex.png new file mode 100644 index 0000000..d8defa5 Binary files /dev/null and b/src/jdrafting/resources/images/vertex.png differ diff --git a/src/jdrafting/resources/images/zoom_all.png b/src/jdrafting/resources/images/zoom_all.png new file mode 100644 index 0000000..c5c2d78 Binary files /dev/null and b/src/jdrafting/resources/images/zoom_all.png differ diff --git a/src/jdrafting/resources/images/zoom_in.png b/src/jdrafting/resources/images/zoom_in.png new file mode 100644 index 0000000..6d0a0f3 Binary files /dev/null and b/src/jdrafting/resources/images/zoom_in.png differ diff --git a/src/jdrafting/resources/images/zoom_out.png b/src/jdrafting/resources/images/zoom_out.png new file mode 100644 index 0000000..68f7976 Binary files /dev/null and b/src/jdrafting/resources/images/zoom_out.png differ diff --git a/src/jdrafting/resources/language/language.properties b/src/jdrafting/resources/language/language.properties new file mode 100644 index 0000000..8409af2 --- /dev/null +++ b/src/jdrafting/resources/language/language.properties @@ -0,0 +1,480 @@ +# general +app_des=is an open-source drafting application to design small Technical Drawing sketches.
This application is inspired by the 'Classical Construction',
also known as 'Ruler-and-compass Construction' or 'Compass-and-straightedge construction'.
However, another drawing tools can be used.
(More info about classical construction: Wikipedia) +width=Width +height=Height +degrees=deg +not_saved=Not saved +cancel=Cancel +add=Add +remove=Remove + +# labels +thickness=

Line

thickness +point_thickness=

Point

thickness +lbl_fill=Fill +lbl_no_fill=No fill +lbl_line_color=Line color +lbl_point_color=Point color +lbl_fill_color=Fill color + +# actions +new=New exercise +new_des=Start a new exercise +open=Open... +open_des=Open an exercise +save=Save +save_des=Save exercise +save_as=Save as... +save_as_des=Save exercise as +save_image=Save image... +save_image_des=Export exercise as image SVG, PNG +print=Print... +print_des=Print in selected printer +fileinfo=Exercise info +fileinfo_des=Get or modify exercise info +redo=Redo +undo=Undo +move_up=Up shapes +move_up_des=Increase selected shapes index +move_down=Down shapes +move_down_des=Decrease selected shapes index +copy=Copy selected +copy_des=Copy selected shapes +exit=Exit +exit_des=Quit program +about=About... +zoom_all=Zoom all +zoom_all_des=Adjust content to canvas +zoom_in=Zoom in +zoom_in_des=Zoom in +zoom_out=Zoom out +zoom_out_des=Zoom out +delete=Delete selected +delete_des=Delete selected shapes +background_color=Canvas color... +background_color_des=Canvas background color +color=Shape color... +color_des=Shape color +point_color=Point color... +point_color_des=Point color +eyedropper=Eyedropper +eyedropper_des=Capture shape style +paste_style=Paste style +paste_style_des=Paste current style color and stroke +text=See/Hide shape names +text_des=See/Hide shape names +backward=Previous frame +backward_des=Previous frame +forward=Next frame +forward_des=Next frame +end=End frame +end_des=End frame +rewind=Rewind +rewind_des=Rewind +selection=Rectangular selection +selection_des=Select shapes in a rectangular area +invert=Invert selection +invert_des=Invert selection +select_all=Select all +select_all_des=Select all +point=Point +point_des=Create a new point +segment=Segment +segment_des=Create segment +arrow=Arrow +arrow_des=Create single or double arrow +polygon_tools=Polygon tools +reg_poly=Regular polygon +reg_poly_des=Create regular polygon +polygon=Polygon +polygon_des=Create polygon +polyline=Polyline +polyline_des=Create polyline +free_hand=Free hand +free_hand_des=Create an arbitrary shape +rectangle=Rectangle +rectangle_des=Create rectangle +ellipse=Ellipse +ellipse_des=Create ellipse +arc=Arc +arc_des=Create arc +circumference=Circumference +circumference_des=Create circumference +angle=Angle +angle_des=Create a segment by angle +triangle_tools=Triangle tools +triangle=Triangle +triangle_des=Create triangle +incenter=Incenter +ortocenter=Ortocenter +baricenter=Baricenter +circumcenter=Circumcenter +comment=Comment +comment_des=Add comment +spline=Spline +spline_des=Create spline between points +parabola=Parabola +parabola_des=Create parabola from rectangle bounds +hyperbola=Hyperbola +hyperbola_des=Create hyperbola from rectangle bounds +func_des=Create graph from function +func=Function +ruler=Ruler +ruler_des=Capture distance +protractor=Protractor +protractor_des=Capture angle +divisions=Division points +divisions_des=Divide shape in points +modify=Modify segment +modify_des=Modify a segment along its container straight +midpoint=Midpoint +midpoint_des=Set midpoint in the center of the shape bounds +centroid=Centroid +vertex=Vertex +vertex_des=Add vertex points to shape +extremes=Extremes +extremes_des=Add extreme vertex to open shapes +inter=Intersection points +inter_des=Select two or more shapes (none points) to add intersections between them +fragment=Fragment shape sides +fragment_des=Fragment shape sides into individual segments +fusion=Fusion shapes +fusion_des=Select shapes to be merged. Shift for connect extremes +bounds=Rectangle bounds +bounds_des=Create rectangle bounds of a shape +perp=Perpendicular +perp_des=Create perpendicular from segment +para=Parallel +para_des=Create parallel from segment +mediatrix=Mediatrix +mediatrix_des=Create mediatrix of a segment +bisectrix=Bisectrix +bisectrix_des=Create bisectrix +capable_arc=Capable arc +capable_arc_des=Create capable arc of a segment +translation=Translate +translation_des=Translate selected shapes +rotation=Rotate +rotation_des=Rotate selected shapes +homothety=Homothety +homothety_des=Homothety of the selected shapes +central_sym=Central symmetry +central_sym_des=Central symmetry of the selected shapes +axial_sym=Axial symmetry +axial_sym_des=Axial symmetry of the selected shapes +paint=Paint +paint_des=Paint shape +fix_dist=Fix
distance +paste=Paste +paste_des=Paste selected shapes +fill=Fill color +fill_des=Fill color of the shapes +area_intersection=Shape intersection +area_intersection_des=Shape from selected intersected shapes +area_union=Shape union +area_union_des=Shape from union of the selected shapes +area_substract=Shape difference +area_substract_des=Shape from the difference of two shapes +area_sym_diff=Symmetric difference +area_sym_diff_des=Symmetric difference of the selected shapes + +# menus +file=File +edit=Edit +style=Style +shapes=Shapes +exercise=Exercise +transforms=Transforms +conics=Conics +view=View +appearance=Appearance +help=Help + +item_style=Show/hide style bar +item_ruler_prot=Show/hide ruler&protractor bar +item_status=Show/hide status bar +item_action=Show/hide action bar +item_shape=Show/hide shape bar +item_tool=Show/hide tool bar +show_all=Show all +hide_all=Hide all + +# toolbars +tools=Tools +actions=Actions +tit_style=Line/Point style +ruler_prot=Ruler and Protractor + +# dialogs +exit_msg=Ignore changes? +exit_dlg=Exit +inter_msg=Select two or more shapes (none points) +inter_dlg=Intersection points error +ext_dlg=The shape must be an open shape +ext_title=Error while selecting shape +bisectrix_dlg=Parallel or coincident segments +bisectrix_title=Error while calculating bisectrix +div_dlg=Number of divisions +reg_poly_dlg=Number of vertex/sides +save_image_acce1=Image setting +save_image_acce2=Include background +save_image_acce3=Include shape names +overwrite1=File exists, overwrite? +overwrite2=Overwrite +exer_prop=Exercise properties +title=Title +exer_desc=Exercise description +start_frame=Start frame +shape_name=Shape identifier +shape_prop=Shape properties +shape_desc=Shape description +save_close=Save and Close +details=Details +selected_shapes_msg=You must select at least one shape +homo_dlg=Homothety factor +dist_mult_dlg=Multiply distance by factor +jme_dlg=JME expression +jme_examples=examples +jme_doc=JME documentation +jme_min=Min 'x' or 't' value +jme_max=Max 'x' or 't' value +jme_intervals=Number of intervals +save_error1=Error when saving +save_error2=Empty exercise +sel_2_error=Select two o more shapes +inter_error=No joins +empty_intersection_error=Empty intersection +empty_union_error=Empty union +empty_substract_error=Empty difference +empty_sym_diff_error=Empty symmetric difference + +# new shapes default description +new_point=point +new_segment=segment +new_segment_extreme=segment extreme +new_arc=arc +new_circumference=circumference +new_circumference_center=circumference center +new_arrow=arrow +new_triangle=triangle +new_rectangle=rectangle +new_square=square +new_ellipse=ellipse +new_regular_polygon=regular polygon +new_polygon=polygon +new_polyline=polyline +new_v_axis=vertical axis +new_h_axis=horizontal axis +new_parabola=parabola +new_spline=spline +new_free_hand=free hand +new_midpoint=> midpoint of +new_centroid=> centroid of +new_vertex=> vertex of +new_extreme=> extreme of +new_parallel=> parallel to +new_perpendicular=> perpendicular to +new_mediatrix=> mediatrix of +new_bisectrix=> bisectrix of +new_capable_arc=> capable arc of +new_fragment=> fragment of +new_div=> division point of +new_join=> intersection point between +new_bounds=> bounds of +new_fusion=> fusion of +new_focus=focus +new_para_vertex=parabola vertex +new_para_bounds=parabola bounds +new_directrix=directrix +new_hype_branch=hyperbola branch +new_hype_vertex=hyperbola vertex +new_hype_bounds=hyperbola bounds +new_hype_center=hyperbola center +new_main_axis=hyperbola main axis +new_img_axis=hyperbola imaginary axis +new_circumscribed=circumscribed circumference +new_center_reg=regular polygon center +new_intersection=> intersection of +new_union=> union of +new_substract=> substract of +new_sym_diff=> symmetric difference of + +# status msg +txt_hand=Use mouse wheel to zoom. Mouse dragging with second buttom to move shapes. Shift over shape for select/unselect several shapes. Drag to move viewport +txt_eyedropper=Select shape to capture line style. Shift for capture point style. Control to capture color on screen +txt_paste_style1=Select shape to apply line style. Control for don't apply fill. Shift to apply point style +txt_ruler1=Select start point +txt_ruler2=Select end point. Shift for add to current distance. Control for substract +txt_prot1=Select vertex angle +txt_prot2=Select first side point +txt_prot3=Select second side point +txt_sel1=Drag mouse to select a rectangular area. Control to force full shape selection +txt_point=Select point position. Control for non-intersection adjusting. Shift to use line style instead of point style +txt_seg1=Select segment start point +txt_seg2=Select segment end point. Shift for 45º fixed angles. Control to add extremes +txt_arrow1=Select arrow start point +txt_arrow2=Select arrow end point. Shift for 45º fixed angles. Control for double arrow +txt_circ1=Select circumference center. Shift for switch radius/diameter +txt_circ2=Select radius. Shift for switch radius/diameter. Control to add center +txt_arc1=Select arc center +txt_arc2=Select start angle +txt_arc3=Select end angle. Control for conjugate angle. Shift for fixed angle. Alt Gr for integer angles +txt_angle1=Select vertex +txt_angle2=Select end point of the first side +txt_angle3=Select end point of the second side. Shift for suplementary angle. Control for complementary angle. Alt for include both sides +txt_rect1=Select rectangle start point +txt_rect2=Move mouse to select rectangle end point. Shift for square. You can use ruler +txt_ellipse1=Select center or corner. Shift for alternate center/corner mode +txt_ellipse2=Select ellipse size. Control to add axis and focuses. Shift for alternate center/corner mode. You can use ruler +txt_poly=Select vertex. Double click to finish +txt_free1=Drag mouse to draw free shape. Double click to finish +txt_modify1=Select segment to modify +txt_modify2=Select first extreme +txt_modify3=Select second extreme. Shift for current line style instead of old segment style +txt_midpoint=Select shape to add midpoint of the shape bounds. Shift to use line style. Control for centroid +txt_vtx1=Select shape to add vertex. Shift for line style +txt_ext1=Select shape to add extreme vertex (must be an open shape) +txt_div1=Select shape to add divisions points. You can use Fix distance +txt_fragment1=Select shape to fragment sides. Press Shift for use current stroke instead of shape stroke +txt_bounds1=Select shape to add rectangle bounds +txt_perp1=Select segment +txt_perp2=Select perpendicular start +txt_perp3=Select perpendicular end +txt_para1=Select segment +txt_para2=Select parallel start +txt_para3=Select parallel end +txt_mediatrix1=Select segment +txt_mediatrix2=Select mediatrix start +txt_mediatrix3=Select mediatrix end +txt_bisectrix1=Select first segment +txt_bisectrix2=Select second segment +txt_bisectrix3=Select first bisectrix extreme +txt_bisectrix4=Select second bisectrix extreme +txt_cap1=Select segment +txt_cap2=Select one of the two sides +txt_translate1=Select first point of direction vector +txt_translate2=Select second point of direction vector. You can use ruler +txt_rotation1=Select rotation center +txt_rotation2=Move mouse to selectangle. Shift for fixed angle. Control for integer angles +txt_homothety1=Select homothety center. Shift for negative factor. Control for inverted factor +txt_central_sym1=Select symmetry center +txt_axial_sym1=Select first point of axis +txt_axial_sym2=Select second point of axis +txt_triangle_points1=Select three points. Shift for equilateral. Control for right +txt_triangle_points2=Select three points. Shift for line style +txt_parabola1=Select parabola bounds start point +txt_parabola2=Move mouse to select parabola bounds end point. Control to add additional shapes. Shift for square bounds. You can use ruler +txt_hyperbola1=Select hyperbola bounds start point +txt_hyperbola2=Move mouse to select end point. You can use the ruler +txt_hyperbola3=Move mouse to select main axis. Control to add auxiliary shapes +txt_reg_poly1=Select center of the polygon +txt_reg_poly2=Select radius. Control to add auxiliary shapes. You can use ruler +txt_paint1=Select shape to fill. Control for delete fill. Alt for line color. Shift for point color +txt_comment1=Select start point of the text box +txt_area_substract1=Select first shape +txt_area_substract2=Select second shape +txt_jme1=Select start point for coordinates origin +txt_jme2=Select end point for x axis + +# mnemonics +mne_menu_file=f +mne_menu_edit=e +mne_menu_style=s +mne_menu_shapes=h +mne_menu_tools=t +mne_menu_exercise=x +mne_menu_view=v +mne_menu_help=l +mne_menu_transform=t +mne_menu_trian=t +mne_menu_poly=o +mne_menu_conics=i +mne_menu_appea=a + +mne_about=a +mne_angle=n +mne_arc=a +mne_arrow=r +mne_sym_axial=a +mne_backward=p +mne_baricenter=b +mne_bisectrix=b +mne_bounds=g +mne_canvas_col=c +mne_cap_arc=c +mne_sym_central=c +mne_circ=c +mne_circumcenter=c +mne_copy=c +mne_delete=t +mne_divisions=s +mne_ellipse=e +mne_end=e +mne_ex_metadata=x +mne_exit=e +mne_extremes=e +mne_eyedropper=e +mne_forward=n +mne_fragment=f +mne_free=f +mne_fusion=u +mne_homo=h +mne_hyperbola=h +mne_incenter=i +mne_intersection=n +mne_invert_sel=i +mne_jme=u +mne_mediatrix=m +mne_midpoint=i +mne_mod_seg=y +mne_z_up=p +mne_z_down=d +mne_new=n +mne_open=o +mne_ortocenter=o +mne_parabola=p +mne_parallel=a +mne_paste=a +mne_perp=p +mne_point=p +mne_point_col=p +mne_polygon=p +mne_polyline=o +mne_protractor=o +mne_rect=e +mne_redo=r +mne_reg_pol=r +mne_rewind=r +mne_rotation=r +mne_ruler=l +mne_save=s +mne_save_as=a +mne_save_img=v +mne_print=p +mne_segment=s +mne_sel_all=l +mne_selection=e +mne_shape_col=s +mne_spline=l +mne_text=s +mne_trans=t +mne_triangle=t +mne_undo=u +mne_vertex=v +mne_zoom_all=z +mne_zoom_in=o +mne_zoom_out=m +mne_paint=i +mne_comment=m +mne_shape_fill=f +mne_union=u +mne_sym_diff=y + +# toast +toast_copy=shape/s copied +toast_no_copy=nothing selected +toast_paste=shape/s pasted + +# tooltips +tip_swap_color=Swap colors +tip_undo_redo=Undo/Redo history diff --git a/src/jdrafting/resources/language/language_el.properties b/src/jdrafting/resources/language/language_el.properties new file mode 100644 index 0000000..12399a7 --- /dev/null +++ b/src/jdrafting/resources/language/language_el.properties @@ -0,0 +1,458 @@ +#language_el.properties +about=\u03A0\u03B5\u03C1\u03AF... +actions=\u0395\u03BD\u03AD\u03C1\u03B3\u03B5\u03B9\u03B5\u03C2 +add=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 +angle=\u0393\u03C9\u03BD\u03AF\u03B1 +angle_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 \u03B1\u03C0\u03CC \u03B3\u03C9\u03BD\u03AF\u03B1 +app_des=\u03B5\u03AF\u03BD\u03B1\u03B9 \u03BC\u03B9\u03B1 \u03B5\u03C6\u03B1\u03C1\u03BC\u03BF\u03B3\u03AE \u03C3\u03C7\u03B5\u03B4\u03AF\u03B1\u03C3\u03B7\u03C2 \u03B1\u03BD\u03BF\u03B9\u03C7\u03C4\u03BF\u03CD \u03BA\u03CE\u03B4\u03B9\u03BA\u03B1 \u03B3\u03B9\u03B1 \u03C4\u03BF \u03C3\u03C7\u03B5\u03B4\u03B9\u03B1\u03C3\u03BC\u03CC
\u03BC\u03B9\u03BA\u03C1\u03CE\u03BD \u03C3\u03BA\u03AF\u03C4\u03C3\u03C9\u03BD \u03A4\u03B5\u03C7\u03BD\u03B9\u03BA\u03BF\u03CD \u03A3\u03C7\u03B5\u03B4\u03AF\u03BF\u03C5.
\u0391\u03C5\u03C4\u03AE \u03B7 \u03B5\u03C6\u03B1\u03C1\u03BC\u03BF\u03B3\u03AE \u03B5\u03AF\u03BD\u03B1\u03B9 \u03B5\u03BC\u03C0\u03BD\u03B5\u03C5\u03C3\u03BC\u03AD\u03BD\u03B7 \u03B1\u03C0\u03CC \u03C4\u03B7\u03BD \u00ABClassical Construction\u00BB,
\u03B3\u03BD\u03C9\u03C3\u03C4\u03AE \u03BA\u03B1\u03B9 \u03C9\u03C2 \u00ABRuler-and-compass Construction\u00BB \u03AE \u00ABCompass-and-straightedge construction\u00BB.
\u03A9\u03C3\u03C4\u03CC\u03C3\u03BF, \u03BC\u03C0\u03BF\u03C1\u03B5\u03AF \u03BD\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03B7\u03B8\u03B5\u03AF \u03BA\u03B1\u03B9 \u03BC\u03B5 \u03AC\u03BB\u03BB\u03B1 \u03B5\u03C1\u03B3\u03B1\u03BB\u03B5\u03AF\u03B1 \u03C3\u03C7\u03B5\u03B4\u03AF\u03B1\u03C3\u03B7\u03C2.
(\u03A0\u03B5\u03C1\u03B9\u03C3\u03C3\u03CC\u03C4\u03B5\u03C1\u03B5\u03C2 \u03C0\u03BB\u03B7\u03C1\u03BF\u03C6\u03BF\u03C1\u03AF\u03B5\u03C2 \u03C3\u03C7\u03B5\u03C4\u03B9\u03BA\u03AC \u03BC\u03B5 \u03C4\u03B7\u03BD \u03BA\u03BB\u03B1\u03C3\u03B9\u03BA\u03AE \u03BA\u03B1\u03C4\u03B1\u03C3\u03BA\u03B5\u03C5\u03AE\: Wikipedia) +appearance=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7 +arc=\u03A4\u03CC\u03BE\u03BF +arc_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C4\u03CC\u03BE\u03BF\u03C5 +area_intersection=\u03A4\u03BF\u03BC\u03AE \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +area_intersection_des=\u03A3\u03C7\u03AE\u03BC\u03B1 \u03B1\u03C0\u03CC \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03B1 \u03C4\u03B5\u03BC\u03BD\u03CC\u03BC\u03B5\u03BD\u03B1 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03B1 +area_substract=\u0394\u03B9\u03B1\u03C6\u03BF\u03C1\u03AC \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +area_substract_des=\u03A3\u03C7\u03AE\u03BC\u03B1 \u03B1\u03C0\u03CC \u03C4\u03B7 \u03B4\u03B9\u03B1\u03C6\u03BF\u03C1\u03AC \u03B4\u03CD\u03BF \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +area_sym_diff=\u03A3\u03C5\u03BC\u03BC\u03B5\u03C4\u03C1\u03B9\u03BA\u03AE \u03B4\u03B9\u03B1\u03C6\u03BF\u03C1\u03AC +area_sym_diff_des=\u03A3\u03C5\u03BC\u03BC\u03B5\u03C4\u03C1\u03B9\u03BA\u03AE \u03B4\u03B9\u03B1\u03C6\u03BF\u03C1\u03AC \u03C4\u03C9\u03BD \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +area_union=\u0388\u03BD\u03C9\u03C3\u03B7 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +area_union_des=\u03A3\u03C7\u03AE\u03BC\u03B1 \u03B1\u03C0\u03CC \u03C4\u03B7\u03BD \u03AD\u03BD\u03C9\u03C3\u03B7 \u03C4\u03C9\u03BD \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +arrow=\u0392\u03AD\u03BB\u03BF\u03C2 +arrow_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03BC\u03BF\u03BD\u03BF\u03CD \u03AE \u03B4\u03B9\u03C0\u03BB\u03BF\u03CD \u03B2\u03AD\u03BB\u03BF\u03C5\u03C2 +axial_sym=\u0391\u03BE\u03BF\u03BD\u03B9\u03BA\u03AE \u03C3\u03C5\u03BC\u03BC\u03B5\u03C4\u03C1\u03AF\u03B1 +axial_sym_des=\u0391\u03BE\u03BF\u03BD\u03B9\u03BA\u03AE \u03C3\u03C5\u03BC\u03BC\u03B5\u03C4\u03C1\u03AF\u03B1 \u03C4\u03C9\u03BD \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +background_color=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03BA\u03B1\u03BC\u03B2\u03AC... +background_color_des=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03C6\u03CC\u03BD\u03C4\u03BF\u03C5 \u03C4\u03BF\u03C5 \u03BA\u03B1\u03BC\u03B2\u03AC +backward=\u03A0\u03C1\u03BF\u03B7\u03B3\u03BF\u03CD\u03BC\u03B5\u03BD\u03BF \u03BA\u03B1\u03C1\u03AD +backward_des=\u03A0\u03C1\u03BF\u03B7\u03B3\u03BF\u03CD\u03BC\u03B5\u03BD\u03BF \u03BA\u03B1\u03C1\u03AD +baricenter=\u0392\u03B1\u03C1\u03CD\u03BA\u03B5\u03BD\u03C4\u03C1\u03BF +bisectrix=\u0394\u03B9\u03C7\u03BF\u03C4\u03CC\u03BC\u03BF\u03C2 +bisectrix_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03B4\u03B9\u03C7\u03BF\u03C4\u03CC\u03BC\u03BF\u03C5 +bisectrix_dlg=\u03A4\u03B1 \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03B1 \u03B5\u03AF\u03BD\u03B1\u03B9 \u03C0\u03B1\u03C1\u03AC\u03BB\u03BB\u03B7\u03BB\u03B1 \u03AE \u03C3\u03C5\u03BC\u03C0\u03AF\u03C0\u03C4\u03BF\u03C5\u03BD +bisectrix_title=\u03A3\u03C6\u03AC\u03BB\u03BC\u03B1 \u03BA\u03B1\u03C4\u03AC \u03C4\u03BF\u03BD \u03C5\u03C0\u03BF\u03BB\u03BF\u03B3\u03B9\u03C3\u03BC\u03CC \u03C4\u03B7\u03C2 \u03B4\u03B9\u03C7\u03BF\u03C4\u03CC\u03BC\u03BF\u03C5 +bounds=\u039F\u03C1\u03B8\u03BF\u03B3\u03CE\u03BD\u03B9\u03B1 \u03CC\u03C1\u03B9\u03B1 +bounds_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03BF\u03C1\u03B8\u03BF\u03B3\u03C9\u03BD\u03AF\u03C9\u03BD \u03BF\u03C1\u03AF\u03C9\u03BD \u03B5\u03BD\u03CC\u03C2 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +cancel=\u0386\u03BA\u03C5\u03C1\u03BF +capable_arc=\u0397\u03BC\u03B9\u03BA\u03CD\u03BA\u03BB\u03B9\u03BF +capable_arc_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03B7\u03BC\u03B9\u03BA\u03C5\u03BA\u03BB\u03AF\u03BF\u03C5 \u03B1\u03C0\u03CC \u03AD\u03BD\u03B1 \u03C4\u03BC\u03AE\u03BC\u03B1 +central_sym=\u039A\u03B5\u03BD\u03C4\u03C1\u03B9\u03BA\u03AE \u03C3\u03C5\u03BC\u03BC\u03B5\u03C4\u03C1\u03AF\u03B1 +central_sym_des=\u039A\u03B5\u03BD\u03C4\u03C1\u03B9\u03BA\u03AE \u03C3\u03C5\u03BC\u03BC\u03B5\u03C4\u03C1\u03AF\u03B1 \u03C4\u03C9\u03BD \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +centroid=\u039A\u03B5\u03BD\u03C4\u03C1\u03BF\u03B5\u03B9\u03B4\u03AD\u03C2 +circumcenter=\u03A0\u03B5\u03C1\u03AF\u03BA\u03B5\u03BD\u03C4\u03C1\u03BF +circumference=\u03A0\u03B5\u03C1\u03B9\u03C6\u03AD\u03C1\u03B5\u03B9\u03B1 \u03BA\u03CD\u03BA\u03BB\u03BF\u03C5 +circumference_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C0\u03B5\u03C1\u03B9\u03C6\u03AD\u03C1\u03B5\u03B9\u03B1\u03C2 \u03BA\u03CD\u03BA\u03BB\u03BF\u03C5 +color=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2... +color_des=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +comment=\u03A3\u03C7\u03CC\u03BB\u03B9\u03BF +comment_des=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03C3\u03C7\u03BF\u03BB\u03AF\u03BF\u03C5 +conics=\u039A\u03C9\u03BD\u03B9\u03BA\u03AC +copy=\u0391\u03BD\u03C4\u03B9\u03B3\u03C1\u03B1\u03C6\u03AE \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03BF\u03C5 +copy_des=\u0391\u03BD\u03C4\u03B9\u03B3\u03C1\u03B1\u03C6\u03AE \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +degrees=\ \u03BC\u03BF\u03AF\u03C1\u03B5\u03C2 +delete=\u0394\u03B9\u03B1\u03B3\u03C1\u03B1\u03C6\u03AE \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD +delete_des=\u0394\u03B9\u03B1\u03B3\u03C1\u03B1\u03C6\u03AE \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +details=\u039B\u03B5\u03C0\u03C4\u03BF\u03BC\u03AD\u03C1\u03B5\u03B9\u03B5\u03C2 +dist_mult_dlg=\u03A0\u03BF\u03BB\u03BB\u03B1\u03C0\u03BB\u03B1\u03C3\u03B9\u03B1\u03C3\u03BC\u03CC\u03C2 \u03B1\u03C0\u03CC\u03C3\u03C4\u03B1\u03C3\u03B7\u03C2 \u03B2\u03AC\u03C3\u03B5\u03B9 \u03C3\u03C5\u03BD\u03C4\u03B5\u03BB\u03B5\u03C3\u03C4\u03AE +div_dlg=\u0391\u03C1\u03B9\u03B8\u03BC\u03CC\u03C2 \u03C4\u03BC\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +divisions=\u03A3\u03B7\u03BC\u03B5\u03AF\u03B1 \u03B4\u03B9\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7\u03C2 +divisions_des=\u0394\u03B9\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 \u03C3\u03B5 \u03C3\u03B7\u03BC\u03B5\u03AF\u03B1 +edit=\u0395\u03C0\u03B5\u03BE\u03B5\u03C1\u03B3\u03B1\u03C3\u03AF\u03B1 +ellipse=\u0388\u03BB\u03BB\u03B5\u03B9\u03C8\u03B7 +ellipse_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03AD\u03BB\u03BB\u03B5\u03B9\u03C8\u03B7\u03C2 +empty_intersection_error=\u039A\u03B5\u03BD\u03AE \u03C4\u03BF\u03BC\u03AE +empty_substract_error=\u039A\u03B5\u03BD\u03AE \u03B4\u03B9\u03B1\u03C6\u03BF\u03C1\u03AC +empty_sym_diff_error=\u039A\u03B5\u03BD\u03AE \u03C3\u03C5\u03BC\u03BC\u03B5\u03C4\u03C1\u03B9\u03BA\u03AE \u03B4\u03B9\u03B1\u03C6\u03BF\u03C1\u03AC +empty_union_error=\u039A\u03B5\u03BD\u03AE \u03AD\u03BD\u03C9\u03C3\u03B7 +end=\u03A4\u03B5\u03BB\u03B5\u03C5\u03C4\u03B1\u03AF\u03BF \u03BA\u03B1\u03C1\u03AD +end_des=\u03A4\u03B5\u03BB\u03B5\u03C5\u03C4\u03B1\u03AF\u03BF \u03BA\u03B1\u03C1\u03AD +exer_desc=\u03A0\u03B5\u03C1\u03B9\u03B3\u03C1\u03B1\u03C6\u03AE \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7\u03C2 +exer_prop=\u0399\u03B4\u03B9\u03CC\u03C4\u03B7\u03C4\u03B5\u03C2 \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7\u03C2 +exercise=\u0386\u03C3\u03BA\u03B7\u03C3\u03B7 +exit=\u0388\u03BE\u03BF\u03B4\u03BF\u03C2 +exit_des=\u0388\u03BE\u03BF\u03B4\u03BF\u03C2 \u03C0\u03C1\u03BF\u03B3\u03C1\u03AC\u03BC\u03BC\u03B1\u03C4\u03BF\u03C2 +exit_dlg=\u0388\u03BE\u03BF\u03B4\u03BF\u03C2 +exit_msg=\u03A0\u03B1\u03C1\u03AC\u03B2\u03BB\u03B5\u03C8\u03B7 \u03C4\u03C9\u03BD \u03B1\u03BB\u03BB\u03B1\u03B3\u03CE\u03BD; +ext_dlg=\u03A4\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 \u03C0\u03C1\u03AD\u03C0\u03B5\u03B9 \u03BD\u03B1 \u03B5\u03AF\u03BD\u03B1\u03B9 \u03AD\u03BD\u03B1 \u03B1\u03BD\u03BF\u03B9\u03C7\u03C4\u03CC \u03C3\u03C7\u03AE\u03BC\u03B1 +ext_title=\u03A3\u03C6\u03AC\u03BB\u03BC\u03B1 \u03BA\u03B1\u03C4\u03AC \u03C4\u03B7\u03BD \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +extremes=\u0386\u03BA\u03C1\u03B1 +extremes_des=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03B1\u03BA\u03C1\u03B1\u03AF\u03C9\u03BD \u03C3\u03B7\u03BC\u03B5\u03AF\u03C9\u03BD \u03C3\u03B5 \u03B1\u03BD\u03BF\u03B9\u03C7\u03C4\u03AC \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03B1 +eyedropper=\u03A3\u03C4\u03B1\u03B3\u03BF\u03BD\u03CC\u03BC\u03B5\u03C4\u03C1\u03BF +eyedropper_des=\u039B\u03AE\u03C8\u03B7 \u03C3\u03C4\u03C5\u03BB \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +file=\u0391\u03C1\u03C7\u03B5\u03AF\u03BF +fileinfo=\u03A0\u03BB\u03B7\u03C1\u03BF\u03C6\u03BF\u03C1\u03AF\u03B5\u03C2 \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7\u03C2 +fileinfo_des=\u039B\u03AE\u03C8\u03B7 \u03AE \u03C4\u03C1\u03BF\u03C0\u03BF\u03C0\u03BF\u03AF\u03B7\u03C3\u03B7 \u03C4\u03C9\u03BD \u03C0\u03BB\u03B7\u03C1\u03BF\u03C6\u03BF\u03C1\u03B9\u03CE\u03BD \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7\u03C2 +fill=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03B3\u03B5\u03BC\u03AF\u03C3\u03BC\u03B1\u03C4\u03BF\u03C2 +fill_des=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03B3\u03B5\u03BC\u03AF\u03C3\u03BC\u03B1\u03C4\u03BF\u03C2 \u03C4\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +fix_dist=\u03A3\u03C4\u03B1\u03B8\u03B5\u03C1\u03AE
\u03B1\u03C0\u03CC\u03C3\u03C4\u03B1\u03C3\u03B7 +forward=\u0395\u03C0\u03CC\u03BC\u03B5\u03BD\u03BF \u03BA\u03B1\u03C1\u03AD +forward_des=\u0395\u03C0\u03CC\u03BC\u03B5\u03BD\u03BF \u03BA\u03B1\u03C1\u03AD +fragment=\u039A\u03B1\u03C4\u03AC\u03C4\u03BC\u03B7\u03C3\u03B7 \u03C0\u03BB\u03B5\u03C5\u03C1\u03CE\u03BD \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +fragment_des=\u039A\u03B1\u03C4\u03AC\u03C4\u03BC\u03B7\u03C3\u03B7 \u03C0\u03BB\u03B5\u03C5\u03C1\u03CE\u03BD \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 \u03C3\u03B5 \u03BC\u03B5\u03BC\u03BF\u03BD\u03C9\u03BC\u03AD\u03BD\u03B1 \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03B1 +free_hand=\u0395\u03BB\u03B5\u03CD\u03B8\u03B5\u03C1\u03BF \u03C7\u03AD\u03C1\u03B9 +free_hand_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03B1\u03C5\u03B8\u03B1\u03AF\u03C1\u03B5\u03C4\u03BF\u03C5 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +func=\u03A3\u03C5\u03BD\u03AC\u03C1\u03C4\u03B7\u03C3\u03B7 +func_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03B3\u03C1\u03B1\u03C6\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 \u03B1\u03C0\u03CC \u03C3\u03C5\u03BD\u03AC\u03C1\u03C4\u03B7\u03C3\u03B7 +fusion=\u03A3\u03C5\u03B3\u03C7\u03CE\u03BD\u03B5\u03C5\u03C3\u03B7 \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +fusion_des=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03B1 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03B1 \u03C0\u03BF\u03C5 \u03B8\u03B1 \u03C3\u03C5\u03B3\u03C7\u03C9\u03BD\u03B5\u03C5\u03B8\u03BF\u03CD\u03BD. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C3\u03CD\u03BD\u03B4\u03B5\u03C3\u03B7 \u03C4\u03C9\u03BD \u03AC\u03BA\u03C1\u03C9\u03BD +height=\u038E\u03C8\u03BF\u03C2 +help=\u0392\u03BF\u03AE\u03B8\u03B5\u03B9\u03B1 +hide_all=\u0391\u03C0\u03CC\u03BA\u03C1\u03C5\u03C8\u03B7 \u03CC\u03BB\u03C9\u03BD +homo_dlg=\u03A3\u03C5\u03BD\u03C4\u03B5\u03BB\u03B5\u03C3\u03C4\u03AE\u03C2 \u03C0\u03C1\u03BF\u03BF\u03C0\u03C4\u03B9\u03BA\u03BF\u03CD +homothety=\u03A0\u03C1\u03BF\u03BF\u03C0\u03C4\u03B9\u03BA\u03CC +homothety_des=\u03A0\u03C1\u03BF\u03BF\u03C0\u03C4\u03B9\u03BA\u03CC \u03C4\u03C9\u03BD \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +hyperbola=\u03A5\u03C0\u03B5\u03C1\u03B2\u03BF\u03BB\u03AE +hyperbola_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C5\u03C0\u03B5\u03C1\u03B2\u03BF\u03BB\u03AE\u03C2 \u03B1\u03C0\u03CC \u03BF\u03C1\u03B8\u03BF\u03B3\u03CE\u03BD\u03B9\u03B1 \u03CC\u03C1\u03B9\u03B1 +incenter=\u0388\u03B3\u03BA\u03B5\u03BD\u03C4\u03C1\u03BF +inter=\u03A3\u03B7\u03BC\u03B5\u03AF\u03B1 \u03C4\u03BF\u03BC\u03AE\u03C2 +inter_des=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03B4\u03CD\u03BF \u03AE \u03C0\u03B5\u03C1\u03B9\u03C3\u03C3\u03CC\u03C4\u03B5\u03C1\u03B1 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03B1 (\u03BA\u03B1\u03BD\u03AD\u03BD\u03B1 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF) \u03B3\u03B9\u03B1 \u03BD\u03B1 \u03C0\u03C1\u03BF\u03C3\u03B8\u03AD\u03C3\u03B5\u03C4\u03B5 \u03C4\u03BF\u03BC\u03AD\u03C2 \u03BC\u03B5\u03C4\u03B1\u03BE\u03CD \u03C4\u03BF\u03C5\u03C2 +inter_dlg=\u03A3\u03C6\u03AC\u03BB\u03BC\u03B1 \u03C3\u03B7\u03BC\u03B5\u03AF\u03C9\u03BD \u03C4\u03BF\u03BC\u03AE\u03C2 +inter_error=\u03A7\u03C9\u03C1\u03AF\u03C2 \u03C3\u03C5\u03BD\u03B4\u03AD\u03C3\u03BC\u03BF\u03C5\u03C2 +inter_msg=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03B4\u03CD\u03BF \u03AE \u03C0\u03B5\u03C1\u03B9\u03C3\u03C3\u03CC\u03C4\u03B5\u03C1\u03B1 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03B1 (\u03BA\u03B1\u03BD\u03AD\u03BD\u03B1 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF) +invert=\u0391\u03BD\u03C4\u03B9\u03C3\u03C4\u03C1\u03BF\u03C6\u03AE \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE\u03C2 +invert_des=\u0391\u03BD\u03C4\u03B9\u03C3\u03C4\u03C1\u03BF\u03C6\u03AE \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE\u03C2 +item_action=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7/\u03B1\u03C0\u03CC\u03BA\u03C1\u03C5\u03C8\u03B7 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 \u03B5\u03BD\u03B5\u03C1\u03B3\u03B5\u03B9\u03CE\u03BD +item_ruler_prot=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7/\u03B1\u03C0\u03CC\u03BA\u03C1\u03C5\u03C8\u03B7 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 \u03C7\u03AC\u03C1\u03B1\u03BA\u03B1 & \u03BC\u03BF\u03B9\u03C1\u03BF\u03B3\u03BD\u03C9\u03BC\u03CC\u03BD\u03B9\u03BF\u03C5 +item_shape=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7/\u03B1\u03C0\u03CC\u03BA\u03C1\u03C5\u03C8\u03B7 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +item_status=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7/\u03B1\u03C0\u03CC\u03BA\u03C1\u03C5\u03C8\u03B7 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 \u03BA\u03B1\u03C4\u03AC\u03C3\u03C4\u03B1\u03C3\u03B7\u03C2 +item_style=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7/\u03B1\u03C0\u03CC\u03BA\u03C1\u03C5\u03C8\u03B7 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 \u03C3\u03C4\u03C5\u03BB +item_tool=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7/\u03B1\u03C0\u03CC\u03BA\u03C1\u03C5\u03C8\u03B7 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 \u03B5\u03C1\u03B3\u03B1\u03BB\u03B5\u03AF\u03C9\u03BD +jme_dlg=\u03A0\u03B1\u03C1\u03AC\u03C3\u03C4\u03B1\u03C3\u03B7 JME +jme_doc=\u03A4\u03B5\u03BA\u03BC\u03B7\u03C1\u03AF\u03C9\u03C3\u03B7 JME +jme_examples=\u03C0\u03B1\u03C1\u03B1\u03B4\u03B5\u03AF\u03B3\u03BC\u03B1\u03C4\u03B1 +jme_intervals=\u0391\u03C1\u03B9\u03B8\u03BC\u03CC\u03C2 \u03B4\u03B9\u03B1\u03C3\u03C4\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +jme_max=\u039C\u03AD\u03B3\u03B9\u03C3\u03C4\u03B7 \u03C4\u03B9\u03BC\u03AE 'x' \u03AE 't' +jme_min=\u0395\u03BB\u03AC\u03C7\u03B9\u03C3\u03C4\u03B7 \u03C4\u03B9\u03BC\u03AE 'x '\u03AE 't' +lbl_fill=\u0393\u03AD\u03BC\u03B9\u03C3\u03BC\u03B1 +lbl_fill_color=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03B3\u03B5\u03BC\u03AF\u03C3\u03BC\u03B1\u03C4\u03BF\u03C2 +lbl_line_color=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 +lbl_no_fill=\u03A7\u03C9\u03C1\u03AF\u03C2 \u03B3\u03AD\u03BC\u03B9\u03C3\u03BC\u03B1 +lbl_point_color=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 +mediatrix=\u039C\u03B5\u03C3\u03BF\u03BA\u03AC\u03B8\u03B5\u03C4\u03BF\u03C2 +mediatrix_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03B9\u03C5\u03C1\u03B3\u03AF\u03B1 \u03BC\u03B5\u03C3\u03BF\u03BA\u03B1\u03B8\u03AD\u03C4\u03BF\u03C5 \u03B5\u03BD\u03CC\u03C2 \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +midpoint=\u039C\u03AD\u03C3\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF +midpoint_des=\u039F\u03C1\u03B9\u03C3\u03BC\u03CC\u03C2 \u03C4\u03BF\u03C5 \u03BC\u03AD\u03C3\u03BF\u03C5 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 \u03C3\u03C4\u03BF \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03C4\u03C9\u03BD \u03BF\u03C1\u03AF\u03C9\u03BD \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +mne_about=\u03C0 +mne_angle=\u03B3 +mne_arc=\u03BE +mne_arrow=\u03B2 +mne_backward=\u03C0 +mne_baricenter=\u03B2 +mne_bisectrix=\u03B9 +mne_bounds=\u03B8 +mne_canvas_col=\u03BA +mne_cap_arc=\u03B7 +mne_circ=\u03C6 +mne_circumcenter=\u03C0 +mne_comment=\u03C7 +mne_copy=\u03B3 +mne_delete=\u03B4 +mne_divisions=\u03B4 +mne_ellipse=\u03B5 +mne_end=\u03C4 +mne_ex_metadata=\u03BB +mne_exit=\u03BE +mne_extremes=\u03B1 +mne_eyedropper=\u03B3 +mne_forward=\u03BC +mne_fragment=\u03BB +mne_free=\u03B5 +mne_fusion=\u03C5 +mne_homo=\u03C1 +mne_hyperbola=\u03C5 +mne_incenter=\u03BA +mne_intersection=\u03BF +mne_invert_sel=\u03C3 +mne_jme=\u03C5 +mne_mediatrix=\u03C3 +mne_menu_appea=\u03B5 +mne_menu_conics=\u03BA +mne_menu_edit=\u03B5 +mne_menu_exercise=\u03BA +mne_menu_file=\u03B1 +mne_menu_help=\u03B2 +mne_menu_poly=\u03C0 +mne_menu_shapes=\u03C7 +mne_menu_style=\u03C3 +mne_menu_tools=\u03B3 +mne_menu_transform=\u03BC +mne_menu_trian=\u03C4 +mne_menu_view=\u03BF +mne_midpoint=\u03B5 +mne_mod_seg=\u03C4 +mne_new=\u03C3 +mne_open=\u03BD +mne_ortocenter=\u03BF +mne_paint=\u03B2 +mne_parabola=\u03C0 +mne_parallel=\u03C0 +mne_paste=\u03B9 +mne_perp=\u03BA +mne_point=\u03C3 +mne_point_col=\u03C3 +mne_polygon=\u03C0 +mne_polyline=\u03C4 +mne_print=\u03B5 +mne_protractor=\u03B3 +mne_rect=\u03BF +mne_redo=\u03B5 +mne_reg_pol=\u03BA +mne_rewind=\u03B5 +mne_rotation=\u03C0 +mne_ruler=\u03C7 +mne_save=\u03B1 +mne_save_as=\u03C9 +mne_save_img=\u03B5 +mne_segment=\u03BC +mne_sel_all=\u03BB +mne_selection=\u03BF +mne_shape_col=\u03C7 +mne_shape_fill=\u03BC +mne_spline=\u03BB +mne_sym_axial=\u03B1 +mne_sym_central=\u03BA +mne_sym_diff=\u03C3 +mne_text=\u03BF +mne_trans=\u03BC +mne_triangle=\u03C4 +mne_undo=\u03C1 +mne_union=\u03B5 +mne_vertex=\u03C1 +mne_z_down=\u03BA +mne_z_up=\u03C0 +mne_zoom_all=\u03BB +mne_zoom_in=\u03BC +mne_zoom_out=\u03C3 +modify=\u03A4\u03C1\u03BF\u03C0\u03BF\u03C0\u03BF\u03AF\u03B7\u03C3\u03B7 \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +modify_des=\u03A4\u03C1\u03BF\u03C0\u03BF\u03C0\u03BF\u03AF\u03B7\u03C3\u03B7 \u03B5\u03BD\u03CC\u03C2 \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 \u03BA\u03B1\u03C4\u03AC \u03BC\u03AE\u03BA\u03BF\u03C2 \u03C4\u03B7\u03C2 \u03B5\u03C5\u03B8\u03B5\u03AF\u03B1\u03C2 \u03C4\u03BF\u03C5 +move_down=\u03A3\u03C7\u03AE\u03BC\u03B1\u03C4\u03B1 \u03BA\u03AC\u03C4\u03C9 +move_down_des=\u039C\u03B5\u03AF\u03C9\u03C3\u03B7 \u03C4\u03BF\u03C5 \u03B4\u03B5\u03AF\u03BA\u03C4\u03B7 \u03C4\u03C9\u03BD \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +move_up=\u03A3\u03C7\u03AE\u03BC\u03B1\u03C4\u03B1 \u03C0\u03AC\u03BD\u03C9 +move_up_des=\u0391\u03CD\u03BE\u03B7\u03C3\u03B7 \u03C4\u03BF\u03C5 \u03B4\u03B5\u03AF\u03BA\u03C4\u03B7 \u03C4\u03C9\u03BD \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +new=\u039D\u03AD\u03B1 \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7 +new_arc=\u03C4\u03CC\u03BE\u03BF +new_arrow=\u03B2\u03AD\u03BB\u03BF\u03C2 +new_bisectrix=> \u03B4\u03B9\u03C7\u03BF\u03C4\u03CC\u03BC\u03BF\u03C2 \u03C4\u03BF\u03C5 +new_bounds=> \u03CC\u03C1\u03B9\u03B1 \u03C4\u03BF\u03C5 +new_capable_arc=> \u03B7\u03BC\u03B9\u03BA\u03CD\u03BA\u03BB\u03B9\u03BF \u03C4\u03BF\u03C5 +new_center_reg=\u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03BA\u03B1\u03BD\u03BF\u03BD\u03B9\u03BA\u03BF\u03CD \u03C0\u03BF\u03BB\u03C5\u03B3\u03CE\u03BD\u03BF\u03C5 +new_centroid=> \u03BA\u03B5\u03BD\u03C4\u03C1\u03BF\u03B5\u03B9\u03B4\u03AD\u03C2 \u03C4\u03BF\u03C5 +new_circumference=\u03C0\u03B5\u03C1\u03B9\u03C6\u03AD\u03C1\u03B5\u03B9\u03B1 \u03BA\u03CD\u03BA\u03BB\u03BF\u03C5 +new_circumference_center=\u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03C0\u03B5\u03C1\u03B9\u03C6\u03AD\u03C1\u03B5\u03B9\u03B1\u03C2 \u03BA\u03CD\u03BA\u03BB\u03BF\u03C5 +new_circumscribed=\u03BF\u03C1\u03B9\u03BF\u03B8\u03B5\u03C4\u03B7\u03BC\u03AD\u03BD\u03B7 \u03C0\u03B5\u03C1\u03B9\u03C6\u03AD\u03C1\u03B5\u03B9\u03B1 \u03BA\u03CD\u03BA\u03BB\u03BF\u03C5 +new_des=\u0388\u03BD\u03B1\u03C1\u03BE\u03B7 \u03BC\u03B9\u03B1\u03C2 \u03BD\u03AD\u03B1\u03C2 \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7\u03C2 +new_directrix=\u03B4\u03B9\u03B5\u03C5\u03B8\u03B5\u03C4\u03BF\u03CD\u03C3\u03B1 +new_div=> \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03B4\u03B9\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7\u03C2 \u03C4\u03BF\u03C5 +new_ellipse=\u03AD\u03BB\u03BB\u03B5\u03B9\u03C8\u03B7 +new_extreme=> \u03AC\u03BA\u03C1\u03BF \u03C4\u03BF\u03C5 +new_focus=\u03B5\u03C3\u03C4\u03AF\u03B1\u03C3\u03B7 +new_fragment=> \u03BA\u03B1\u03C4\u03AC\u03C4\u03BC\u03B7\u03C3\u03B7 \u03C4\u03BF\u03C5 +new_free_hand=\u03B1\u03C5\u03B8\u03B1\u03AF\u03C1\u03B5\u03C4\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 +new_fusion=> \u03C3\u03C5\u03B3\u03C7\u03CE\u03BD\u03B5\u03C5\u03C3\u03B7 \u03C4\u03C9\u03BD +new_h_axis=\u03BF\u03C1\u03B9\u03B6\u03CC\u03BD\u03C4\u03B9\u03BF\u03C2 \u03AC\u03BE\u03BF\u03BD\u03B1\u03C2 +new_hype_bounds=\u03CC\u03C1\u03B9\u03B1 \u03C5\u03C0\u03B5\u03C1\u03B2\u03BF\u03BB\u03AE\u03C2 +new_hype_branch=\u03BA\u03BB\u03AC\u03B4\u03BF\u03C2 \u03C5\u03C0\u03B5\u03C1\u03B2\u03BF\u03BB\u03AE\u03C2 +new_hype_center=\u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03C5\u03C0\u03B5\u03C1\u03B2\u03BF\u03BB\u03AE\u03C2 +new_hype_vertex=\u03BA\u03BF\u03C1\u03C5\u03C6\u03AE \u03C5\u03C0\u03B5\u03C1\u03B2\u03BF\u03BB\u03AE\u03C2 +new_img_axis=\u03C6\u03B1\u03BD\u03C4\u03B1\u03C3\u03C4\u03B9\u03BA\u03CC\u03C2 \u03AC\u03BE\u03BF\u03BD\u03B1\u03C2 \u03C5\u03C0\u03B5\u03C1\u03B2\u03BF\u03BB\u03AE\u03C2 +new_intersection=> \u03C4\u03BF\u03BC\u03AE \u03C4\u03BF\u03C5 +new_join=> \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03BC\u03AE\u03C2 \u03BC\u03B5\u03C4\u03B1\u03BE\u03CD +new_main_axis=\u03BA\u03CD\u03C1\u03B9\u03BF\u03C2 \u03AC\u03BE\u03BF\u03BD\u03B1\u03C2 \u03C5\u03C0\u03B5\u03C1\u03B2\u03BF\u03BB\u03AE\u03C2 +new_mediatrix=> \u03BC\u03B5\u03C3\u03BF\u03BA\u03AC\u03B8\u03B5\u03C4\u03BF\u03C2 \u03C4\u03BF\u03C5 +new_midpoint=> \u03BC\u03AD\u03C3\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 +new_para_bounds=\u03CC\u03C1\u03B9\u03B1 \u03C0\u03B1\u03C1\u03B1\u03B2\u03BF\u03BB\u03AE\u03C2 +new_para_vertex=\u03BA\u03BF\u03C1\u03C5\u03C6\u03AE \u03C0\u03B1\u03C1\u03B1\u03B2\u03BF\u03BB\u03AE\u03C2 +new_parabola=\u03C0\u03B1\u03C1\u03B1\u03B2\u03BF\u03BB\u03AE +new_parallel=> \u03C0\u03B1\u03C1\u03AC\u03BB\u03BB\u03B7\u03BB\u03BF\u03C2 \u03BC\u03B5 +new_perpendicular=> \u03BA\u03AC\u03B8\u03B5\u03C4\u03B7 \u03C0\u03C1\u03BF\u03C2 +new_point=\u03C3\u03B7\u03BC\u03B5\u03AF\u03BF +new_polygon=\u03C0\u03BF\u03BB\u03CD\u03B3\u03C9\u03BD\u03BF +new_polyline=\u03C4\u03B5\u03B8\u03BB\u03B1\u03C3\u03BC\u03AD\u03BD\u03B7 +new_rectangle=\u03BF\u03C1\u03B8\u03BF\u03B3\u03CE\u03BD\u03B9\u03BF +new_regular_polygon=\u03BA\u03B1\u03BD\u03BF\u03BD\u03B9\u03BA\u03CC \u03C0\u03BF\u03BB\u03CD\u03B3\u03C9\u03BD\u03BF +new_segment=\u03C4\u03BC\u03AE\u03BC\u03B1 +new_segment_extreme=\u03B1\u03BA\u03C1\u03B1\u03AF\u03BF \u03C4\u03BC\u03AE\u03BC\u03B1 +new_spline=\u03BA\u03B1\u03BC\u03C0\u03CD\u03BB\u03B7 +new_square=\u03C4\u03B5\u03C4\u03C1\u03AC\u03B3\u03C9\u03BD\u03BF +new_substract=> \u03B1\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u03C4\u03BF\u03C5 +new_sym_diff=> \u03C3\u03C5\u03BC\u03BC\u03B5\u03C4\u03C1\u03B9\u03BA\u03AE \u03B4\u03B9\u03B1\u03C6\u03BF\u03C1\u03AC \u03C4\u03BF\u03C5 +new_triangle=\u03C4\u03C1\u03AF\u03B3\u03C9\u03BD\u03BF +new_union=> \u03AD\u03BD\u03C9\u03C3\u03B7 \u03C4\u03BF\u03C5 +new_v_axis=\u03BA\u03B1\u03C4\u03B1\u03BA\u03CC\u03C1\u03C5\u03C6\u03BF\u03C2 \u03AC\u03BE\u03BF\u03BD\u03B1\u03C2 +new_vertex=> \u03BA\u03BF\u03C1\u03C5\u03C6\u03AE \u03C4\u03BF\u03C5 +not_saved=\u0394\u03B5\u03BD \u03B1\u03C0\u03BF\u03B8\u03B7\u03BA\u03B5\u03CD\u03C4\u03B7\u03BA\u03B5 +open=\u0386\u03BD\u03BF\u03B9\u03B3\u03BC\u03B1... +open_des=\u0386\u03BD\u03BF\u03B9\u03B3\u03BC\u03B1 \u03BC\u03B9\u03B1\u03C2 \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7\u03C2 +ortocenter=\u039F\u03C1\u03B8\u03CC\u03BA\u03B5\u03BD\u03C4\u03C1\u03BF +overwrite1=\u03A4\u03BF \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF \u03C5\u03C0\u03AC\u03C1\u03C7\u03B5\u03B9. \u039D\u03B1 \u03B1\u03BD\u03C4\u03B9\u03BA\u03B1\u03C4\u03B1\u03C3\u03C4\u03B1\u03B8\u03B5\u03AF; +overwrite2=\u0391\u03BD\u03C4\u03B9\u03BA\u03B1\u03C4\u03AC\u03C3\u03C4\u03B1\u03C3\u03B7 +paint=\u0392\u03B1\u03C6\u03AE +paint_des=\u0392\u03B1\u03C6\u03AE \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +para=\u03A0\u03B1\u03C1\u03AC\u03BB\u03BB\u03B7\u03BB\u03B7 +para_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C0\u03B1\u03C1\u03B1\u03BB\u03BB\u03AE\u03BB\u03BF\u03C5 \u03B1\u03C0\u03CC \u03C4\u03BC\u03AE\u03BC\u03B1 +parabola=\u03A0\u03B1\u03C1\u03B1\u03B2\u03BF\u03BB\u03AE +parabola_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C0\u03B1\u03C1\u03B1\u03B2\u03BF\u03BB\u03AE\u03C2 \u03B1\u03C0\u03CC \u03BF\u03C1\u03B8\u03BF\u03B3\u03CE\u03BD\u03B9\u03B1 \u03CC\u03C1\u03B9\u03B1 +paste=\u0395\u03C0\u03B9\u03BA\u03CC\u03BB\u03BB\u03B7\u03C3\u03B7 +paste_des=\u0395\u03C0\u03B9\u03BA\u03CC\u03BB\u03BB\u03B7\u03C3\u03B7 \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +paste_style=\u0395\u03C0\u03B9\u03BA\u03CC\u03BB\u03BB\u03B7\u03C3\u03B7 \u03C3\u03C4\u03C5\u03BB +paste_style_des=\u0395\u03C0\u03B9\u03BA\u03CC\u03BB\u03BB\u03B7\u03C3\u03B7 \u03C4\u03C1\u03AD\u03C7\u03BF\u03BD\u03C4\u03BF\u03C2 \u03C3\u03C4\u03C5\u03BB \u03C7\u03C1\u03CE\u03BC\u03B1\u03C4\u03BF\u03C2 \u03BA\u03B1\u03B9 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 +perp=\u039A\u03AC\u03B8\u03B5\u03C4\u03BF\u03C2 +perp_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03BA\u03B1\u03B8\u03AD\u03C4\u03BF\u03C5 \u03B1\u03C0\u03CC \u03C4\u03BC\u03AE\u03BC\u03B1 +point=\u03A3\u03B7\u03BC\u03B5\u03AF\u03BF +point_color=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5... +point_color_des=\u03A7\u03C1\u03CE\u03BC\u03B1 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 +point_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03BD\u03AD\u03BF\u03C5 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 +point_thickness=

\u03A0\u03AC\u03C7\u03BF\u03C2

\u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 +polygon=\u03A0\u03BF\u03BB\u03CD\u03B3\u03C9\u03BD\u03BF +polygon_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C0\u03BF\u03BB\u03CD\u03B3\u03C9\u03BD\u03BF\u03C5 +polygon_tools=\u0395\u03C1\u03B3\u03B1\u03BB\u03B5\u03AF\u03B1 \u03C0\u03BF\u03BB\u03C5\u03B3\u03CE\u03BD\u03BF\u03C5 +polyline=\u03A4\u03B5\u03B8\u03BB\u03B1\u03C3\u03BC\u03AD\u03BD\u03B7 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE +polyline_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C4\u03B5\u03B8\u03BB\u03B1\u03C3\u03BC\u03AD\u03BD\u03B7\u03C2 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 +print=\u0395\u03BA\u03C4\u03CD\u03C0\u03C9\u03C3\u03B7... +print_des=\u0395\u03BA\u03C4\u03CD\u03C0\u03C9\u03C3\u03B7 \u03C3\u03C4\u03BF\u03BD \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03BF \u03B5\u03BA\u03C4\u03C5\u03C0\u03C9\u03C4\u03AE +protractor=\u039C\u03BF\u03B9\u03C1\u03BF\u03B3\u03BD\u03C9\u03BC\u03CC\u03BD\u03B9\u03BF +protractor_des=\u039A\u03B1\u03C4\u03B1\u03B3\u03C1\u03B1\u03C6\u03AE \u03B3\u03C9\u03BD\u03AF\u03B1\u03C2 +rectangle=\u039F\u03C1\u03B8\u03BF\u03B3\u03CE\u03BD\u03B9\u03BF +rectangle_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C0\u03BF\u03BB\u03C5\u03B3\u03CE\u03BD\u03BF\u03C5 +redo=\u0395\u03C0\u03B1\u03BD\u03AC\u03BB\u03B7\u03C8\u03B7 \u03B5\u03BD\u03AD\u03C1\u03B3\u03B5\u03B9\u03B1\u03C2 +reg_poly=\u039A\u03B1\u03BD\u03BF\u03BD\u03B9\u03BA\u03CC \u03C0\u03BF\u03BB\u03CD\u03B3\u03C9\u03BD\u03BF +reg_poly_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03BA\u03B1\u03BD\u03BF\u03BD\u03B9\u03BA\u03BF\u03CD \u03C0\u03BF\u03BB\u03CD\u03B3\u03C9\u03BD\u03BF\u03C5 +reg_poly_dlg=\u0391\u03C1\u03B9\u03B8\u03BC\u03CC\u03C2 \u03BA\u03BF\u03C1\u03C5\u03C6\u03CE\u03BD / \u03C0\u03BB\u03B5\u03C5\u03C1\u03CE\u03BD +remove=\u039A\u03B1\u03C4\u03AC\u03C1\u03B3\u03B7\u03C3\u03B7 +rewind=\u0395\u03C0\u03B1\u03BD\u03B1\u03C6\u03BF\u03C1\u03AC +rewind_des=\u0395\u03C0\u03B1\u03BD\u03B1\u03C6\u03BF\u03C1\u03AC +rotation=\u03A0\u03B5\u03C1\u03B9\u03C3\u03C4\u03C1\u03BF\u03C6\u03AE +rotation_des=\u03A0\u03B5\u03C1\u03B9\u03C3\u03C4\u03C1\u03BF\u03C6\u03AE \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +ruler=\u03A7\u03AC\u03C1\u03B1\u03BA\u03B1\u03C2 +ruler_des=\u039A\u03B1\u03C4\u03B1\u03B3\u03C1\u03B1\u03C6\u03AE \u03B1\u03C0\u03CC\u03C3\u03C4\u03B1\u03C3\u03B7\u03C2 +ruler_prot=\u03A7\u03AC\u03C1\u03B1\u03BA\u03B1\u03C2 & \u039C\u03BF\u03B9\u03C1\u03BF\u03B3\u03BD\u03C9\u03BC\u03CC\u03BD\u03B9\u03BF +save=\u0391\u03C0\u03BF\u03B8\u03AE\u03BA\u03B5\u03C5\u03C3\u03B7 +save_as=\u0391\u03C0\u03BF\u03B8\u03AE\u03BA\u03B5\u03C5\u03C3\u03B7 \u03C9\u03C2... +save_as_des=\u0391\u03C0\u03BF\u03B8\u03AE\u03BA\u03B5\u03C5\u03C3\u03B7 \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7\u03C2 \u03C9\u03C2 +save_close=\u0391\u03C0\u03BF\u03B8\u03AE\u03BA\u03B5\u03C5\u03C3\u03B7 & \u039A\u03BB\u03B5\u03AF\u03C3\u03B9\u03BC\u03BF +save_des=\u0391\u03C0\u03BF\u03B8\u03AE\u03BA\u03B5\u03C5\u03C3\u03B7 \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7\u03C2 +save_error1=\u03A3\u03C6\u03AC\u03BB\u03BC\u03B1 \u03BA\u03B1\u03C4\u03AC \u03C4\u03B7\u03BD \u03B1\u03C0\u03BF\u03B8\u03AE\u03BA\u03B5\u03C5\u03C3\u03B7 +save_error2=\u039A\u03B5\u03BD\u03AE \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7 +save_image=\u0391\u03C0\u03BF\u03B8\u03AE\u03BA\u03B5\u03C5\u03C3\u03B7 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B1\u03C2... +save_image_acce1=\u03A1\u03C5\u03B8\u03BC\u03AF\u03C3\u03B5\u03B9\u03C2 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B1\u03C2 +save_image_acce2=\u03A3\u03C5\u03BC\u03C0\u03B5\u03C1\u03AF\u03BB\u03B7\u03C8\u03B7 \u03C6\u03CC\u03BD\u03C4\u03BF\u03C5 +save_image_acce3=\u03A3\u03C5\u03BC\u03C0\u03B5\u03C1. \u03BF\u03BD\u03BF\u03BC\u03AC\u03C4\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +save_image_des=\u0395\u03BE\u03B1\u03B3\u03C9\u03B3\u03AE \u03AC\u03C3\u03BA\u03B7\u03C3\u03B7\u03C2 \u03C9\u03C2 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B1\u03C2 SVG, PNG +segment=\u03A4\u03BC\u03AE\u03BC\u03B1 +segment_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +sel_2_error=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03B4\u03CD\u03BF \u03AE \u03C0\u03B5\u03C1\u03B9\u03C3\u03C3\u03CC\u03C4\u03B5\u03C1\u03B1 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03B1 +select_all=\u0395\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03CC\u03BB\u03C9\u03BD +select_all_des=\u0395\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03CC\u03BB\u03C9\u03BD +selected_shapes_msg=\u03A0\u03C1\u03AD\u03C0\u03B5\u03B9 \u03BD\u03B1 \u03B5\u03C0\u03B9\u03BB\u03AD\u03BE\u03B5\u03C4\u03B5 \u03C4\u03BF\u03C5\u03BB\u03AC\u03C7\u03B9\u03C3\u03C4\u03BF\u03BD \u03AD\u03BD\u03B1 \u03C3\u03C7\u03AE\u03BC\u03B1 +selection=\u039F\u03C1\u03B8\u03BF\u03B3\u03CE\u03BD\u03B9\u03B1 \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE +selection_des=\u0395\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD \u03C3\u03B5 \u03BF\u03C1\u03B8\u03BF\u03B3\u03CE\u03BD\u03B9\u03B1 \u03C0\u03B5\u03C1\u03B9\u03BF\u03C7\u03AE +shape_desc=\u03A0\u03B5\u03C1\u03B9\u03B3\u03C1\u03B1\u03C6\u03AE \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +shape_name=\u0391\u03BD\u03B1\u03B3\u03BD\u03C9\u03C1\u03B9\u03C3\u03C4\u03B9\u03BA\u03CC \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +shape_prop=\u0399\u03B4\u03B9\u03CC\u03C4\u03B7\u03C4\u03B5\u03C2 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +shapes=\u03A3\u03C7\u03AE\u03BC\u03B1\u03C4\u03B1 +show_all=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7 \u03CC\u03BB\u03C9\u03BD +spline=\u039A\u03B1\u03BC\u03C0\u03CD\u03BB\u03B7 +spline_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03BA\u03B1\u03BC\u03C0\u03CD\u03BB\u03B7\u03C2 \u03BC\u03B5\u03C4\u03B1\u03BE\u03CD \u03C3\u03B7\u03BC\u03B5\u03AF\u03C9\u03BD +start_frame=\u039A\u03B1\u03C1\u03AD \u03B5\u03BA\u03BA\u03AF\u03BD\u03B7\u03C3\u03B7\u03C2 +style=\u03A3\u03C4\u03C5\u03BB +text=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7/\u0391\u03C0\u03CC\u03BA\u03C1\u03C5\u03C8\u03B7 \u03BF\u03BD\u03BF\u03BC\u03AC\u03C4\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +text_des=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7/\u0391\u03C0\u03CC\u03BA\u03C1\u03C5\u03C8\u03B7 \u03BF\u03BD\u03BF\u03BC\u03AC\u03C4\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +thickness=

\u03A0\u03AC\u03C7\u03BF\u03C2

\u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 +tip_swap_color=\u0391\u03BD\u03C4\u03B9\u03BC\u03B5\u03C4\u03AC\u03B8\u03B5\u03C3\u03B7 \u03C7\u03C1\u03C9\u03BC\u03AC\u03C4\u03C9\u03BD +tip_undo_redo=\u0399\u03C3\u03C4\u03BF\u03C1\u03B9\u03BA\u03CC \u03B1\u03BD\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7\u03C2/\u03B5\u03C0\u03B1\u03BD\u03AC\u03BB\u03B7\u03C8\u03B7\u03C2 +tit_style=\u03A3\u03C4\u03C5\u03BB \u0393\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 / \u03A3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 +title=\u03A4\u03AF\u03C4\u03BB\u03BF\u03C2 +toast_copy=\u03C3\u03C7\u03AE\u03BC\u03B1/\u03C4\u03B1 \u03B1\u03BD\u03C4\u03B9\u03B3\u03C1\u03AC\u03C6\u03B7\u03BA\u03B1\u03BD +toast_no_copy=\u03B4\u03B5\u03BD \u03C5\u03C0\u03AC\u03C1\u03C7\u03B5\u03B9 \u03C4\u03AF\u03C0\u03BF\u03C4\u03B1 \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03BF +toast_paste=\u03C3\u03C7\u03AE\u03BC\u03B1/\u03C4\u03B1 \u03B5\u03C0\u03B9\u03BA\u03BF\u03BB\u03BB\u03AE\u03B8\u03B7\u03BA\u03B1\u03BD +tools=\u0395\u03C1\u03B3\u03B1\u03BB\u03B5\u03AF\u03B1 +transforms=\u039C\u03B5\u03C4\u03B1\u03C3\u03C7\u03B7\u03BC\u03B1\u03C4\u03B9\u03C3\u03BC\u03BF\u03AF +translation=\u039C\u03B5\u03C4\u03B1\u03BA\u03AF\u03BD\u03B7\u03C3\u03B7 +translation_des=\u039C\u03B5\u03C4\u03B1\u03BA\u03AF\u03BD\u03B7\u03C3\u03B7 \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +triangle=\u03A4\u03C1\u03AF\u03B3\u03C9\u03BD\u03BF +triangle_des=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03C4\u03C1\u03B9\u03B3\u03CE\u03BD\u03BF\u03C5 +triangle_tools=\u0395\u03C1\u03B3\u03B1\u03BB\u03B5\u03AF\u03B1 \u03C4\u03C1\u03B9\u03B3\u03CE\u03BD\u03BF\u03C5 +txt_angle1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03BA\u03BF\u03C1\u03C5\u03C6\u03AE +txt_angle2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03B5\u03BB\u03B5\u03C5\u03C4\u03B1\u03AF\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03B7\u03C2 \u03C0\u03C1\u03CE\u03C4\u03B7\u03C2 \u03C0\u03BB\u03B5\u03C5\u03C1\u03AC\u03C2 +txt_angle3=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03B5\u03BB\u03B5\u03C5\u03C4\u03B1\u03AF\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03B7\u03C2 \u03B4\u03B5\u03CD\u03C4\u03B5\u03C1\u03B7\u03C2 \u03C0\u03BB\u03B5\u03C5\u03C1\u03AC\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C3\u03C5\u03BC\u03C0\u03BB\u03B7\u03C1\u03C9\u03BC\u03B1\u03C4\u03B9\u03BA\u03AE \u03B3\u03C9\u03BD\u03AF\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03C0\u03B1\u03C1\u03B1\u03C0\u03BB\u03B7\u03C1\u03C9\u03BC\u03B1\u03C4\u03B9\u03BA\u03AE \u03B3\u03C9\u03BD\u03AF\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Alt \u03B3\u03B9\u03B1 \u03C0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03BA\u03B1\u03B9 \u03C4\u03C9\u03BD \u03B4\u03CD\u03BF \u03C0\u03BB\u03B5\u03C5\u03C1\u03CE\u03BD +txt_arc1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03C4\u03BF\u03C5 \u03C4\u03CC\u03BE\u03BF\u03C5 +txt_arc2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03B7\u03BD \u03B1\u03C1\u03C7\u03AE \u03C4\u03B7\u03C2 \u03B3\u03C9\u03BD\u03AF\u03B1\u03C2 +txt_arc3=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03AD\u03BB\u03BF\u03C2 \u03C4\u03B7\u03C2 \u03B3\u03C9\u03BD\u03AF\u03B1\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03B5\u03BD\u03B1\u03BB\u03BB\u03B1\u03B3\u03AE \u03BA\u03C5\u03C1\u03C4\u03AE\u03C2/\u03BC\u03B7 \u03BA\u03C5\u03C1\u03C4\u03AE\u03C2 \u03B3\u03C9\u03BD\u03AF\u03B1\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C3\u03C4\u03B1\u03B8\u03B5\u03C1\u03AE \u03B3\u03C9\u03BD\u03AF\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Alt Gr \u03B3\u03B9\u03B1 \u03B1\u03BA\u03AD\u03C1\u03B1\u03B9\u03B5\u03C2 \u03B3\u03C9\u03BD\u03AF\u03B5\u03C2 +txt_area_substract1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C0\u03C1\u03CE\u03C4\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 +txt_area_substract2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B4\u03B5\u03CD\u03C4\u03B5\u03C1\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 +txt_arrow1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B1\u03C1\u03C7\u03B9\u03BA\u03CC \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 \u03B2\u03AD\u03BB\u03BF\u03C5\u03C2 +txt_arrow2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03B5\u03BB\u03B9\u03BA\u03CC \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 \u03B2\u03AD\u03BB\u03BF\u03C5\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C3\u03C4\u03B1\u03B8\u03B5\u03C1\u03AD\u03C2 \u03B3\u03C9\u03BD\u03AF\u03B5\u03C2 45\u00BA. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03B4\u03B9\u03C0\u03BB\u03CC \u03B2\u03AD\u03BB\u03BF\u03C2 +txt_axial_sym1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C0\u03C1\u03CE\u03C4\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 \u03AC\u03BE\u03BF\u03BD\u03B1 +txt_axial_sym2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B4\u03B5\u03CD\u03C4\u03B5\u03C1\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 \u03AC\u03BE\u03BF\u03BD\u03B1 +txt_bisectrix1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C0\u03C1\u03CE\u03C4\u03BF \u03C4\u03BC\u03AE\u03BC\u03B1 +txt_bisectrix2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B4\u03B5\u03CD\u03C4\u03B5\u03C1\u03BF \u03C4\u03BC\u03AE\u03BC\u03B1 +txt_bisectrix3=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C0\u03C1\u03CE\u03C4\u03BF \u03AC\u03BA\u03C1\u03BF \u03C4\u03B7\u03C2 \u03B4\u03B9\u03C7\u03BF\u03C4\u03CC\u03BC\u03BF\u03C5 +txt_bisectrix4=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B4\u03B5\u03CD\u03C4\u03B5\u03C1\u03BF \u03AC\u03BA\u03C1\u03BF \u03C4\u03B7\u03C2 \u03B4\u03B9\u03C7\u03BF\u03C4\u03CC\u03BC\u03BF\u03C5 +txt_bounds1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 \u03C0\u03BF\u03C5 \u03B8\u03B1 \u03C0\u03C1\u03BF\u03C3\u03C4\u03B5\u03B8\u03BF\u03CD\u03BD \u03BF\u03C1\u03B8\u03BF\u03B3\u03CE\u03BD\u03B9\u03B1 \u03CC\u03C1\u03B9\u03B1 +txt_cap1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BC\u03AE\u03BC\u03B1 +txt_cap2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03BC\u03B9\u03B1 \u03B1\u03C0\u03CC \u03C4\u03B9\u03C2 \u03B4\u03CD\u03BF \u03C0\u03BB\u03B5\u03C5\u03C1\u03AD\u03C2 +txt_central_sym1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03C3\u03C5\u03BC\u03BC\u03B5\u03C4\u03C1\u03AF\u03B1\u03C2 +txt_circ1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03C4\u03B7\u03C2 \u03C0\u03B5\u03C1\u03B9\u03C6\u03AD\u03C1\u03B5\u03B9\u03B1\u03C2 \u03BA\u03CD\u03BA\u03BB\u03BF\u03C5. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03B5\u03BD\u03B1\u03BB\u03BB\u03B1\u03B3\u03AE \u03B1\u03BA\u03C4\u03AF\u03BD\u03B1\u03C2 / \u03B4\u03B9\u03B1\u03BC\u03AD\u03C4\u03C1\u03BF\u03C5 +txt_circ2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03B1\u03BA\u03C4\u03AF\u03BD\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03B5\u03BD\u03B1\u03BB\u03BB\u03B1\u03B3\u03AE \u03B1\u03BA\u03C4\u03AF\u03BD\u03B1\u03C2 / \u03B4\u03B9\u03B1\u03BC\u03AD\u03C4\u03C1\u03BF\u03C5. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03C0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF\u03C5 +txt_comment1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B1\u03C1\u03C7\u03B9\u03BA\u03CC \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 \u03C0\u03BB\u03B1\u03B9\u03C3\u03AF\u03BF\u03C5 \u03BA\u03B5\u03B9\u03BC\u03AD\u03BD\u03BF\u03C5 +txt_div1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 \u03C0\u03BF\u03C5 \u03B8\u03B1 \u03C0\u03C1\u03BF\u03C3\u03C4\u03B5\u03B8\u03BF\u03CD\u03BD \u03C3\u03B7\u03BC\u03B5\u03AF\u03B1 \u03B4\u03B9\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7\u03C2. \u039C\u03C0\u03BF\u03C1\u03B5\u03AF\u03C4\u03B5 \u03BD\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03AE\u03C3\u03B5\u03C4\u03B5 \u03C4\u03B7\u03BD \u03A3\u03C4\u03B1\u03B8\u03B5\u03C1\u03AE \u03B1\u03C0\u03CC\u03C3\u03C4\u03B1\u03C3\u03B7 +txt_ellipse1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03AE \u03B3\u03C9\u03BD\u03AF\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03B5\u03BD\u03B1\u03BB\u03BB\u03B1\u03BA\u03C4\u03B9\u03BA\u03AE \u03BB\u03B5\u03B9\u03C4\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF\u03C5 / \u03B3\u03C9\u03BD\u03AF\u03B1\u03C2 +txt_ellipse2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03BC\u03AD\u03B3\u03B5\u03B8\u03BF\u03C2 \u03AD\u03BB\u03BB\u03B5\u03B9\u03C8\u03B7\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03C0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03AC\u03BE\u03BF\u03BD\u03B1 \u03BA\u03B1\u03B9 \u03B5\u03C3\u03C4\u03AF\u03B1\u03C3\u03B7\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03B5\u03BD\u03B1\u03BB\u03BB\u03B1\u03BA\u03C4\u03B9\u03BA\u03AE \u03BB\u03B5\u03B9\u03C4\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF\u03C5 / \u03B3\u03C9\u03BD\u03AF\u03B1\u03C2. \u039C\u03C0\u03BF\u03C1\u03B5\u03AF\u03C4\u03B5 \u03BD\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03AE\u03C3\u03B5\u03C4\u03B5 \u03C7\u03AC\u03C1\u03B1\u03BA\u03B1 +txt_ext1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 \u03C0\u03BF\u03C5 \u03B8\u03B1 \u03C0\u03C1\u03BF\u03C3\u03C4\u03B5\u03B8\u03BF\u03CD\u03BD \u03B1\u03BA\u03C1\u03B1\u03AF\u03B1 \u03C3\u03B7\u03BC\u03B5\u03AF\u03B1 (\u03C0\u03C1\u03AD\u03C0\u03B5\u03B9 \u03BD\u03B1 \u03B5\u03AF\u03BD\u03B1\u03B9 \u03B1\u03BD\u03BF\u03B9\u03C7\u03C4\u03CC \u03C3\u03C7\u03AE\u03BC\u03B1) +txt_eyedropper=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C3\u03C7\u03AE\u03BC\u03B1 \u03BB\u03AE\u03C8\u03B7 \u03C3\u03C4\u03C5\u03BB \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03BB\u03AE\u03C8\u03B7 \u03C3\u03C4\u03C5\u03BB \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 +txt_fragment1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 \u03B3\u03B9\u03B1 \u03BA\u03B1\u03C4\u03AC\u03C4\u03BC\u03B7\u03C3\u03B7 \u03C0\u03BB\u03B5\u03C5\u03C1\u03CE\u03BD. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C7\u03C1\u03AE\u03C3\u03B7 \u03C4\u03B7\u03C2 \u03C4\u03C1\u03AD\u03C7\u03BF\u03C5\u03C3\u03B1\u03C2 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 \u03B1\u03BD\u03C4\u03AF \u03C4\u03B7\u03C2 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 \u03C4\u03BF\u03C5 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +txt_free1=\u03A3\u03CD\u03C1\u03B5\u03C4\u03B5 \u03C4\u03BF \u03C0\u03BF\u03BD\u03C4\u03AF\u03BA\u03B9 \u03B3\u03B9\u03B1 \u03C3\u03C7\u03B5\u03B4\u03AF\u03B1\u03C3\u03B7 \u03B5\u03BB\u03B5\u03CD\u03B8\u03B5\u03C1\u03BF\u03C5 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2. \u0394\u03B9\u03C0\u03BB\u03CC \u03BA\u03BB\u03B9\u03BA \u03B3\u03B9\u03B1 \u03BF\u03BB\u03BF\u03BA\u03BB\u03AE\u03C1\u03C9\u03C3\u03B7 +txt_hand=\u03A7\u03C1\u03AE\u03C3\u03B7 \u03C4\u03BF\u03C5 \u03C4\u03C1\u03BF\u03C7\u03BF\u03CD \u03C0\u03BF\u03BD\u03C4\u03B9\u03BA\u03B9\u03BF\u03CD \u03B3\u03B9\u03B1 \u03BC\u03B5\u03B3\u03AD\u03B8\u03C5\u03BD\u03C3\u03B7. \u03A3\u03CD\u03C1\u03B5\u03C4\u03B5 \u03BC\u03B5 \u03B4\u03B5\u03BE\u03AF \u03BA\u03BB\u03B9\u03BA \u03B3\u03B9\u03B1 \u03BD\u03B1 \u03BC\u03B5\u03C4\u03B1\u03BA\u03B9\u03BD\u03AE\u03C3\u03B5\u03C4\u03B5 \u03C4\u03B1 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03B1. Shift \u03C0\u03AC\u03BD\u03C9 \u03B1\u03C0\u03CC \u03C3\u03C7\u03AE\u03BC\u03B1 \u03B3\u03B9\u03B1 \u03B1\u03C0\u03BF-\u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03C0\u03BF\u03BB\u03BB\u03CE\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD. \u03A3\u03CD\u03C1\u03B5\u03C4\u03B5 \u03B3\u03B9\u03B1 \u03BC\u03B5\u03C4\u03B1\u03BA\u03AF\u03BD\u03B7\u03C3\u03B7 \u03C4\u03BF\u03C5 \u03C0\u03B5\u03B4\u03AF\u03BF\u03C5 \u03C0\u03C1\u03BF\u03B2\u03BF\u03BB\u03AE\u03C2 +txt_homothety1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03C4\u03BF\u03C5 \u03C0\u03C1\u03BF\u03BF\u03C0\u03C4\u03B9\u03BA\u03BF\u03CD. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03B1\u03C1\u03BD\u03B7\u03C4\u03B9\u03BA\u03CC \u03C3\u03C5\u03BD\u03C4\u03B5\u03BB\u03B5\u03C3\u03C4\u03AE. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03B1\u03BD\u03B5\u03C3\u03C4\u03C1\u03B1\u03BC\u03BC\u03AD\u03BD\u03BF \u03C3\u03C5\u03BD\u03C4\u03B5\u03BB\u03B5\u03C3\u03C4\u03AE +txt_hyperbola1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B1\u03C1\u03C7\u03B9\u03BA\u03CC \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03C9\u03BD \u03BF\u03C1\u03AF\u03C9\u03BD \u03C4\u03B7\u03C2 \u03C5\u03C0\u03B5\u03C1\u03B2\u03BF\u03BB\u03AE\u03C2 +txt_hyperbola2=\u039A\u03B9\u03BD\u03AE\u03C3\u03C4\u03B5 \u03C4\u03BF \u03C0\u03BF\u03BD\u03C4\u03AF\u03BA\u03B9 \u03B3\u03B9\u03B1 \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03C4\u03BF\u03C5 \u03C4\u03B5\u03BB\u03B5\u03C5\u03C4\u03B1\u03AF\u03BF\u03C5 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5. \u039C\u03C0\u03BF\u03C1\u03B5\u03AF\u03C4\u03B5 \u03BD\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03AE\u03C3\u03B5\u03C4\u03B5 \u03C4\u03BF\u03BD \u03C7\u03AC\u03C1\u03B1\u03BA\u03B1 +txt_hyperbola3=\u039A\u03B9\u03BD\u03AE\u03C3\u03C4\u03B5 \u03C4\u03BF \u03C0\u03BF\u03BD\u03C4\u03AF\u03BA\u03B9 \u03B3\u03B9\u03B1 \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03C4\u03BF\u03C5 \u03B2\u03B1\u03C3\u03B9\u03BA\u03BF\u03CD \u03AC\u03BE\u03BF\u03BD\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control\u03B3\u03B9\u03B1 \u03C0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03B2\u03BF\u03B7\u03B8\u03B7\u03C4\u03B9\u03BA\u03CE\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD +txt_jme1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03AD\u03BD\u03B1\u03C1\u03BE\u03B7\u03C2 \u03B3\u03B9\u03B1 \u03C4\u03B9\u03C2 \u03C3\u03C5\u03BD\u03C4\u03B5\u03C4\u03B1\u03B3\u03BC\u03AD\u03BD\u03B5\u03C2 +txt_jme2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03B5\u03BB\u03B9\u03BA\u03CC \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03B3\u03B9\u03B1 \u03C4\u03BF\u03BD \u03AC\u03BE\u03BF\u03BD\u03B1 x +txt_mediatrix1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BC\u03AE\u03BC\u03B1 +txt_mediatrix2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03B7\u03BD \u03B1\u03C1\u03C7\u03AE \u03C4\u03B7\u03C2 \u03BC\u03B5\u03C3\u03BF\u03BA\u03B1\u03B8\u03AD\u03C4\u03BF\u03C5 +txt_mediatrix3=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03AD\u03BB\u03BF\u03C2 \u03C4\u03B7\u03C2 \u03BC\u03B5\u03C3\u03BF\u03BA\u03B1\u03B8\u03AD\u03C4\u03BF\u03C5 +txt_midpoint=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 \u03C0\u03BF\u03C5 \u03B8\u03B1 \u03C0\u03C1\u03BF\u03C3\u03C4\u03B5\u03B8\u03B5\u03AF \u03C4\u03BF \u03BC\u03AD\u03C3\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03C9\u03BD \u03BF\u03C1\u03AF\u03C9\u03BD \u03C4\u03BF\u03C5 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C7\u03C1\u03AE\u03C3\u03B7 \u03C4\u03BF\u03C5 \u03C3\u03C4\u03C5\u03BB \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03BA\u03B5\u03BD\u03C4\u03C1\u03BF\u03B5\u03B9\u03B4\u03AD\u03C2 +txt_modify1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03BC\u03AE\u03BC\u03B1 \u03C0\u03C1\u03BF\u03C2 \u03C4\u03C1\u03BF\u03C0\u03BF\u03C0\u03BF\u03AF\u03B7\u03C3\u03B7 +txt_modify2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C0\u03C1\u03CE\u03C4\u03BF \u03AC\u03BA\u03C1\u03BF +txt_modify3=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B4\u03B5\u03CD\u03C4\u03B5\u03C1\u03BF \u03AC\u03BA\u03C1\u03BF. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C4\u03C1\u03AD\u03C7\u03BF\u03BD \u03C3\u03C4\u03C5\u03BB \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 \u03B1\u03BD\u03C4\u03AF \u03C4\u03BF\u03C5 \u03C3\u03C4\u03C5\u03BB \u03C4\u03BF\u03C5 \u03C0\u03B1\u03BB\u03B9\u03BF\u03CD \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +txt_paint1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C3\u03C7\u03AE\u03BC\u03B1 \u03B3\u03B9\u03B1 \u03B3\u03AD\u03BC\u03B9\u03C3\u03BC\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03B4\u03B9\u03B1\u03B3\u03C1\u03B1\u03C6\u03AE \u03B3\u03B5\u03BC\u03AF\u03C3\u03BC\u03B1\u03C4\u03BF\u03C2. Alt \u03B3\u03B9\u03B1 \u03C7\u03C1\u03CE\u03BC\u03B1 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2. Shift \u03B3\u03B9\u03B1 \u03C7\u03C1\u03CE\u03BC\u03B1 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 \u03B1\u03BD\u03C4\u03AF \u03B3\u03B9\u03B1 \u03C7\u03C1\u03CE\u03BC\u03B1 \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +txt_para1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BC\u03AE\u03BC\u03B1 +txt_para2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03B7\u03BD \u03B1\u03C1\u03C7\u03AE \u03C4\u03B7\u03C2 \u03C0\u03B1\u03C1\u03B1\u03BB\u03BB\u03AE\u03BB\u03BF\u03C5 +txt_para3=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03AD\u03BB\u03BF\u03C2 \u03C4\u03B7\u03C2 \u03C0\u03B1\u03C1\u03B1\u03BB\u03BB\u03AE\u03BB\u03BF\u03C5 +txt_parabola1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B1\u03C1\u03C7\u03B9\u03BA\u03CC \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03C9\u03BD \u03BF\u03C1\u03AF\u03C9\u03BD \u03C4\u03B7\u03C2 \u03C0\u03B1\u03C1\u03B1\u03B2\u03BF\u03BB\u03AE\u03C2 +txt_parabola2=\u039A\u03B9\u03BD\u03AE\u03C3\u03C4\u03B5 \u03C4\u03BF \u03C0\u03BF\u03BD\u03C4\u03AF\u03BA\u03B9 \u03B3\u03B9\u03B1 \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03C4\u03BF\u03C5 \u03C4\u03B5\u03BB\u03B5\u03C5\u03C4\u03B1\u03AF\u03BF\u03C5 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 \u03C4\u03C9\u03BD \u03BF\u03C1\u03AF\u03C9\u03BD \u03C4\u03B7\u03C2 \u03C0\u03B1\u03C1\u03B1\u03B2\u03BF\u03BB\u03AE\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03C0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03B5\u03C0\u03B9\u03C0\u03BB\u03AD\u03BF\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C4\u03B5\u03C4\u03C1\u03AC\u03B3\u03C9\u03BD\u03B1 \u03CC\u03C1\u03B9\u03B1. \u039C\u03C0\u03BF\u03C1\u03B5\u03AF\u03C4\u03B5 \u03BD\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03AE\u03C3\u03B5\u03C4\u03B5 \u03C7\u03AC\u03C1\u03B1\u03BA\u03B1 +txt_paste_style1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 \u03C0\u03BF\u03C5 \u03B8\u03B1 \u03B5\u03C6\u03B1\u03C1\u03BC\u03BF\u03C3\u03C4\u03B5\u03AF \u03C4\u03BF \u03C3\u03C4\u03C5\u03BB \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03BC\u03B7 \u03B5\u03C6\u03B1\u03C1\u03BC\u03BF\u03B3\u03AE \u03B3\u03B5\u03BC\u03AF\u03C3\u03BC\u03B1\u03C4\u03BF\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03B5\u03C6\u03B1\u03C1\u03BC\u03BF\u03B3\u03AE \u03C4\u03BF\u03C5 \u03C3\u03C4\u03C5\u03BB \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 +txt_perp1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BC\u03AE\u03BC\u03B1 +txt_perp2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03B7\u03BD \u03B1\u03C1\u03C7\u03AE \u03C4\u03B7\u03C2 \u03BA\u03B1\u03B8\u03AD\u03C4\u03BF\u03C5 +txt_perp3=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03AD\u03BB\u03BF\u03C2 \u03C4\u03B7\u03C2 \u03BA\u03B1\u03B8\u03AD\u03C4\u03BF\u03C5 +txt_point=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03B8\u03AD\u03C3\u03B7 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03BD\u03B1 \u03BC\u03B7\u03BD \u03C0\u03C1\u03BF\u03C3\u03B1\u03C1\u03BC\u03BF\u03C3\u03C4\u03B5\u03AF \u03C3\u03B5 \u03C4\u03BF\u03BC\u03AE. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C7\u03C1\u03AE\u03C3\u03B7 \u03C3\u03C4\u03C5\u03BB \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2 \u03B1\u03BD\u03C4\u03AF \u03B3\u03B9\u03B1 \u03C3\u03C4\u03C5\u03BB \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 +txt_poly=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03B7\u03BD \u03BA\u03BF\u03C1\u03C5\u03C6\u03AE. \u0394\u03B9\u03C0\u03BB\u03CC \u03BA\u03BB\u03B9\u03BA \u03B3\u03B9\u03B1 \u03BF\u03BB\u03BF\u03BA\u03BB\u03AE\u03C1\u03C9\u03C3\u03B7 +txt_prot1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03BA\u03BF\u03C1\u03C5\u03C6\u03AE \u03B3\u03C9\u03BD\u03AF\u03B1\u03C2 +txt_prot2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C0\u03C1\u03CE\u03C4\u03B7\u03C2 \u03C0\u03BB\u03B5\u03C5\u03C1\u03AC\u03C2 +txt_prot3=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03B4\u03B5\u03CD\u03C4\u03B5\u03C1\u03B7\u03C2 \u03C0\u03BB\u03B5\u03C5\u03C1\u03AC\u03C2 +txt_rect1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B1\u03C1\u03C7\u03B9\u03BA\u03CC \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 \u03BF\u03C1\u03B8\u03BF\u03B3\u03C9\u03BD\u03AF\u03BF\u03C5 +txt_rect2=\u039C\u03B5\u03C4\u03B1\u03BA\u03B9\u03BD\u03AE\u03C3\u03C4\u03B5 \u03C4\u03BF \u03C0\u03BF\u03BD\u03C4\u03AF\u03BA\u03B9 \u03B3\u03B9\u03B1 \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03C4\u03BF\u03C5 \u03C4\u03B5\u03BB\u03B5\u03C5\u03C4\u03B1\u03AF\u03BF\u03C5 \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF\u03C5 \u03C4\u03BF\u03C5 \u03BF\u03C1\u03B8\u03BF\u03B3\u03C9\u03BD\u03AF\u03BF\u03C5. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C4\u03B5\u03C4\u03C1\u03AC\u03B3\u03C9\u03BD\u03BF. \u039C\u03C0\u03BF\u03C1\u03B5\u03AF\u03C4\u03B5 \u03BD\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03AE\u03C3\u03B5\u03C4\u03B5 \u03C7\u03AC\u03C1\u03B1\u03BA\u03B1 +txt_reg_poly1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03C4\u03BF\u03C5 \u03C0\u03BF\u03BB\u03C5\u03B3\u03CE\u03BD\u03BF\u03C5 +txt_reg_poly2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03B1\u03BA\u03C4\u03AF\u03BD\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03C0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03B2\u03BF\u03B7\u03B8\u03B7\u03C4\u03B9\u03BA\u03CE\u03BD \u03C3\u03C7\u03B7\u03BC\u03AC\u03C4\u03C9\u03BD. \u039C\u03C0\u03BF\u03C1\u03B5\u03AF\u03C4\u03B5 \u03BD\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03AE\u03C3\u03B5\u03C4\u03B5 \u03C7\u03AC\u03C1\u03B1\u03BA\u03B1 +txt_rotation1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03BA\u03AD\u03BD\u03C4\u03C1\u03BF \u03C0\u03B5\u03C1\u03B9\u03C3\u03C4\u03C1\u03BF\u03C6\u03AE\u03C2 +txt_rotation2=\u039A\u03B9\u03BD\u03AE\u03C3\u03C4\u03B5 \u03C4\u03BF \u03C0\u03BF\u03BD\u03C4\u03AF\u03BA\u03B9 \u03B3\u03B9\u03B1 \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03B3\u03C9\u03BD\u03AF\u03B1\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C3\u03C4\u03B1\u03B8\u03B5\u03C1\u03AE \u03B3\u03C9\u03BD\u03AF\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03B1\u03BA\u03AD\u03C1\u03B1\u03B9\u03B5\u03C2 \u03B3\u03C9\u03BD\u03AF\u03B5\u03C2 +txt_ruler1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B1\u03C1\u03C7\u03B9\u03BA\u03CC \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF +txt_ruler2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03B5\u03BB\u03B5\u03C5\u03C4\u03B1\u03AF\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03C3\u03C4\u03B7\u03BD \u03C4\u03C1\u03AD\u03C7\u03BF\u03C5\u03C3\u03B1 \u03B1\u03C0\u03CC\u03C3\u03C4\u03B1\u03C3\u03B7. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03B1\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 +txt_seg1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B1\u03C1\u03C7\u03B9\u03BA\u03CC \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +txt_seg2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C4\u03B5\u03BB\u03B9\u03BA\u03CC \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 \u03C4\u03BC\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C3\u03C4\u03B1\u03B8\u03B5\u03C1\u03AD\u03C2 \u03B3\u03C9\u03BD\u03AF\u03B5\u03C2 45\u00BA. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03C0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03AC\u03BA\u03C1\u03C9\u03BD +txt_sel1=\u039C\u03B5\u03C4\u03B1\u03BA\u03B9\u03BD\u03AE\u03C3\u03C4\u03B5 \u03C4\u03BF \u03C0\u03BF\u03BD\u03C4\u03AF\u03BA\u03B9 \u03B3\u03B9\u03B1 \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03BC\u03B9\u03B1\u03C2 \u03BF\u03C1\u03B8\u03BF\u03B3\u03CE\u03BD\u03B9\u03B1\u03C2 \u03C0\u03B5\u03C1\u03B9\u03BF\u03C7\u03AE\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5Control \u03B3\u03B9\u03B1 \u03B5\u03C0\u03B9\u03B2\u03BF\u03BB\u03AE \u03CC\u03BB\u03BF\u03C5 \u03C4\u03BF\u03C5 \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03BF\u03C5 \u03C3\u03C7\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +txt_translate1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C0\u03C1\u03CE\u03C4\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 \u03B4\u03B9\u03B1\u03BD\u03CD\u03C3\u03BC\u03B1\u03C4\u03BF\u03C2 +txt_translate2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B4\u03B5\u03CD\u03C4\u03B5\u03C1\u03BF \u03C3\u03B7\u03BC\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 \u03B4\u03B9\u03B1\u03BD\u03CD\u03C3\u03BC\u03B1\u03C4\u03BF\u03C2. \u039C\u03C0\u03BF\u03C1\u03B5\u03AF\u03C4\u03B5 \u03BD\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03AE\u03C3\u03B5\u03C4\u03B5 \u03C4\u03BF\u03BD \u03C7\u03AC\u03C1\u03B1\u03BA\u03B1 +txt_triangle_points1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03C1\u03AF\u03B1 \u03C3\u03B7\u03BC\u03B5\u03AF\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03B9\u03C3\u03CC\u03C0\u03BB\u03B5\u03C5\u03C1\u03BF. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Control \u03B3\u03B9\u03B1 \u03BF\u03C1\u03B8\u03BF\u03B3\u03CE\u03BD\u03B9\u03BF +txt_triangle_points2=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03C1\u03AF\u03B1 \u03C3\u03B7\u03BC\u03B5\u03AF\u03B1. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C3\u03C4\u03C5\u03BB \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2. +txt_vtx1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03C3\u03C7\u03AE\u03BC\u03B1 \u03C0\u03BF\u03C5 \u03B8\u03B1 \u03C0\u03C1\u03BF\u03C3\u03C4\u03B5\u03B8\u03BF\u03CD\u03BD \u03BA\u03BF\u03C1\u03C5\u03C6\u03AD\u03C2. \u03A0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 Shift \u03B3\u03B9\u03B1 \u03C3\u03C4\u03C5\u03BB \u03B3\u03C1\u03B1\u03BC\u03BC\u03AE\u03C2. +undo=\u0391\u03BD\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 +vertex=\u039A\u03BF\u03C1\u03C5\u03C6\u03AE +vertex_des=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03C3\u03B7\u03BC\u03B5\u03AF\u03C9\u03BD \u03BA\u03BF\u03C1\u03C5\u03C6\u03AE\u03C2 \u03C3\u03B5 \u03C3\u03C7\u03AE\u03BC\u03B1 +view=\u03A0\u03C1\u03BF\u03B2\u03BF\u03BB\u03AE +width=\u03A0\u03BB\u03AC\u03C4\u03BF\u03C2 +zoom_all=\u039C\u03B5\u03B3\u03AD\u03B8\u03C5\u03BD\u03C3\u03B7 \u03CC\u03BB\u03C9\u03BD +zoom_all_des=\u03A0\u03C1\u03BF\u03C3\u03B1\u03C1\u03BC\u03BF\u03B3\u03AE \u03C0\u03B5\u03C1\u03B9\u03B5\u03C7\u03BF\u03BC\u03AD\u03BD\u03BF\u03C5 \u03C3\u03C4\u03BF\u03BD \u03BA\u03B1\u03BC\u03B2\u03AC +zoom_in=\u039C\u03B5\u03B3\u03AD\u03B8\u03C5\u03BD\u03C3\u03B7 +zoom_in_des=\u039C\u03B5\u03B3\u03AD\u03B8\u03C5\u03BD\u03C3\u03B7 +zoom_out=\u03A3\u03BC\u03AF\u03BA\u03C1\u03C5\u03BD\u03C3\u03B7 +zoom_out_des=\u03A3\u03BC\u03AF\u03BA\u03C1\u03C5\u03BD\u03C3\u03B7 diff --git a/src/jdrafting/resources/language/language_en.properties b/src/jdrafting/resources/language/language_en.properties new file mode 100644 index 0000000..8409af2 --- /dev/null +++ b/src/jdrafting/resources/language/language_en.properties @@ -0,0 +1,480 @@ +# general +app_des=is an open-source drafting application to design small Technical Drawing sketches.
This application is inspired by the 'Classical Construction',
also known as 'Ruler-and-compass Construction' or 'Compass-and-straightedge construction'.
However, another drawing tools can be used.
(More info about classical construction: Wikipedia) +width=Width +height=Height +degrees=deg +not_saved=Not saved +cancel=Cancel +add=Add +remove=Remove + +# labels +thickness=

Line

thickness +point_thickness=

Point

thickness +lbl_fill=Fill +lbl_no_fill=No fill +lbl_line_color=Line color +lbl_point_color=Point color +lbl_fill_color=Fill color + +# actions +new=New exercise +new_des=Start a new exercise +open=Open... +open_des=Open an exercise +save=Save +save_des=Save exercise +save_as=Save as... +save_as_des=Save exercise as +save_image=Save image... +save_image_des=Export exercise as image SVG, PNG +print=Print... +print_des=Print in selected printer +fileinfo=Exercise info +fileinfo_des=Get or modify exercise info +redo=Redo +undo=Undo +move_up=Up shapes +move_up_des=Increase selected shapes index +move_down=Down shapes +move_down_des=Decrease selected shapes index +copy=Copy selected +copy_des=Copy selected shapes +exit=Exit +exit_des=Quit program +about=About... +zoom_all=Zoom all +zoom_all_des=Adjust content to canvas +zoom_in=Zoom in +zoom_in_des=Zoom in +zoom_out=Zoom out +zoom_out_des=Zoom out +delete=Delete selected +delete_des=Delete selected shapes +background_color=Canvas color... +background_color_des=Canvas background color +color=Shape color... +color_des=Shape color +point_color=Point color... +point_color_des=Point color +eyedropper=Eyedropper +eyedropper_des=Capture shape style +paste_style=Paste style +paste_style_des=Paste current style color and stroke +text=See/Hide shape names +text_des=See/Hide shape names +backward=Previous frame +backward_des=Previous frame +forward=Next frame +forward_des=Next frame +end=End frame +end_des=End frame +rewind=Rewind +rewind_des=Rewind +selection=Rectangular selection +selection_des=Select shapes in a rectangular area +invert=Invert selection +invert_des=Invert selection +select_all=Select all +select_all_des=Select all +point=Point +point_des=Create a new point +segment=Segment +segment_des=Create segment +arrow=Arrow +arrow_des=Create single or double arrow +polygon_tools=Polygon tools +reg_poly=Regular polygon +reg_poly_des=Create regular polygon +polygon=Polygon +polygon_des=Create polygon +polyline=Polyline +polyline_des=Create polyline +free_hand=Free hand +free_hand_des=Create an arbitrary shape +rectangle=Rectangle +rectangle_des=Create rectangle +ellipse=Ellipse +ellipse_des=Create ellipse +arc=Arc +arc_des=Create arc +circumference=Circumference +circumference_des=Create circumference +angle=Angle +angle_des=Create a segment by angle +triangle_tools=Triangle tools +triangle=Triangle +triangle_des=Create triangle +incenter=Incenter +ortocenter=Ortocenter +baricenter=Baricenter +circumcenter=Circumcenter +comment=Comment +comment_des=Add comment +spline=Spline +spline_des=Create spline between points +parabola=Parabola +parabola_des=Create parabola from rectangle bounds +hyperbola=Hyperbola +hyperbola_des=Create hyperbola from rectangle bounds +func_des=Create graph from function +func=Function +ruler=Ruler +ruler_des=Capture distance +protractor=Protractor +protractor_des=Capture angle +divisions=Division points +divisions_des=Divide shape in points +modify=Modify segment +modify_des=Modify a segment along its container straight +midpoint=Midpoint +midpoint_des=Set midpoint in the center of the shape bounds +centroid=Centroid +vertex=Vertex +vertex_des=Add vertex points to shape +extremes=Extremes +extremes_des=Add extreme vertex to open shapes +inter=Intersection points +inter_des=Select two or more shapes (none points) to add intersections between them +fragment=Fragment shape sides +fragment_des=Fragment shape sides into individual segments +fusion=Fusion shapes +fusion_des=Select shapes to be merged. Shift for connect extremes +bounds=Rectangle bounds +bounds_des=Create rectangle bounds of a shape +perp=Perpendicular +perp_des=Create perpendicular from segment +para=Parallel +para_des=Create parallel from segment +mediatrix=Mediatrix +mediatrix_des=Create mediatrix of a segment +bisectrix=Bisectrix +bisectrix_des=Create bisectrix +capable_arc=Capable arc +capable_arc_des=Create capable arc of a segment +translation=Translate +translation_des=Translate selected shapes +rotation=Rotate +rotation_des=Rotate selected shapes +homothety=Homothety +homothety_des=Homothety of the selected shapes +central_sym=Central symmetry +central_sym_des=Central symmetry of the selected shapes +axial_sym=Axial symmetry +axial_sym_des=Axial symmetry of the selected shapes +paint=Paint +paint_des=Paint shape +fix_dist=Fix
distance +paste=Paste +paste_des=Paste selected shapes +fill=Fill color +fill_des=Fill color of the shapes +area_intersection=Shape intersection +area_intersection_des=Shape from selected intersected shapes +area_union=Shape union +area_union_des=Shape from union of the selected shapes +area_substract=Shape difference +area_substract_des=Shape from the difference of two shapes +area_sym_diff=Symmetric difference +area_sym_diff_des=Symmetric difference of the selected shapes + +# menus +file=File +edit=Edit +style=Style +shapes=Shapes +exercise=Exercise +transforms=Transforms +conics=Conics +view=View +appearance=Appearance +help=Help + +item_style=Show/hide style bar +item_ruler_prot=Show/hide ruler&protractor bar +item_status=Show/hide status bar +item_action=Show/hide action bar +item_shape=Show/hide shape bar +item_tool=Show/hide tool bar +show_all=Show all +hide_all=Hide all + +# toolbars +tools=Tools +actions=Actions +tit_style=Line/Point style +ruler_prot=Ruler and Protractor + +# dialogs +exit_msg=Ignore changes? +exit_dlg=Exit +inter_msg=Select two or more shapes (none points) +inter_dlg=Intersection points error +ext_dlg=The shape must be an open shape +ext_title=Error while selecting shape +bisectrix_dlg=Parallel or coincident segments +bisectrix_title=Error while calculating bisectrix +div_dlg=Number of divisions +reg_poly_dlg=Number of vertex/sides +save_image_acce1=Image setting +save_image_acce2=Include background +save_image_acce3=Include shape names +overwrite1=File exists, overwrite? +overwrite2=Overwrite +exer_prop=Exercise properties +title=Title +exer_desc=Exercise description +start_frame=Start frame +shape_name=Shape identifier +shape_prop=Shape properties +shape_desc=Shape description +save_close=Save and Close +details=Details +selected_shapes_msg=You must select at least one shape +homo_dlg=Homothety factor +dist_mult_dlg=Multiply distance by factor +jme_dlg=JME expression +jme_examples=examples +jme_doc=JME documentation +jme_min=Min 'x' or 't' value +jme_max=Max 'x' or 't' value +jme_intervals=Number of intervals +save_error1=Error when saving +save_error2=Empty exercise +sel_2_error=Select two o more shapes +inter_error=No joins +empty_intersection_error=Empty intersection +empty_union_error=Empty union +empty_substract_error=Empty difference +empty_sym_diff_error=Empty symmetric difference + +# new shapes default description +new_point=point +new_segment=segment +new_segment_extreme=segment extreme +new_arc=arc +new_circumference=circumference +new_circumference_center=circumference center +new_arrow=arrow +new_triangle=triangle +new_rectangle=rectangle +new_square=square +new_ellipse=ellipse +new_regular_polygon=regular polygon +new_polygon=polygon +new_polyline=polyline +new_v_axis=vertical axis +new_h_axis=horizontal axis +new_parabola=parabola +new_spline=spline +new_free_hand=free hand +new_midpoint=> midpoint of +new_centroid=> centroid of +new_vertex=> vertex of +new_extreme=> extreme of +new_parallel=> parallel to +new_perpendicular=> perpendicular to +new_mediatrix=> mediatrix of +new_bisectrix=> bisectrix of +new_capable_arc=> capable arc of +new_fragment=> fragment of +new_div=> division point of +new_join=> intersection point between +new_bounds=> bounds of +new_fusion=> fusion of +new_focus=focus +new_para_vertex=parabola vertex +new_para_bounds=parabola bounds +new_directrix=directrix +new_hype_branch=hyperbola branch +new_hype_vertex=hyperbola vertex +new_hype_bounds=hyperbola bounds +new_hype_center=hyperbola center +new_main_axis=hyperbola main axis +new_img_axis=hyperbola imaginary axis +new_circumscribed=circumscribed circumference +new_center_reg=regular polygon center +new_intersection=> intersection of +new_union=> union of +new_substract=> substract of +new_sym_diff=> symmetric difference of + +# status msg +txt_hand=Use mouse wheel to zoom. Mouse dragging with second buttom to move shapes. Shift over shape for select/unselect several shapes. Drag to move viewport +txt_eyedropper=Select shape to capture line style. Shift for capture point style. Control to capture color on screen +txt_paste_style1=Select shape to apply line style. Control for don't apply fill. Shift to apply point style +txt_ruler1=Select start point +txt_ruler2=Select end point. Shift for add to current distance. Control for substract +txt_prot1=Select vertex angle +txt_prot2=Select first side point +txt_prot3=Select second side point +txt_sel1=Drag mouse to select a rectangular area. Control to force full shape selection +txt_point=Select point position. Control for non-intersection adjusting. Shift to use line style instead of point style +txt_seg1=Select segment start point +txt_seg2=Select segment end point. Shift for 45º fixed angles. Control to add extremes +txt_arrow1=Select arrow start point +txt_arrow2=Select arrow end point. Shift for 45º fixed angles. Control for double arrow +txt_circ1=Select circumference center. Shift for switch radius/diameter +txt_circ2=Select radius. Shift for switch radius/diameter. Control to add center +txt_arc1=Select arc center +txt_arc2=Select start angle +txt_arc3=Select end angle. Control for conjugate angle. Shift for fixed angle. Alt Gr for integer angles +txt_angle1=Select vertex +txt_angle2=Select end point of the first side +txt_angle3=Select end point of the second side. Shift for suplementary angle. Control for complementary angle. Alt for include both sides +txt_rect1=Select rectangle start point +txt_rect2=Move mouse to select rectangle end point. Shift for square. You can use ruler +txt_ellipse1=Select center or corner. Shift for alternate center/corner mode +txt_ellipse2=Select ellipse size. Control to add axis and focuses. Shift for alternate center/corner mode. You can use ruler +txt_poly=Select vertex. Double click to finish +txt_free1=Drag mouse to draw free shape. Double click to finish +txt_modify1=Select segment to modify +txt_modify2=Select first extreme +txt_modify3=Select second extreme. Shift for current line style instead of old segment style +txt_midpoint=Select shape to add midpoint of the shape bounds. Shift to use line style. Control for centroid +txt_vtx1=Select shape to add vertex. Shift for line style +txt_ext1=Select shape to add extreme vertex (must be an open shape) +txt_div1=Select shape to add divisions points. You can use Fix distance +txt_fragment1=Select shape to fragment sides. Press Shift for use current stroke instead of shape stroke +txt_bounds1=Select shape to add rectangle bounds +txt_perp1=Select segment +txt_perp2=Select perpendicular start +txt_perp3=Select perpendicular end +txt_para1=Select segment +txt_para2=Select parallel start +txt_para3=Select parallel end +txt_mediatrix1=Select segment +txt_mediatrix2=Select mediatrix start +txt_mediatrix3=Select mediatrix end +txt_bisectrix1=Select first segment +txt_bisectrix2=Select second segment +txt_bisectrix3=Select first bisectrix extreme +txt_bisectrix4=Select second bisectrix extreme +txt_cap1=Select segment +txt_cap2=Select one of the two sides +txt_translate1=Select first point of direction vector +txt_translate2=Select second point of direction vector. You can use ruler +txt_rotation1=Select rotation center +txt_rotation2=Move mouse to selectangle. Shift for fixed angle. Control for integer angles +txt_homothety1=Select homothety center. Shift for negative factor. Control for inverted factor +txt_central_sym1=Select symmetry center +txt_axial_sym1=Select first point of axis +txt_axial_sym2=Select second point of axis +txt_triangle_points1=Select three points. Shift for equilateral. Control for right +txt_triangle_points2=Select three points. Shift for line style +txt_parabola1=Select parabola bounds start point +txt_parabola2=Move mouse to select parabola bounds end point. Control to add additional shapes. Shift for square bounds. You can use ruler +txt_hyperbola1=Select hyperbola bounds start point +txt_hyperbola2=Move mouse to select end point. You can use the ruler +txt_hyperbola3=Move mouse to select main axis. Control to add auxiliary shapes +txt_reg_poly1=Select center of the polygon +txt_reg_poly2=Select radius. Control to add auxiliary shapes. You can use ruler +txt_paint1=Select shape to fill. Control for delete fill. Alt for line color. Shift for point color +txt_comment1=Select start point of the text box +txt_area_substract1=Select first shape +txt_area_substract2=Select second shape +txt_jme1=Select start point for coordinates origin +txt_jme2=Select end point for x axis + +# mnemonics +mne_menu_file=f +mne_menu_edit=e +mne_menu_style=s +mne_menu_shapes=h +mne_menu_tools=t +mne_menu_exercise=x +mne_menu_view=v +mne_menu_help=l +mne_menu_transform=t +mne_menu_trian=t +mne_menu_poly=o +mne_menu_conics=i +mne_menu_appea=a + +mne_about=a +mne_angle=n +mne_arc=a +mne_arrow=r +mne_sym_axial=a +mne_backward=p +mne_baricenter=b +mne_bisectrix=b +mne_bounds=g +mne_canvas_col=c +mne_cap_arc=c +mne_sym_central=c +mne_circ=c +mne_circumcenter=c +mne_copy=c +mne_delete=t +mne_divisions=s +mne_ellipse=e +mne_end=e +mne_ex_metadata=x +mne_exit=e +mne_extremes=e +mne_eyedropper=e +mne_forward=n +mne_fragment=f +mne_free=f +mne_fusion=u +mne_homo=h +mne_hyperbola=h +mne_incenter=i +mne_intersection=n +mne_invert_sel=i +mne_jme=u +mne_mediatrix=m +mne_midpoint=i +mne_mod_seg=y +mne_z_up=p +mne_z_down=d +mne_new=n +mne_open=o +mne_ortocenter=o +mne_parabola=p +mne_parallel=a +mne_paste=a +mne_perp=p +mne_point=p +mne_point_col=p +mne_polygon=p +mne_polyline=o +mne_protractor=o +mne_rect=e +mne_redo=r +mne_reg_pol=r +mne_rewind=r +mne_rotation=r +mne_ruler=l +mne_save=s +mne_save_as=a +mne_save_img=v +mne_print=p +mne_segment=s +mne_sel_all=l +mne_selection=e +mne_shape_col=s +mne_spline=l +mne_text=s +mne_trans=t +mne_triangle=t +mne_undo=u +mne_vertex=v +mne_zoom_all=z +mne_zoom_in=o +mne_zoom_out=m +mne_paint=i +mne_comment=m +mne_shape_fill=f +mne_union=u +mne_sym_diff=y + +# toast +toast_copy=shape/s copied +toast_no_copy=nothing selected +toast_paste=shape/s pasted + +# tooltips +tip_swap_color=Swap colors +tip_undo_redo=Undo/Redo history diff --git a/src/jdrafting/resources/language/language_es.properties b/src/jdrafting/resources/language/language_es.properties new file mode 100644 index 0000000..802ea6b --- /dev/null +++ b/src/jdrafting/resources/language/language_es.properties @@ -0,0 +1,480 @@ +# general +app_des=es una aplicación open-source para diseñar pequeños ejercicios de Dibujo Técnico.
Esta aplicación está inspirada por la 'Construcción clásica',
también conocida como 'Construcción de regla y compás'.
Sin embargo, otras herramientas de trazado pueden usarse.
(Más info sobre construcción clásica: Wikipedia) +width=Ancho +height=Alto +degrees=grad +not_saved=No guardado +cancel=Cancelar +add=Añadir +remove=Eliminar + +# labels +thickness=

Grosor

de línea +point_thickness=

Grosor

de punto +lbl_fill=Fill +lbl_no_fill=Sin relleno +lbl_line_color=Color línea +lbl_point_color=Color punto +lbl_fill_color=Color relleno + +# actions +new=Nuevo +new_des=Empezar un nuevo ejercicio +open=Abrir... +open_des=Abrir un ejercicio +save=Guardar +save_des=Guardar ejercicio +save_as=Guardar como... +save_as_des=Guardar ejercicio como +save_image=Guardar imagen... +save_image_des=Exportar ejercicio como imagen SVG, PNG +print=Imprimir... +print_des=Imprimir en la impresora seleccionada +fileinfo=Ejercicio info +fileinfo_des=Obtener o modificar la información del ejercicio +redo=Rehacer +undo=Deshacer +move_up=Subir figuras +move_up_des=Incrementar índice de las figuras seleccionadas +move_down=Bajar figuras +move_down_des=Decrementar índice de las figuras seleccionadas +copy=Copiar seleccionadas +copy_des=Copiar figuras seleccionadas +exit=Salir +exit_des=Salir +about=Acerca de... +zoom_all=Zoom a todo +zoom_all_des=Ajustar contenido a lienzo +zoom_in=Aumentar Zoom +zoom_in_des=Aumentar Zoom +zoom_out=Disminuir Zoom +zoom_out_des=Disminuir Zoom +delete=Borrar seleccionadas +delete_des=Borrar figuras seleccionadas +background_color=Color del lienzo... +background_color_des=Color del lienzo +color=Color de la figura... +color_des=Color de la figura +point_color=Color de punto... +point_color_des=Color de punto +eyedropper=Cuentagotas +eyedropper_des=Capturar estilo de figura +paste_style=Pegar estilo +paste_style_des=Pegar actual estilo y color de línea +text=Mostrar/ocultar nombre figuras +text_des=Mostrar/ocultar nombre figuras +backward=Paso previo +backward_des=Paso previo +forward=Siguiente paso +forward_des=Siguiente paso +end=Final +end_des=Final +rewind=Inicio +rewind_des=Inicio +selection=Selección rectangular +selection_des=Seleccionar figuras en un área rectangular +invert=Invertir selección +invert_des=Invertir selección +select_all=Seleccionar todo +select_all_des=Seleccionar todo +point=Punto +point_des=Crear un punto nuevo +segment=Segmento +segment_des=Crear segmento +arrow=Flecha +arrow_des=Crear flecha simple o doble +polygon_tools=Herramientas de polígono +reg_poly=Polígono regular +reg_poly_des=Crear polígono regular +polygon=Polígono +polygon_des=Crear polígono +polyline=Polilínea +polyline_des=Crear polilínea +free_hand=Mano libre +free_hand_des=Crear una figura arbitraria +rectangle=Rectangulo +rectangle_des=Crear rectángulo +ellipse=Elipse +ellipse_des=Crear elipse +arc=Arco +arc_des=Crear arco +circumference=Circunferencia +circumference_des=Crear circunferencia +angle=Ángulo +angle_des=Crear un segmento por ángulo +triangle_tools=Herramientas de triángulo +triangle=Triángulo +triangle_des=Crear triángulo +incenter=Incentro +ortocenter=Ortocentro +baricenter=Baricentro +circumcenter=Circuncentro +comment=Comentario +comment_des=Añadir comentario +spline=Spline +spline_des=Crear spline entre puntos +parabola=Parábola +parabola_des=Crear parábola a partir de rectángulo frontera +hyperbola=Hipérbola +hyperbola_des=Crear hipérbola a partir de rectángulo frontera +func_des=Crear gráfico desde función +func=Función +ruler=Regla +ruler_des=Capturar distancia +protractor=Transportador +protractor_des=Capturar ángulo +divisions=Puntos de división +divisions_des=Dividir figura en puntos +modify=Modificar segmento +modify_des=Modifica un segmento en su recta contenedora +midpoint=Punto medio +midpoint_des=Punto medio del rectángulo contenedor de la figura +centroid=Centroide +vertex=Vértices +vertex_des=Añadir vértices a la figura +extremes=Extremos +extremes_des=Añadir extremos a una figura no cerrada +inter=Puntos de intersección +inter_des=Seleccionar dos o más figuras (no puntos) para añadir intersecciones entre ellas +fragment=Fragmentar lados +fragment_des=Fragmenta una figura en sus lados constituyentes +fusion=Mezclar figuras +fusion_des=Seleccionar figuras para ser mezcladas. Shift para conectar extremos +bounds=Rectángulo frontera +bounds_des=Crear rectángulo frontera de una figura +perp=Perpendicular +perp_des=Perpendicular de un segmento +para=Paralela +para_des=Paralela a un segmento +mediatrix=Mediatriz +mediatrix_des=Mediatriz de un segmento +bisectrix=Bisectriz +bisectrix_des=Bisectriz entre dos segmentos +capable_arc=Arco capaz +capable_arc_des=Arco capaz de un segmento +translation=Trasladar +translation_des=Trasladar figuras seleccionadas +rotation=Rotar +rotation_des=Rotar figuras seleccionadas +homothety=Homotecia +homothety_des=Homotecia de las figuras seleccionadas +central_sym=Simetría central +central_sym_des=Simetría central de las figuras seleccionadas +axial_sym=Simetría axial +axial_sym_des=Simetría axial de las figuras seleccionadas +paint=Pintar +paint_des=Pintar figura +fix_dist=Fijar
distancia +paste=Pegar +paste_des=Pegar figuras seleccionadas +fill=Color de relleno\u2026 +fill_des=Color de relleno de las figuras +area_intersection=Intersección de figuras +area_intersection_des=Figura a partir de la intersección de las seleccionadas +area_union=Unión de figuras +area_union_des=Figura a partir de la unión de las seleccionadas +area_substract=Diferencia de figuras +area_substract_des=Figura a partir de la diferencia de otras dos +area_sym_diff=Diferencia simétrica de figuras +area_sym_diff_des=Figura a partir de la diferencia simétrica de las seleccionadas + +# menus +file=Archivo +edit=Editar +style=Estilo +shapes=Formas +exercise=Ejercicio +transforms=Transformaciones +conics=Cónicas +view=Vista +appearance=Apariencia +help=Ayuda + +item_style=Mostrar/Ocultar barra de estilo +item_ruler_prot=Mostrar/Ocultar barra de regla y compás +item_status=Mostrar/Ocultar barra de estado +item_action=Mostrar/Ocultar barra de acciones +item_shape=Mostrar/Ocultar barra de figuras +item_tool=Mostrar/Ocultar barra de herramientas +show_all=Mostrar todo +hide_all=Ocultar todo + +# toolbars +tools=Herramientas +actions=Acciones +tit_style=Estilo de línea/punto +ruler_prot=Regla y Transportador + +# dialogs +exit_msg=Ignorar cambios? +exit_dlg=Salir +inter_msg=Seleccione dos o más figuras (puntos no) +inter_dlg=Error en la intersección +ext_dlg=La figura debe ser abierta +ext_title=Error al seleccionar figura +bisectrix_dlg=Segmentos paralelos o coincidentes +bisectrix_title=Error en bisectriz +div_dlg=Número de divisiones +reg_poly_dlg=Número de vértices/lados +save_image_acce1=Parámetros de imagen +save_image_acce2=Incluir fondo +save_image_acce3=Incluir nombre de figuras +overwrite1=El fichero existe, ¿sobreescribir? +overwrite2=Sobreescribir +exer_prop=Propiedades del ejercicio +title=Título +exer_desc=Descripción del ejercicio +start_frame=Frame inicial +shape_name=Nombre de la figura +shape_prop=Propiedades de la figura +shape_desc=Descripción de la figura +save_close=Guardar y cerrar +details=Detalles +selected_shapes_msg=Se debe seleccionar al menos una figura +homo_dlg=Factor de homotecia +dist_mult_dlg=Multiplicar distancia por factor +jme_dlg=Expresión JME +jme_examples=ejemplos +jme_doc=Documentación JME +jme_min=Mínimo valor 'x' o 't' +jme_max=Máximo valor 'x' o 't' +jme_intervals=Número de intervalos +save_error1=Error al guardar +save_error2=El ejercicio está vacío +sel_2_error=Seleccione dos o más figuras +inter_error=No hay intersecciones +empty_intersection_error=Intersección vacía +empty_union_error=Unión vacía +empty_substract_error=Diferencia vacía +empty_sym_diff_error=Diferencia simétrica vacía + +# new shapes default description +new_point=punto +new_segment=segmento +new_segment_extreme=extremo de segmento +new_arc=arco +new_circumference=circunferencia +new_circumference_center=centro circunferencia +new_arrow=flecha +new_triangle=triángulo +new_rectangle=rentángulo +new_square=cuadrado +new_regular_polygon=polígono regular +new_polygon=polígono +new_polyline=polilínea +new_ellipse=elipse +new_v_axis=eje vertical +new_h_axis=eje horizontal +new_parabola=parábola +new_spline=spline +new_free_hand=mano alzada +new_midpoint=> punto medio de +new_centroid=> centroide de +new_vertex=> vértice de +new_extreme=> extremo de +new_parallel=> paralela a +new_perpendicular=> perpendicular a +new_mediatrix=> mediatriz de +new_bisectrix=> bisectriz de +new_capable_arc=> arco capaz de +new_fragment=> fragmento de +new_div=> division de +new_join=> punto de intersección entre +new_bounds=> frontera de +new_fusion=> fusión de +new_focus=foco +new_para_vertex=vértice de parábola +new_para_bounds=frontera de parábola +new_directrix=directriz +new_hype_branch=rama de hipérbola +new_hype_vertex=vértice de hipérbola +new_hype_bounds=frontera de hipérbola +new_hype_center=centro de hipérbola +new_main_axis=eje principal de hipérbola +new_img_axis=eje imaginario de hipérbola +new_circumscribed=circunferencia circunscrita +new_center_reg=centro de polígono regular +new_intersection=> intersección de +new_union=> unión de +new_substract=> diferencia de +new_sym_diff=> diferencia simétrica de + +# status msg +txt_hand=Use rueda de ratón para hacer zoom. Arrastrar ratón con botón secundario para mover figuras. Shift sobre la figura para seleccionar/deseleccionar. +txt_eyedropper=Seleccionar figura para capturar estilo de línea. Shift para capturar estilo de punto. Control para capturar color en pantalla +txt_paste_style1=Seleccionar figura para aplicar estilo de línea. Control para no aplicar relleno. Shift para aplicar estilo de punto. +txt_ruler1=Seleccionar punto de inicio +txt_ruler2=Seleccionar punto final. Shift para añadir distancia. Control para restar +txt_prot1=Seleccionar vértice del ángulo +txt_prot2=Seleccionar primer lado +txt_prot3=Seleccionar segundo lado +txt_sel1=Arrastrar ratón para seleccionar área rectangular. Control para seleccionar a partir de figura completa +txt_point=Seleccionar posición del punto. Control para no ajustar a intersección. Shift para usar estilo de línea en lugar de punto +txt_seg1=Seleccionar punto de inicio +txt_seg2=Seleccionar punto final. Shift para ángulos fijos cada 45º. Control para añadir extremos +txt_arrow1=Seleccionar punto de inicio +txt_arrow2=Seleccionar punto final. Shift para ángulos fijos cada 45º. Control para flecha doble +txt_circ1=Seleccionar centro de la circunferencia. Shift para intercambiar radio/diametro +txt_circ2=Seleccionar radio. Shift para intercambiar radio/diametro. Control para añadir centro +txt_arc1=Seleccionar centro del arco +txt_arc2=Seleccionar ángulo inicial +txt_arc3=Seleccionar ángulo final. Control para ángulo conjugado. Shift para ángulo fijo. Alt para ángulos enteros +txt_angle1=Seleccionar vértice +txt_angle2=Seleccionar primer lado +txt_angle3=Seleccionar segundo lado. Shift para ángulo suplementario. Control para ángulo complementario. Alt para añadir ambos lados +txt_rect1=Selecionar punto inicial del rectángulo +txt_rect2=Mover ratón para seleccionar punto final. Shift para cuadrado. Puede usar la regla +txt_ellipse1=Seleccionar centro o esquina. Shift para modo centro/esquina +txt_ellipse2=Seleccionar tamaño de la elipse. Control para añadir ejes y focos. Shift para modo centro/esquina. Puede usar la regla +txt_poly=Seleccionar vértices. Doble click para terminar +txt_free1=Arrartre ratón para dibujar una figura arbitraria. Doble click para terminar +txt_modify1=Seleccionar segmento a modificar +txt_modify2=Seleccionar primer extremo +txt_modify3=Seleccionar segundo extremo. Shift para actual estilo de línea en lugar del estilo de la figura +txt_midpoint=Seleccionar figura para añadir punto medio del rectángulo frontera. Shift para usar estilo de línea. Control para centroide +txt_vtx1=Seleccione figura para añadir vértices. Shift para estilo de línea +txt_ext1=Seleccione figura para añadir extremos (debe ser una figura abierta) +txt_div1=Seleccione figura para añadir puntos de división. Puede usar la regla para una distancia fijada +txt_fragment1=Seleccionar figura para fragmentar lados. Shift para usar estilo actual en lugar de estilo de figura +txt_bounds1=Seleccionar figura para añadir rectángulo frontera +txt_perp1=Seleccione segmento +txt_perp2=Seleccione comienzo de la perpendicular +txt_perp3=Seleccione fin de la perpendicular +txt_para1=Seleccione segmento +txt_para2=Seleccione comienzo de la paralela +txt_para3=Seleccione fin de la paralela +txt_mediatrix1=Seleccione segmento +txt_mediatrix2=Seleccione comienzo de la mediatriz +txt_mediatrix3=Seleccione final de la mediatriz +txt_bisectrix1=Seleccione primer segmento +txt_bisectrix2=Seleccione segundo segmento +txt_bisectrix3=Seleccione comienzo de la bisectriz +txt_bisectrix4=Seleccione final de la bisectriz +txt_cap1=Seleccione segmento +txt_cap2=Seleccione uno de los dos lados +txt_translate1=Seleccione primer punto del vector de dirección +txt_translate2=Seleccione segundo punto del vector de dirección. Puede usar la regla +txt_rotation1=Seleccione centro de rotación +txt_rotation2=Mueva ratón para seleccionar ángulo. Shift para ángulos fijos. Control para ángulos enteros +txt_homothety1=Seleccione centro de homotecia. Shift para factor negativo. Control para invertir factor +txt_central_sym1=Seleccione centro de simetría +txt_axial_sym1=Seleccione primer punto del eje +txt_axial_sym2=Seleccione segundo punto del eje +txt_triangle_points1=Seleccione tres puntos. Shift para equilátero. Control para rectángulo +txt_triangle_points2=Seleccione tres puntos. Shift para estilo de línea +txt_parabola1=Selecionar punto inicial de la frontera de la parábola +txt_parabola2=Mover ratón para seleccionar punto final. Control para añadir figuras auxiliares. Shift para frontera cuadrada. Puede usar la regla +txt_hyperbola1=Selecionar punto inicial de la frontera de la hipérbola +txt_hyperbola2=Mover ratón para seleccionar punto final. Puede usar la regla +txt_hyperbola3=Mover ratón para seleccionar eje principal. Control para añadir figuras auxiliares +txt_reg_poly1=Seleccione centro del polígono +txt_reg_poly2=Seleccione radio. Control para añadir figuras auxiliares. Puede usar la regla +txt_paint1=Seleccionar figura para rellenar. Control para eliminar relleno. Alt para usar color de línea. Shift para usar color de punto +txt_comment1=Selecionar punto inicial de la caja de texto +txt_area_substract1=Seleccionar primera figura +txt_area_substract2=Seleccionar segunda figura +txt_jme1=Seleccionar punto inicial del origen de coordenadas +txt_jme2=Seleccionar punto final del eje de abcisas + +# mnemonics +mne_menu_file=a +mne_menu_edit=e +mne_menu_style=s +mne_menu_shapes=f +mne_menu_tools=h +mne_menu_exercise=j +mne_menu_view=v +mne_menu_help=y +mne_menu_transform=t +mne_menu_trian=h +mne_menu_poly=e +mne_menu_conics=i +mne_menu_appea=a + +mne_about=a +mne_angle=n +mne_arc=a +mne_arrow=f +mne_sym_axial=i +mne_backward=p +mne_baricenter=b +mne_bisectrix=b +mne_bounds=c +mne_canvas_col=l +mne_cap_arc=r +mne_sym_central=s +mne_circ=c +mne_circumcenter=c +mne_copy=c +mne_delete=o +mne_divisions=n +mne_ellipse=e +mne_end=f +mne_ex_metadata=e +mne_exit=s +mne_extremes=e +mne_eyedropper=u +mne_forward=s +mne_fragment=f +mne_free=m +mne_fusion=z +mne_homo=h +mne_hyperbola=h +mne_incenter=i +mne_intersection=s +mne_invert_sel=i +mne_jme=u +mne_mediatrix=m +mne_midpoint=u +mne_mod_seg=o +mne_z_up=u +mne_z_down=b +mne_new=n +mne_open=a +mne_ortocenter=o +mne_parabola=p +mne_parallel=a +mne_paste=p +mne_perp=p +mne_point=p +mne_point_col=o +mne_polygon=o +mne_polyline=l +mne_protractor=d +mne_rect=r +mne_redo=r +mne_reg_pol=p +mne_rewind=i +mne_rotation=r +mne_ruler=g +mne_save=g +mne_save_as=u +mne_save_img=r +mne_print=i +mne_segment=s +mne_sel_all=e +mne_selection=s +mne_shape_col=c +mne_spline=l +mne_text=s +mne_trans=t +mne_triangle=t +mne_undo=d +mne_vertex=v +mne_zoom_all=z +mne_zoom_in=u +mne_zoom_out=d +mne_paint=i +mne_comment=o +mne_shape_fill=f +mne_union=u +mne_sym_diff=i + +# toast +toast_copy=figura/s copiada/s +toast_no_copy=nada seleccionado +toast_paste=figura/s pegada/s + +# tooltips +tip_swap_color=Intercambiar colores +tip_undo_redo=Historial deshacer/rehacer