diff options
Diffstat (limited to 'kig')
563 files changed, 59270 insertions, 0 deletions
diff --git a/kig/AUTHORS b/kig/AUTHORS new file mode 100644 index 00000000..9f44cc84 --- /dev/null +++ b/kig/AUTHORS @@ -0,0 +1 @@ +Kig developers <kde-edu-devel@kde.org> diff --git a/kig/COPYING b/kig/COPYING new file mode 100644 index 00000000..0b84a43f --- /dev/null +++ b/kig/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/kig/ChangeLog b/kig/ChangeLog new file mode 100644 index 00000000..ec8d6042 --- /dev/null +++ b/kig/ChangeLog @@ -0,0 +1,1600 @@ +2005-09-29 Maurizio Paolini <paolini@dmf.unicatt.it> + + * Fix drawing of arcs with very small angle (patch by + F. Pasquarelli). + +2005-09-27 Maurizio Paolini <paolini@dmf.unicatt.it> + + * Imported PyKig, and made install as a binary script. + +2005-09-16 Pino Toscano <toscano.pino@tiscali.it> + + * Move a text label by (2,2), so the attached ones should be less + sticky to the "parent" object. + +2005-09-08 Pino Toscano <toscano.pino@tiscali.it> + + * Fix two bugs, patch by Maurizio Paolini. + +2005-09-02 Pino Toscano <toscano.pino@tiscali.it> + + * Slightly change in the text when asking for the results of a + macro: tell the user that a macro can construct more than one + object per time. + +2005-08-25 Inge Wallin <inge@lysator.liu.se> + + * Fix bug 111452: Code violates C++ spec with improper const and + iterator declarations. + +2005-08-15 Pino Toscano <toscano.pino@tiscali.it> + + * Bump the version to 0.10.5 (I don't think to have enough stuff + to have a 0.11 version - at least at the moment). + + * Small changes in the credits, putting me as maintainer. + + * Small update in the TODO. + + * Avoid setting empty captions in the KigFileDialog. + + * Update a bit one tip. + + * Add apidox comments for two classes. + +2005-07-30 Pino Toscano <toscano.pino@tiscali.it> + + * With the help of the kde-usability team, I've refactored the Kig + exporter dialogs. These new ones are more usable than the previous + ones, more simply and more maintainable. This could make us + closing bug #101072. + +2005-07-25 Pino Toscano <toscano.pino@tiscali.it> + + * Apidox improvements. + +2005-07-24 Pino Toscano <toscano.pino@tiscali.it> + + * More apidox fixes. + +2005-07-23 Pino Toscano <toscano.pino@tiscali.it> + + * Apidox fixes. + +2005-07-06 Pino Toscano <toscano.pino@tiscali.it> + + * Apidox improvements. + +2005-06-24 Pino Toscano <toscano.pino@tiscali.it> + + * When making the template fo a new script, give as args names the + names of the related objects, if they have one, as suggested by + Daniel Moyne. Otherwise, will be used argn as usual. + +2005-06-20 Pino Toscano <toscano.pino@tiscali.it> + + * Moved the EditAngleSize dialog to the KigInputDialog class. + + * Handle Return and Escape as key shortcuts to respectively accept + or reject a KigInputDialog. + +2005-06-19 Pino Toscano <toscano.pino@tiscali.it> + + * Creating a new class, KigInputDialog. This new class is much + like KInputDialog, but it fits better Kig purpouses. At the moment + it has two methods to get one or two coordinates, made in a + cleaner way than the former ZoomArea class and + CoordinateSystem::getCoordFromUser(). Using it instead of the + former class/method listed above. This way we can construct a + custom input dialog accepting also markup text, allowing us to + give instructions much clear when we want the user insert a + coordinate, fixing also the bug #100007. + +2005-06-16 Pino Toscano <toscano.pino@tiscali.it> + + * Re-enable the "snap" to points in construct mode. + +2005-06-14 Pino Toscano <toscano.pino@tiscali.it> + + * Give to the user the possbility to select which element popup a + menu for. + +2005-06-01 Pino Toscano <toscano.pino@tiscali.it> + + * Small changes to make the port to Qt4 less intrusive. + +2005-05-25 Pino Toscano <toscano.pino@tiscali.it> + + * The kfile_kig displays also whether the file is compressed or + not. + +2005-05-21 Pino Toscano <toscano.pino@tiscali.it> + + * Created a new system to select - in normal mode - one object + among some: using Shift (or Control) + LMB the user can select the + object he/she desire through a popup menu. This new system is + applyed also to to mouse hover/left click in construct mode, to + select one object if we have more than one valid object. This, + plus a search among the object under the mouse, allow the fixing + of bug #99870. + + * Added a "tip of day" to inform the user of the possibility to + select any object from an object stack under the cursor. + + * Give a different behaviour for Shift and Control keys in normal + mode: Shift make appear a popup to choose an object from the + object stack under the cursor; Control keep the selection. + +2005-05-19 Pino Toscano <toscano.pino@tiscali.it> + + * Give to the kfile_kig the ability to read info from compressed + files. + +2005-05-15 Danny Allen <dannya40uk@yahoo.co.uk> + + * Added my new action icons for Kig, with SVG sources, changed + icons to "hi*" naming conventions. + +2005-05-14 Pino Toscano <toscano.pino@tiscali.it> + + * Created a new system to select - in normal mode - one object + among some: using Shift (or Control) + LMB the user can select the + object he/she desire through a popup menu. This new system is + applyed also to to mouse hover/left click in construct mode, to + select one object if we have more than one valid object. This, + plus a search among the object under the mouse, allow the fixing + of bug #99870. + + * Added a "tip of day" to inform the user of the possibility to + select any object from an object stack under the cursor. + +2005-05-07 Pino Toscano <toscano.pino@tiscali.it> + + * Using three standard action in our popup menu instead of + creating new actions "from scratch". Removing the Toggle + fullscreen action created by hand, fixes also an ugly bug that + happened when choosing Toggle fullscreen from the popup to exit + from fullscreen. + +2005-05-06 Pino Toscano <toscano.pino@tiscali.it> + + * Made Kig standard names for its icons, so artists can theme them + in their icon themes. Some files needed obviously changes. + +2005-04-30 Pino Toscano <toscano.pino@tiscali.it> + + * When selecting a custom color for one object, put the old object + color as selected color in the Color dialog. + +2005-04-28 Pino Toscano <toscano.pino@tiscali.it> + + * Other work on Cabri filter: can read styles and translations. + +2005-04-27 Pino Toscano <toscano.pino@tiscali.it> + + * Some work also on Cabri filter: fixed a color, and imported four + objects. + +2005-04-25 Pino Toscano <toscano.pino@tiscali.it> + + * Almost rewritten a core part of the KSeg import filter: some + object are handled in a more correct way than before (thus less + crashes, but some crashes still occurs with locuses). As a result, + some objects can be imported correctly now. + +2005-04-20 Pino Toscano <toscano.pino@tiscali.it> + + * Give to the KSeg filter the ability to import names (called + labels) of objects. This is not so perfect, because we need to + decode better the read object label. + + * Fix intersection points loading in KSeg filter. + +2005-04-16 Pino Toscano <toscano.pino@tiscali.it> + + * Fixing a small issue when saving a file with no name set. The + name wasn't empty, but it contained a temp file. Luckly m_bTemp + stores whether using a temp file. This could fix bug #98142. + +2005-04-15 Pino Toscano <toscano.pino@tiscali.it> + + * Correct size for some menu item icons, as reported by Danny + Allen. + + * Better handling of unexistant types found when loading a + document. + + * Corrected two strings, thanks to Danny Allen. + + * Use KStdGuiItem in more places than before. + +2005-04-14 Pino Toscano <toscano.pino@tiscali.it> + + * Committing fix for bug #100292. The new code makes the + ObjectHierarchy construction from QDomElement more safe than + before; the new "static constructor" and + ObjectImpFactory::deserialize() can report an error in case of. + + * As a result of the fix, less assert() asre used in + deserialization code. + +2005-02-21 Pino Toscano <toscano.pino@tiscali.it> + + * Again, bump the version number to 0.10. + +2005-02-10 Dominique Devriese <devriese@kde.org> + + * The rest of a fix for bug #98517. The problem was that + namecalcer's weren't being saved if they weren't shown on the + document. This fixes the previous fix to not crash on faulty + documents, but give a proper parser error. This should + permanently fix #98517. + + * Fix this ChangeLog to document the previous fix for #98517. + +2005-02-07 Maurizio Paolini <paolini@dmf.unicatt.it> + + * Bug fix: new algorithm for calcPath in calcpath.cc; the + previous had exponential complexity for some examples + +2005-02-03 Dominique Devriese <devriese@kde.org> + + * Add a nice new locus example: trifolium-of-delongchamps.kig + +2005-02-03 Pino Toscano <toscano.pino@tiscali.it> + + * Bump the version number to 0.9.1. + +2005-01-31 Maurizio Paolini <paolini@dmf.unicatt.it> + + * added point/line/circle inversion with respect to a given + circle. + +2005-01-26 Maurizio Paolini <paolini@dmf.unicatt.it> + + * added many constructions related to polygons: + sides, center of mass, winding number, convexity + test, convex hull. + + * changes in construct_mode (and in a few other + places) to allow the construction with a "cursor" + point that gives information on the constructed + object, but should not be inserted as argument. + This is used for a better interactive construction + of regular polygons + +2005-01-20 Maurizio Paolini <paolini@dmf.unicatt.it> + + * new "attaching" style for labels to objects, based on the + new RelativePointType. This type is a point located at a + relative position with respect to a given attachPoint() + associated to the ObjectImp. It depends on three arguments: + the object to which the position is relative and two DoubleImp + givin the x and y displacement. Now use for angles and + polygons. + +2005-01-18 Maurizio Paolini <paolini@dmf.unicatt.it> + + * now the construction of a generic affinity and a generic + projectivity (defined by the image of 3 or 4 points) takes + advantage of the existence of polygon. + +2005-01-15 Maurizio Paolini <paolini@dmf.unicatt.it> + + * new PolygonBNPType corresponding to a polygon with a + generic number of vertices. It is constructed by selecting + the vertices and ending up selecting the first vertex again. + + * the new ObjectConstructor::isAlreadySelectedOK method is a + technical addition, it returns false for all normal objects + (meaning that duplicated arguments are not acceptable). This + is introduced in order to implement the construction of + polygons "a la Drgeo". + +2005-01-12 Pino Toscano <toscano.pino@tiscali.it> + + * Created a new KigPainter::drawArea() to draw and fill closed + areas. This new functions differs from drawPolygon, because the + old one is used to draw only polygons, while the new one is used + to draw all the other closed and filled areases. + + * Adapted our CoordinateSystem's to use the new drawArea to draw + the axis arrows. + +2005-01-09 Maurizio Paolini <paolini@dmf.unicatt.it> + + * new action to construct the vertices of a polygon + + * new action to construct a generic triangle (as a polygon) given + its vertices + +2004-12-30 Dominique Devriese <devriese@kde.org> + + * Fix for a bug where a name calcer wasn't saved if it was only + referred to from the object it is the namecalcer for, and not by + its own objectholder. This commit makes sure that no more such + files are generated, and that kig no longer crashes on such + files. Thanks to Pino Toscano for help in fixing this. (#98517) + +2004-12-28 Maurizio Paolini <paolini@dmf.unicatt.it> + + * polygons are filled; no boundary drawn + + * fixed a problem when trasforming a segment with + a projective (nonaffine) transformation; in certain + cases the result is invalid (no longer a segment) + + * fixed a bug in similitude transformation as suggested by + Pino Toscano (#85171) + +2004-12-27 Pino Toscano <toscano.pino@tiscali.it> + + * Fix bug #95637: Scrolling does not work during construction of + a new object + +2004-12-24 Pino Toscano <toscano.pino@tiscali.it> + + * Kig can now save and open compressed files with extension + .kigz. This format is a gzip compressed tarball. + +2004-12-14 Pino Toscano <toscano.pino@tiscali.it> + + * More tooltips and "what's this" to some dialog. + + * No more file name in every file. + + * More control on macro name: when loading from file, if a macro + has no name, we'll assign it a bogus one (like "Unnamed macro + #id"). Furthermore, we ensure that the user don't set an empty + name for a macro in the Edit Types Dialog. + + * Other misc changes. + +2004-11-09 Pino Toscano <toscano.pino@tiscali.it> + + * Made kig.dektop and kig_part.desktop more compliant to + freedesktop.org's desktop entry specifications. + + * Introduced PolygonImp, a new ObjectImp to handle all the types + of polygons (both generic and regular ones). Modified PoligonType + and PoligonBCVConstructor to use the new PolygonImp. Adapted also + the ObjectImpVisitor to visit PolygonImp. + + * Improved the LatexExporter to export PolygonImp's. + + * Fixed a crash that occurs when selecting (during a text label + construction) the property "Name" for an object with no + name(calcer) set. + + * Other misc changes. + +2004-11-08 Maurizio Paolini <paolini@dmf.unicatt.it> + + * Scaling and stretching can be defined using two segments to + obtain the scaling ratio (as the ratio of their length). Added in + the Transformations menu, not in the Transformations toolbar, + which is crowded enough already :-) + +2004-11-06 Maurizio Paolini <paolini@dmf.unicatt.it> + + * Added osculating circle and evolute of a curve as a + builtin macros (based upon the center of curvature). + +2004-10-27 Pino Toscano <toscano.pino@tiscali.it> + + * Add all the constructible regular polygons actions to + kigpartui.rc. + + * Remove the call to KAboutData::setTranslator in kig/aboutdata.h, + since Scripty generate automatically the two strings. + +2004-10-24 Dominique Devriese <devriese@kde.org> + + * Added a few new icons from Bart Van Hove bartvanhove _at_ skynet _dot_ be + +2004-10-23 Maurizio Paolini <paolini@dmf.unicatt.it> + + * Added construction of the center of curvature of a curve + at a point. It works for conics, cubics and locuses. + However a special hack was necessary to make it possible to + construct the locus of the center of curvatures starting from + a locus. Indeed there are problems associated to the getParam + function of a locus, which now returns a result with a too large + error. Simply reducing the "epsilon" in "LocusImp::getParamofmin" + is not feasible since it would degrade the overall response time. + However it is quite frequent that the LocusImp::getParam is called + right after a LocusImp::getPoint, although at a quite different + level, and on curves that are physically the same, but internally + different. Since the two functions are inverse one of the other + we simply cache the value of "param" in getPoint and try to use + the cached value when doing the getParam: if the resulting point + is equal to the argument of getParam we win, otherwise we proceed + along the lengthy minimization process. + A nice example of the involute (locus of centers of curvature) + of a curve can be retrieved from + "www.dmf.unicatt.it/~paolini/kig/cicloide.kig" + The getParam function however requires adjustment: right now the + internal test whether a point lies on a locus miserably fails + even if we test it on the moving point used to construct the + locus :-( + +2004-10-13 Maurizio Paolini <paolini@dmf.unicatt.it> + + * Added construction of the tangent to a locus + +2004-10-11 Maurizio Paolini <paolini@dmf.unicatt.it> + + * Added PoligonBCVType for regular poligons with n sides + given the center and a vertex. + + * Transport of Measure is generalized to allow both + segments and arcs to provide a length and both a line + or a circle with a point on it as the target of the + transport. + +2004-10-09 Pino Toscano <toscano.pino@tiscali.it> + + * Add a CircleByCenterAndDiameter object, to construct a circle + using a point as center and the length of a segment as diameter. + + * A new SVG exporter to make SVG documents starting form Kig + documents. + +2004-10-08 Maurizio Paolini <paolini@dmf.unicatt.it> + + * Added the construction of the center of curvature for + conics and cubics. It works similarly to the construction + of the tangent and the arguments are a curve (conic or cubic) + and a point on in. Using the center of curvature it is + straightforward to construct the osculating circle or the + evolute. + +2004-09-13 Pino Toscano <toscano.pino@tiscali.it> + + * The script code editor now can use the Kate KTextEditor, and + consequently we can use its interfaces to do some various thing, + like the syntax highlight of the code. Moreover if the + KTextEditor::Document could not be created, will be used a + standard KTextEdit as the code editor. + + * Some i18n fixes. + +2004-09-10 Maurizio Paolini <paolini@dmf.unicatt.it> + + * Now it's possible to construct the tanget to a cubic. + +2004-09-05 Pino Toscano <toscano.pino@tiscali.it> + + * A new LaTex exporter. + + * Add a new little system to handle script's properties like + icon, template code and stuff related to script type. + + * Now any change in the Types Dialog is applied when the users + click OK. If Cancel is pressed, any change is lost, even types + addition/deletion. This is done by saving the types when Types + Dialog is started. + + * Now the user can choose Start->Python Script from the popup + menu to start a new Python script with the selected objects as + arguments. If there are no objects selected, the Script Wizard + starts as usual. + + * Add the main Kig icons with size 22 and 64. + + * The KSeg filter can import bisector lines. + + * Add a new tip. + +2004-09-02 Dominique Devriese <devriese@kde.org> + + * Commit David Vignoni's new kig_doc icon + +2004-09-01 Pino Toscano <toscano.pino@tiscali.it> + + * Implement Vector difference as internal macro. + + * The Type list has a popup menu to edit, delete and export types. + + * Small changes to Script Wizard UI. + + * kfile_kig read also the compatibility version. + +2004-07-21 Pino Toscano <toscano.pino@tiscali.it> + + * Make a text label not "pasted" to its point by adding a + "padding" ( 2 pixels every side ). + + * Now the code editor in the Script Wizard uses KDE global fixed + font. + +2004-07-20 Pino Toscano <toscano.pino@tiscali.it> + + * Implement arc-line intersection in KSeg filter. + + * Add support in KGeo filter for constrained points and info + about the presence of grid and axes. + + * Little fix in Cabri filter. + + * Fixed a crash in the Types dialog. + +2004-07-18 Dominique Devriese <devriese@kde.org> + + * Introduce the concept of a CompatibilityVersion. Kig now saves + its files with a compatibility version of 0.7.0, indicating that + the file format has stayed more or less the same since 0.7, and + that an app able to open documents created by Kig 0.7 should + normally not have much problems with this version's files ( apart + from some new document types, but those are handled separately + anyway ). On loading, Kig now first checks the compatibility + version, and falls back to the real version only if the previous + is not available. The CompatibilityVersion will change only on + major file format changes, unlike the normal Version. + + * Move the version back to 0.9, because Kig really isn't in a 1.0 + state yet. Some problems need addressing first. It took me some + time to realise, but calling this release 1.0 would not do credit + to the program's long-term potential. + +2004-07-13 Pino Toscano <toscano.pino@tiscali.it> + + * Move AngleType from other_type.* into angle_type.*. + + * Add a new HalfAngleType object, which returns only an angle + smaller than 180 degrees. This is useful for Dr. Geo angles, + which are always smaller than 180 degrees. + + * Activate and improve Cabri filter. + + * Some improvements in the Types dialog. + + * A new magic file for application/x-cabri. + + * Add CubicCartesianData and Cubic to Python Scripting API. + + * Some fixes in Dr. Geo filter: implement Bordeaux colour; fix + intersection params; Kig shows the name of every point; ignore + Dr. Geo block to UI. + + * Improved the generation of pot catalogs. + + * Now it's possible to construct the tangent to a conic or an + arc. + + * Add a select statement to show a message in the statusbar for + PropertyObjectConstructor, and use this to show an info text + while constructing an angle bisector. + + * A new type to calc the difference between two vectors. + + * ObjectFactory::sensiblePointCalcer can construct line-line + intersections. + + * Fix a bug that occurs when Kig draw a line that has the same + coordinates in its LineData class. + + * Activate Help button in the Script Wizard. + + * Some little improvements. + +2004-07-03 Dominique Devriese <devriese@kde.org> + + * Make the type edit dialog appear when the edit button is + pressed, not when the user clicks on a type. + +2004-06-29 Pino Toscano <toscano.pino@tiscali.it> + + * Some fixes related to the Kig version change. + + * Some fixes in the Dr. Geo filter. + + * Fixed a small bug that leads to display "Select the point" when + I reset the name of an object after setting it. + + * i18n fixes in kfile_kig. + +2004-06-29 Dominique Devriese <devriese@kde.org> + + * Change the version to 1.0 + +2004-06-26 Pino Toscano <toscano.pino@tiscali.it> + + * A new property "Angle" for arcs. + + * Many improvements in the Dr. Geo filter. + +2004-06-15 Pino Toscano <toscano.pino@tiscali.it> + + * Add info about whether grid and axes are shown to the kig kfile + thing. + + * Now the coordinate system toggleaction is correctly updated + when the coordinate system is changed in another way. + + * Disable the page selection in the print dialog. + +2004-06-14 Dominique Devriese <devriese@kde.org> + + * Change some more functions that still pass "bool valid&"s around + to using Coordinate::valid(). + +2004-06-14 Pino Toscano <toscano.pino@tiscali.it> + + * Add "Tips Of Day" feature. + + * A new tab in the Print dialog allow the user to choose whether + to print grid and/or axes. + + * Add arc-line intersection type. + +2004-06-13 Pino Toscano <toscano.pino@tiscali.it> + + * Add the possibility to attach a label ( and a point, of + course :) ) to a vector. This because now VectorImp inherits + CurveImp. + + * Make types modifiable through types dialog. The system is not + complete, because at the moment there is no way to update the UI. + + * The user now can choose manually the zoom area. + + * Get rid of the Invisible coordinate system; now there are two + actions ( Show Grid and Show Axes ) to show/hide grid and axes + separately. I think this is a better way: the Invisible coordinate + system did not allow to work with polar coordinates and no grid. + +2004-06-11 Dominique Devriese <devriese@kde.org> + + * Some improvements by me to Pino's object names code. Mostly, + this includes cleaning the design a bit, and adding support for + the names in some places. + +2004-05-31 Dominique Devriese <devriese@kde.org> + + * Fix some memory leaks found by valgrind. + +2004-05-30 Dominique Devriese <devriese@kde.org> + + * implement usetexts and select statements for builtin macro's + + * Show a text in the statusbar describing what we want the user to + select, in ConstructMode. + + * Add an example of a cubic constructed as a locus. + + * Clean the status bar text on time in ConstructMode and + TestConstructMode. + + * Make SetCoordinateSystemAction into a KSelectAction, so the user + can see what kind of coordinate system he's currently using. + + * Some tuning of the UI: move Transformations and Tests menu into + the Objects menu because the menubar was getting too crowded. + +2004-05-29 Dominique Devriese <devriese@kde.org> + + * Fix a crash related to a locus containing some invalid points, + and added the offending test file in filters/tests. + + * Fix some --enable-final problems in the filters/ directory. + +2004-05-28 Dominique Devriese <devriese@kde.org> + + * Add a test file "testalotofeverything.kig" + + * Add a Similitude transformation. + + * Add a VectorEqualityTestType. + +2004-05-27 Dominique Devriese <devriese@kde.org> + + * Fix so that Kig saves its window settings on exit. + +2004-05-24 Pino Toscano <toscano.pino@tiscali.it> + + * Add the possibility to give a name to every object. The name is + displayed when moving mouse over the object, like "Select this + line (AB)" or choosing the object which attach another object to. + For now there is no label with title displayed with the object. + + * Make the Vector Sum of two vectors starting at an arbitrary + point. + +2004-05-23 Dominique Devriese <devriese@kde.org> + + * replace the custom configureShortcuts code with the standard KDE + one if the user is running HEAD. + +2004-05-18 Pino Toscano <toscano.pino@tiscali.it> + + * Move ArcBTPType from other_type.* into arc_type.*. + + * Add two new types, ArcBCPA ( Arc By Center, starting Point and + Angle ) and CircleBCL ( Circle By Center and Line - via macro ). + + * Now every submenu in the RMB menu can have an own icon. + + * Add a menu icon for every ExportAction. + + * Various little improvements + +2004-05-18 Dominique Devriese <devriese@kde.org> + + * Add a --convert-to-native command line option, which converts + the given file to the native Kig file format ( without a GUI ). + + * Split up KigDocument into KigPart and KigDocument + +2004-05-15 Dominique Devriese <devriese@kde.org> + + * Intersecting with a segment only gives points that really are on + the segment, and an InvalidImp in other cases. + +2004-05-12 Pino Toscano <toscano.pino@tiscali.it> + + * Now circles can display their equation in the form + "( x - x0 )^2 + ( y - y0 )^2 = r^2". + + * Add Doxygen comments for AngleImp, VectorImp and ArcImp + classes, to make them documented in scripting-api documentation. + + * Add a Copy action to text labels to copy their text ( with + substitutions already made ) into the clipboard. + + * Add two little kfile plugins: kfile_kig and kfile_drgeo. + + * Various little improvements + +2004-05-11 Dominique Devriese <devriese@kde.org> + + * Rename the two Transformation::scaling functions to + scalingOverPoint and scalingOverLine, and export them to python. + + * Fix a crash reported by Maurizio: when debugging is enabled, and + one attempts to move an object of a type that inherits + ObjectABType, and depends on a non-movable object. + +2004-05-03 Dominique Devriese <devriese@kde.org> + + * Add a DrGeo test file using a locus + +2004-05-03 Pino Toscano <toscano.pino@tiscali.it> + + * More work on Dr. Geo filter: now locuses should work, fix object + visibility. + +2004-04-30 Pino Toscano <toscano.pino@tiscali.it> + + * Add line and half-line by vector. + + * More work on Dr. Geo filter to support some types of On_curve + points. + +2004-04-30 Dominique Devriese <devriese@kde.org> + + * properly generate python error output for compile errors. + + * make touch screens work by placing a mouseMoved call aboove + every mouseClicked call. + +2004-04-28 Dominique Devriese <devriese@kde.org> + + * change the Qt CapStyle used for drawing locuses, conics and + lines to FlatCap, which gives better results with large line + widths. + + * Add two line styles: DashDotLine and DashDotDotLine + + * Remove the KigPainter::drawConic and KigPainter::drawCubic + functions. Conics and cubics are now drawn with the generic + KigPainter::drawCurve function. The performance penalty is not + noticable for me, and I haven't been able to quantify it in any + way, so I assume it negligible. Cubics and conics drawing now + correctly takes line styles into account. + +2004-04-27 Pino Toscano <toscano.pino@tiscali.it> + + * Implement styles and visibility for KSeg filter. + +2004-04-27 Dominique Devriese <devriese@kde.org> + + * Make locuses work with the line styles, by drawing them with + drawPolyline instead of drawing the individual segments ourselves. + Thanks to Maurizio for the ideas and the help. + + * Fix a stupid bug in the last commit which caused Kig to crash on + older Kig files. + + * Make it possible to switch the radical lines of a conic that are + shown. + +2004-04-24 Dominique Devriese <devriese@kde.org> + + * Add a vector sum object. + + * Move VectorType from other_type.* into vector_type.*. + + * Fix a bug where some objects were not preliminarily drawn + correctly. + +2004-04-23 Dominique Devriese <devriese@kde.org> + + * Add a segment-midpoint icon from Julien Narboux + <Julien.Narboux@inria.fr>. + + * Implement a scheme that automatically instantiates the singleton + ObjectType's. Now, the ObjectType constructor now adds itself to + ObjectTypeFactory, so that we cannot forget to do it. This fixes + loading of files containing property test objects. + + * Remove the old code that removed $appdata/kig-types/*.kigt on + exit, as it's not necessary anymore. + +2004-04-22 Dominique Devriese <devriese@kde.org> + + * Add a "opposite vector" property to VectorImp. + +2004-04-21 Pino Toscano <toscano.pino@tiscali.it> + + * Add a SameDistanceType, to check whether a point have the same + distance from a second point and from a third point. + +2004-04-21 Dominique Devriese <devriese@kde.org> + + * Fix tooltips to not contain "&&" instead of "&" ( closes: 78411 ). + +2004-04-20 Dominique Devriese <devriese@kde.org> + + * Various i18n'able string fixes. + + * Add some documentation to the functions in misc/conic-common.h + + * Rename "Cubic" to "Cubic Curve", as discussed with Maurizio + Paolini and Jaap Woldringh. + + * Save and load line and point styles in the native format. Most + of the code comes from Pino. + +2004-04-19 Pino Toscano <toscano.pino@tiscali.it> + + * More work in Dr. Geo filter. + + * A new cool (I hope :) ) icon for Python Script. + + * Some various improvements. + +2004-04-17 Dominique Devriese <devriese@kde.org> + + * Fix the calculation of the rectangle containing the entire + document ( which is used for centering on the document ), to take + into account non-point objects. This is accomplished by adding a + surroundingRect function to ObjectImp, and by implementing it + properly for all objects that can have such a thing. + +2004-04-15 Pino Toscano <toscano.pino@tiscali.it> + + * More work in Dr. Geo filter: add a new object and simplifying a + bit his internal structure. + + * Kig now ask the user what to do when he/she tries to save to + a file in another format than Kig's own. + + * Improve Python scripting's API: add vectors, angles and arcs. + + * Add i18n for TestConstructor's. + + * Improved NewScriptAction class to support, without other + changes, other scripting languages. + + * Some little here-and-there improvements + +2004-04-11 Dominique Devriese <devriese@kde.org> + + * Improve Kig embedded in Konqueror experience: make translations + and icons work by using the correct instanceName(), and using the + iconLoader we get from our KInstance instead of from KGlobal ( so + that the kig specific dirs are checked for icons as well ). + +2004-04-10 Dominique Devriese <devriese@kde.org> + + * Enable the DrGeo input filter. + +2004-04-10 Pino Toscano <toscano.pino@tiscali.it> + + * Improved angle size editing, making possible choosing between + degrees, radians and gradians. + + * More work in Dr. Geo filter. + + * Some improvements in Goniometry class + +2004-04-08 Pino Toscano <toscano.pino@tiscali.it> + + * More work on types dialog: a new dialog will allow to edit + types that now work in read-only mode. + + * Add a 'cross' style for points. + + * Some very minor work in Dr. Geo filter. + + * Add a new simple class to easily work with goniometric + measures, and adapt Kig to use this class. + + * Some #include fixes + +2004-04-05 Pino Toscano <toscano.pino@tiscali.it> + + * A new look for type list in the type dialog: a listview instead + of a listbox that should make easier editing a type. + +2004-04-04 Pino Toscano <toscano.pino@tiscali.it> + + * Now Kig can save icon information of every macro. + + * Some work on types dialog: icon of every type is now visible, + and made a sort of skeleton to modify type's data. + + * Made buttons of some dialogs like other KDE dialogs ones (with + icons and correct alignment). + + * Kig now ask the user when exporting type(s) to an already + existant file. + + * Some i18n fixes + +2004-04-02 Dominique Devriese <devriese@kde.org> + + * Implement the point-on-curve checking. The code should work, + but the objects using it cannot be built yet. + +2004-03-28 Dominique Devriese <devriese@kde.org> + + * Add some non-functional code for point-on-curve checking. + +2004-03-28 Pino Toscano <toscano.pino@tiscali.it> + + * Add point styles + + * Some i18n fixes + + * More work on the DrGeo import filter + +2004-03-27 Dominique Devriese <devriese@kde.org> + + * Apply a patch by Albert Astals Cid <tsdgeos@terra.es> that gives + focus to the text input on first opening the text dialog. + Closes:78409. + + * Fix a translation issue with internal macro's. + + * Make a TextImp transformable by simply transforming its + location, and showing the text label in that location again + (Closes: 78407 ). + +2004-03-26 Pino Toscano <toscano.pino@tiscali.it> + + * Implemented a popup menu submenu for changing the line style of + an object. + + * Various i18n fixes + +2004-03-15 Dominique Devriese <devriese@kde.org> + + * misc/coordinate_system.cpp: add a simple "Invisible" Coordinate + system, showing no axes or grid at all. + +2004-03-10 Dominique Devriese <devriese@kde.org> + + * Don't mess up the order of given objects in a macro + construction. + +2004-03-09 Dominique Devriese <devriese@kde.org> + + * Fix the macro system to reject macro's where not all of the + given objects are used. + + * Fix the macro system to properly check whether the final objects + depend on the given objects, and fix a problem with the wrong + object being selected as the final object in some rare cases. + +2004-03-06 Dominique Devriese <devriese@kde.org> + + * Incorporate a patch by Pino Toscano adding some unfinished work + on a Dr.Geo import filter. It's not finished yet, and also not + visible in the UI yet. + + * Fix the ObjectHierarchy::resultDoesNotDependOnGiven() function + to do something much less stupid than before. It still only + checks if one of the result objects does not depend on the given + objects, it needs to be changed to properly check whether all the + result objects depend on the given objects. + + * Fix the ObjectHierarchy class to generate a correct hierarchy + when a result object depends on another object that does not + depend on the given objects. + +2004-02-24 Dominique Devriese <devriese@kde.org> + + * Make the tests stuff generate a proper text label with + property calcers instead of a normal calcer as rest arguments. + + * Make the configure.in.in stuff properly detect python on RH + -> many thanks to Maurizio for helping me with this. + + * Fix some problems with the useText, that were introduced with + the new tests stuff. + +2004-02-23 Dominique Devriese <devriese@kde.org> + + * Add a ContainsTestType, testing whether a given point is on a + given curve. + +2004-02-17 Dominique Devriese <devriese@kde.org> + + * Add the possibility to give an item a custom color in the + popup dialog. + +2004-02-16 Dominique Devriese <devriese@kde.org> + + * make the usetext in TestConstructMode also appear for all other + arguments than the last one + +2004-02-15 Dominique Devriese <devriese@kde.org> + + * Add code by Maurizio Paolini, and some adaptations of his code + by me for supporting property tests like "are these two lines + parallel ?" + + * update the aboutdata: Maurizio Paolini did not only help with + math intensive code, and Franco Pasquarelli did some very + important work in the locus code. + +2004-02-14 Dominique Devriese <devriese@kde.org> + + * Fix a bug waiting to pop up in construct_mode.cc, where you + select the same object twice. + + * Add documentation about the locus and textlabel design to the + DESIGN document. + +2004-02-10 Dominique Devriese <devriese@kde.org> + + * Fix a bug which caused Kig to crash on moving a text label by + removing a wrong assertion in objects/object_calcer.cc + +2004-02-09 Dominique Devriese <devriese@kde.org> + + * Replace the line-line-intersection algorithm with a much simpler + one by Maurizio Paolini + + * Add three new transformations by Maurizio Paolini + + * Remove people-to-inform-about-kig-releases as no further + separate releases are planned. + +2004-02-08 Dominique Devriese <devriese@kde.org> + + * Fix a bug reported by Maurizio Paolini: don't crash on getting + the arguments for a locus in the wrong order. + +2004-01-21 Dominique Devriese <devriese@kde.org> + + * replace my own autoconf code in configure.in.in for checking for + boost.python and python by some macro's by Ben Burton which + additionally check whether the python and boost.python combination + found is sane. They're also generally cleaner and such. + + * clean up configure.in.bot a bit + +2004-01-20 Dominique Devriese <devriese@kde.org> + + * Add a new internal "Segment Axis" type, implemented as a macro. + +2004-01-18 Dominique Devriese <devriese@kde.org> + + * bump the version number to 0.7.1 + +2003-12-16 Dominique Devriese <devriese@kde.org> + + * Fix the scrolling for horizontal scrolling using the alt button + or a horizontal scroll wheel. + +2003-12-15 Dominique Devriese <devriese@kde.org> + + * Another try at fixing the ArgsParser parsing order. I think + I've tried most problematic cases, and they all seem to work + properly. Let's pray for the best ;) + +2003-12-10 Dominique Devriese <devriese@kde.org> + + * Fix a crash when using a macro having the moving point of a + locus as its argument, reported by Marco Zoso. + +2003-11-14 Dominique Devriese <devriese@kde.org> + + * Fix two crashes in TextLabelRedefineMode. + +2003-11-10 Dominique Devriese <devriese@kde.org> + + * bump the version number to 0.6.1 + + * Fix bugs #67671 and #67694 + +2003-10-22 Dominique Devriese <devriese@kde.org> + + * Improve the errors given by the "New Script Wizard", by making + it get a proper error description from the python interpreter. + + * fix the ArgsParser parsing order, properly this time.. + +2003-10-20 Dominique Devriese <devriese@kde.org> + + * Work on the Cabri import filter, so that it actually becomes + usable for some easier files.. + + * Improve the errors given by the "New Script Wizard", by making + it get a proper error description from the python interpreter. + +2003-10-09 Dominique Devriese <devriese@kde.org> + + * Add a lot of documentation to the new classes. + + * Fix the moving system again, it now only redraws exactly those + objects that need to be redrawn. E.g. when a constrained point + was moved, before it was assumed that all of its parents, and + their children would move, whereas in reality, a constrained point + does not move the curve it is constrained to. This is now taken + into account for. This much optimizes the case where we move the + constrained point in examples/sine-curve.kig. + + * Added a DESIGN document, documenting the Kig object system + design. I think that is rather finished now, and it's probably + about time to try and make some other people than myself get it ;) + + * Remove support for the ancient pre-0.4 file format that we still + supported opening. If you still have old files around using it, + you should convert them to the new format, by opening them with a + Kig version between 0.4 to 0.6, and re-saving them. Maintaining + compatibility with these old files doesn't seem very useful, + because I don't think there are many files in this format + available, and therefore I didn't think it was worth the trouble + of porting the code to the new object system. + + * Introduce a new file format that matches the new object system + better. Files in the old format can still be opened seamlessly. + + * Add undo/redo support for changing visible aspects of an object + ( size, color, shown state ). In the new system, this was as easy + as replacing the ObjectDrawer of an ObjectHolder with another one. + + * Another ( hopefully the last ) major change to the object + system. Decouple the link between how an object is calced and how + it is drawn. We now have a hierarchy structure of ObjectCalcer's + describing various objects and their interrelations. On top of + that, there are the ObjectHolder's, which hold a link to an + ObjectCalcer from the hierarchy, and keep an ObjectDrawer + describing how to draw it. The document only keeps a list of + ObjectHolder's, nothing else.. + + * make the New Script Wizard give an error when the script does + not generate a valid ObjectImp. + + * fix some issues with the escaping of an & in a translatable + string in an xml file + + * fix the generation of the pot translation template file + + * fix a problem with the order of arguments in ScalingOverLineType + causing a test file to not be loaded correctly.. + + * update some test files to the new ( post-0.4 ) kig file format. + + * add ( sometimes placeholder ) icons for the remaining actions + that missed icons ( thanks to Maurizio Paolini ) + +2003-09-08 Dominique Devriese <devriese@kde.org> + + * Fix a bug that prevented Kig from opening its own files, + rejecting them because they were of the 0.6.0 version, which Kig + could not open.. I'm backporting this into Kig 0.6.0 and + informing the packager.. + + * clean up: Objects now store their parents in order, so that no + parsing has to be done in the calc() function.. Also some more + modifications making that function a bit simpler are included. + Specifically, ArgsParser now does the checking of the arguments, + instead of every single calc function doing it itself.. + +2003-09-02 Dominique Devriese <devriese@kde.org> + + * rename ArgparserObjectType to ArgsParserObjectType + + * remove ArgsChecker class, and rename ArgParser to ArgsParser + +2003-09-02 Dominique Devriese <devriese@kde.org> + + * branch off Kig 0.6.0 + +2003-09-01 Dominique Devriese <devriese@kde.org> + + * only move an object if its parents are not yet moving.. This + fixes bug #63250. + + * remove the defective operator| and operator& implementations for + the Objects class + +2003-08-31 Dominique Devriese <devriese@kde.org> + + * make the Kig Python Scripting API docs only available online. + It's too much trouble to generate them during the build process, + and I can't add a hard build-time dependency on doxygen anyway... + + * keep the Kig version number in a central place, so that it can + easily be changed. Use some autoconf magic to fill it in in the + other places.. + +2003-08-25 Dominique Devriese <devriese@kde.org> + + * update the configure.in.* files and remove + README.boost-python1.30-gcc3.2 and boost-python1.30-gcc3.2.patch + because distributing a Boost.Python patch with Kig is really + stupid, and because the Debian packagers have already applied the + patch in their version of Boost.Python, and so should the other + distro's. Seems I need to thank Ben Burton for suggesting to the + Debian packagers to apply the patch. + + * fix a wrong "lib not found error" in configure.in.in by removing + -pedantic from CXXFLAGS while trying to compile. + +2003-08-15 Dominique Devriese <devriese@kde.org> + + * improve the inline documentation in order to improve the doxygen + generated docs for the python scripting API. + +2003-08-03 Dominique Devriese <devriese@kde.org> + + * give Transformation::apply better semantics + +2003-07-27 Dominique Devriese <devriese@kde.org> + + * add a warning to configure.in.bot about how Boost.Python 1.30 + together with GCC 3.2+ is a bad combination, along with a patch. + + * add documentation about attaching text labels and locuses to the + index.docbook file + +2003-07-23 Dominique Devriese <devriese@kde.org> + + * add a nifty python scripting example that shows the graph of a + sine curve, but can in fact be used to show any function's graph + you would come up with. It uses python scripting and the locus + facility in a clever way to do this. In fact, I stole the idea + from something I saw Hilaire Fernandes do with the Dr.Genius guile + scripting on a presentation at FOSDEM. Kig - of course ;) - does + it way cooler.. :) + + * add support for using the python math package in Kig python + scripts, by importing it from PythonScripter's ctor, and by making + Kig load its part library with RTLD_GLOBAL, to eliminate a problem + which caused python to not be able to load its math dll.. + +2003-07-20 Dominique Devriese <devriese@kde.org> + + * add support for attached text labels. + +2003-07-17 Dominique Devriese <devriese@kde.org> + + * fix the "conversion from const char* to char*" problem in + python_scripter.cc. This introduces a small, harmless memory leak + because of how the python libs work.. + +2003-07-16 Dominique Devriese <devriese@kde.org> + + * add documentation about installing the python dev libs to + configure.in.bot + + * adapt some infrastructure regarding text labels to be able to + work with labels that get their location from an invisible Point + object. This will ease the adding of support for attached text + labels.. + +2003-07-12 Dominique Devriese <devriese@kde.org> + + * prevent a crash when kig cannot find its library. It now just + complains and exits properly. + +2003-07-03 Dominique Devriese <devriese@kde.org> + + * add Python scripting support. Rather large addition, involving + a lot of autoconf and automake magic.. + + * make snapToGrid work for PolarCoordinateSystem + + * make shift -> snap to grid work in PointConstructionMode and + normal Construction Mode too.. + + * add the concept of cache objects, which cannot be stored, in + order to support a python compiled script ObjectImp.. + +2003-07-02 Dominique Devriese <devriese@kde.org> + + * sanitize the ObjectImp inherits() system. It now uses static + objects instead of enum values, this also eliminates some ugly + functions in ObjectImp, and allows for more flexible addition of + new ObjectImp types.. + + * fix a memory leak in KigDocument, which did not delete its + KCommandHistory.. + + * fix some use of uninitialised value in dragrectmode, which + caused the dragrect to not work at random times + + * fix the clearing of the selection when the user clicks on an + empty point.. + +2003-06-27 Dominique Devriese <devriese@kde.org> + + * implement helpSlot() in ManageTypesDialog.. + + * add a "Set Coordinate System" menu to the Settings menu.. + + * make shift snap to grid in moving mode, and rework the moving + API to something a bit saner in the process.. + +2003-06-25 Dominique Devriese <devriese@kde.org> + + * add undo support for various view actions like zoom in, zoom + out, recenter screen, select screen rect etc. Check out the + comment in the function KigWidget::slotZoomIn() in + kig/kig_view.cpp for why I implemented this even though it isn't + really "correct". + +2003-06-24 Dominique Devriese <devriese@kde.org> + + * fix a crash bug reported by Pino Toscano, that occurs because + TextLabelRedefineMode was not yet updated to the new + reference-counting Object's stuff.. + +2003-06-21 Dominique Devriese <devriese@kde.org> + + * Implement Select all, Unselect all and Invert selection. + + +2003-06-20 Dominique Devriese <devriese@kde.org> + + * fix this bug:"17) Add the possiblity, by pressing Esc, to stop + the selection, even of the area to be shown." + + * add an icon for Arc's center property, this fixes: "12) Why + don't you use baseCircle.png as icon to show/construct the center + of a circle and (why not?) an arc?" + + * fix: a text label constructed using "add text label" from an + object popup wasn't properly calced after construction.. + + * fix this bug: "2) Add the possibility to set shortcut for all + the actions & objects (For examples: Ctrl+P to construct a point, + Ctrl+R to start a reflection, and so on...).", and add some + default accels too ( "p" for point, "s" for segment etc. ( note : + no control key ). + + * fix this bug: "3) When I select a segment, in his popup there are + two same entry in Construct submenu, called Mid point an + Midpoint. Why?" as reported by Pino Toscano + + +2003-06-11 Dominique Devriese <devriese@kde.org> + + * move transformations to their own menu entry, thanks to Pino + Toscano + + * move angle stuff to their own objects submenu, and toolbar, + thanks to Pino Toscano + + * update the images in the docs, thanks to Pino Toscano + +2003-06-04 Dominique Devriese <devriese@kde.org> + + * show an appropriate error when trying to open an non-existing + file.. + + * clean up the object parent-child relation mechanism. Rather + large code cleanup, that simplifies a lot of code.. Needed a + backwards-compatible file format extension. This commit now also + adds proper treatment of internal objects, because it no longer + relies on the inherently wrong isInternal() hack, but features The + Correct Fix(tm). + +2003-06-03 Dominique Devriese <devriese@kde.org> + + * bugfix: show default icons for actions that don't have any.. ( + fixes bug #59283 ) + + * release Kig 0.5.1 + +2003-05-30 Dominique Devriese <devriese@kde.org> + + * Add an option to select the part of the screen that should be + shown by dragging a rect.. + +2003-05-28 Dominique Devriese <devriese@kde.org> + + * fix warnings when compiling with --disable-debug + +2003-05-26 Dominique Devriese <devriese@kde.org> + + * fix a crash bug for a weird cubic situation + +2003-05-25 Dominique Devriese <devriese@kde.org> + + * implement another of Stephan Binner's suggestions: in the set + coordinate system popup, show a checked mark next to the + current coordinate system.. + + * fix a bug that caused the "circle by center and point" type to + not be visible.. + +2003-05-24 Dominique Devriese <devriese@kde.org> + + * when the user tries to construct a macro that constructs an + object from its children, warn him instead of + crashing... Thanks to Stephan Binner for the bug report + + * improve the export to image dialog, as suggested by Stephan Binner + +2003-05-23 Dominique Devriese <devriese@kde.org> + + * remove some obsolete code and clean some older code up.. + +2003-05-22 Dominique Devriese <devriese@kde.org> + + * add simple printing support using the fantastic KDE-Print lib + +2003-05-21 Dominique Devriese <devriese@kde.org> + + * fix the full screen mode, to use the correct shortcut for + starting and stopping it, and use QWidget::showFullScreen, instead + of creating a full screen pseudo-dialog etc. + + * "branch off" release 0.5, and update the version strings etc. + +2003-05-17 Dominique Devriese <devriese@kde.org> + + * add a toolbar icon ( i.e. GUIAction ) for constructing an angle + bisector.. + +2003-05-15 Maurizio Paolini <paolini@dmf.unicatt.it> + + * take advantage of the new invalid coordinate when creating circles + and arcs through three aligned points + +2003-05-13 Dominique Devriese <devriese@kde.org> + + * fix compilation with --enable-final + +2003-05-12 Dominique Devriese <devriese@kde.org> + + * fix a crash bug for macro's involving PropertyImp + + * update the AboutData: upgrade some people to authors, and add + credit for some more people.. + + * add an angle bisector property + +2003-05-10 Dominique Devriese <devriese@kde.org> + + * fix the transformation types for cases where the object being + transformed is the same as one of the arguments that the + transformation needs.. E.g. right-click on a + point->transform->reflect over a point works properly + point->transform->now.. + +2003-05-09 Maurizio Paolini <paolini@dmf.unicatt.it> + + * the drawLocus is now changed to function as a generic drawCurve. + The changes are very little, and the locusCalcPoint is no + longer necessary. It seems that performance is not affected + significantly. The drawCubic is not used any longer; it is + still there in kigpainter, but can be purged as soon as + no problems arise with the new setup. The generic drawCurve + is used in place of drawCubic. + +2003-05-08 Dominique Devriese <devriese@kde.org> + + * add support for quite some more types to the kseg import filter + +2003-05-08 Maurizio Paolini <paolini@dmf.unicatt.it> + + * fixed drawing problem while building a cubic by 9 points. The + problem was located in calcCubicRoot when the degree is + less than 3 + +2003-05-08 Dominique Devriese <devriese@kde.org> + + * add some properties to the arc object + + * organise the filters directory more sanely + + * fix for deleting: remove deleted objects from their children, so + they don't appear in saved files + +2003-05-07 Dominique Devriese <devriese@kde.org> + + * more undo support: redefining text labels and points is undoable + now.. + + * small undo stuff cleanup + +2003-05-06 Dominique Devriese <devriese@kde.org> + + * fix the change text action for text labels to reuse the label + construction dialog. this makes it support multi-line + labels, and changing the parameters + +2003-05-05 Dominique Devriese <devriese@kde.org> + + * add zoom in/out icons to the document popup menu + +2003-05-03 Dominique Devriese <devriese@kde.org> + + * add support for multiline text labels.. still needs some + further work.. + + * add a set size action to the angle type + + * change the angle size icon.. + +2003-05-03 Maurizio Paolini <paolini@dmf.unicatt.it> + + * add transformation support for arcs.. + +2003-05-02 Dominique Devriese <devriese@kde.org> + + * add undo support for changing the coordinate system + + * generalize the undo support from the moving mode, and add undo + support for many of the object specific actions.. + +2003-05-01 Dominique Devriese <devriese@kde.org> + + * perfect the grid.. + + * fix useless error output on startup + + * add scroll bars to the full screen mode + + * add zoom actions to the document popup + + * add a change text action to text labels + +2003-04-28 Dominique Devriese <devriese@kde.org> + + * add undo support for moving + + * add a full screen mode + +2003-04-27 Dominique Devriese <devriese@kde.org> + + * some PolarCoords improvements + + * show a popup menu when the user clicks on the document, and allow + him to change the coordinate system.. + +2003-04-26 Dominique Devriese <devriese@kde.org> + + * added property icons + +2003-04-22 Dominique Devriese <devriese@kde.org> + + * fix the move dependencies.. + +2003-04-19 Dominique Devriese <devriese@kde.org> + + * Add KSeg file format support + + * Start using the ChangeLog for versions after 0.4.1 ;) + +ma feb 11 00:12:52 CET 2002 - Dominique Devriese <devriese@kde.org> + + * Initial Creation diff --git a/kig/DESIGN b/kig/DESIGN new file mode 100644 index 00000000..fd887779 --- /dev/null +++ b/kig/DESIGN @@ -0,0 +1,275 @@ +EXPLANATION OF THE KIG DESIGN +============================= + +1. Object system +---------------- + +The Kig Object System is a design I'm particularly proud of. It +started out pretty basic, but has undergone some major revisions, that +have proven very succesful. Currently, I have just made one more +major change, and I think this will be the last majore change to it +for quite some time to come. That's also why I'm writing this +explanation for other developers. + + + +1.1 ObjectImp's: Basic objects. + +An ObjectImp represents the current state of an object in Kig. It +keeps information about what type of object it is ( e.g. a line, a +point, a circle etc. ), and its exact data ( e.g. the center and +radius of the circle ). It is *not* in any way aware of how the +object was calculated from its parents (e.g. is this a line that is +constructed as the parallel of another line, or as the line going +through two given points ? ) or how it is drawn on the window ( +e.g. the thickness of the line, its color etc. ). + +There is also the notion of BogusImp's in Kig. These are special +kinds of ObjectImp's that *only* hold data. They do not represent any +real object that can be drawn on a window. Their use is *only* in +holding data for other objects to use. Examples are StringImp, +IntImp, ConicImp etc. + +There are a lot of ObjectImp's in Kig, most of them are in files +called *_imp.h and *_imp.cc or *_imp.cpp in the objects subdirectory. +Examples are PointImp, LineImp, ConicImp, CircleImp, CubicImp, +AngleImp etc. + +There is also the concept of ObjectImpType's. These identify a kind +of ObjectImp. They carry information about the inheritance among the +different ObjectImp types, and some strings identifying them. You can +get hold of the ObjectImpType of a certain ObjectImp by using its +type() method, you can also get hold of them by name using +ObjectImpFactory. + + +1.2 ObjectCalcer's: calculating ObjectImp's from other ObjectImp's + +An ObjectCalcer is an object that represents an algorithm for +calculating an ObjectImp from other ObjectImp's. It is also a node in +the dependency graph of a certain document. E.g. a LineImp can be +calculated from the two PointImp's it has to go through; every time +either of them moves, this calculation is redone. In this case, there +would be an ObjectCalcer that keeps a reference to its two parents ( +the ObjectCalcer's representing the points ), and that will calculate +its ObjectImp value every time it is asked to do so ( i.e. every time +one of its parents moves.. ). + +Because of the complex relations that ObjectCalcer's hold to other +ObjectCalcer's and to other classes, they have been made +reference-counted. This means that they keep a count internally of +how much times a pointer to them is held. If this count reaches 0, +this means that nobody needs them anymore, and they delete themselves. +E.g. an ObjectCalcer always keeps a reference to its parents, to +ensure that those aren't deleted before it is deleted. + +In the inheritance graph of a document, the lowermost objects keep +references to their parents and those keep reference to their parents, +so that all of the top of the graph is kept alive. Of course, someone +needs to keep a reference to the bottommost objects in the graph, +because otherwise, the entire graph would be deleted. As we will see +later, an external class ( ObjectHolder ) keeps a reference to the +ObjectCalcer's that the user is aware of. Thus, the reference +counting system makes sure that all the objects that the user knows +about, and all of their ancestors are kept alive, and the others die. +At the end of the program, this reference is released, and all the +objects are deleted. + +A special case of an ObjectCalcer is the ObjectConstCalcer. This is +an ObjectCalcer that has no parents, and only holds some data. The +data is held as an ObjectImp of some type, and it will remain +constant, and no calculation needs to be done to get it, it is just +returned every time it is needed. + +Other ObjectCalcer's are ObjectPropertyCalcer and ObjectTypeCalcer. +ObjectTypeCalcer is a ObjectCalcer that calculates an object according +to what a ObjectType object specifies. It basically forwards all +calculations to that object ( check below ). An ObjectPropertyCalcer +gets data from a property of a certain object. In fact, ObjectImp's +can specify property's ( e.g. properties of a circle are its radius, +its circumference, its center etc. An angle has its bisector as a +LineImp property ), and they are returned as ObjectImp's of an +appropriate type. The ObjectPropertyCalcer just gets one of the +properties of a certain ObjectImp and stores it. + + +1.3 ObjectType's: a specification of how to calculate an object. + +An ObjectType represents a certain algorithm to calculate an ObjectImp +from other ObjectImp's. Unlike an ObjectCalcer, it does not +participate in the inheritance graph, and there is only one +instantiation of each type of ObjectType. An ObjectTypeCalcer is an +ObjectCalcer that keeps a pointer to a certain ObjectType, and +forwards all requests it gets to its ObjectType. It's very normal +that multiple ObjectTypeCalcer's share the same ObjectType. + +There are very much ObjectType's in Kig, check out all of the files +that end in *_type.* or *_types.* in the objects subdirectory of the +Kig source code. + + +1.4 ObjectHolder's: a link from the document to the hierarchy + +An ObjectHolder represents an object as it is known to the document. +It keeps a pointer to an ObjectCalcer, where it gets its data ( the +ObjectImp that the ObjectCalcer holds ) from. It also holds +information about how to draw this ObjectImp on the window, by keeping +a pointer to an ObjectDrawer ( see below ). In its draw method, it +gets the ObjectImp from the ObjectCalcer, and passes it to the +ObjectDrawer, asking it to draw the ObjectImp on the window. + +The document ( check the KigDocument class ) holds a list of these +ObjectHolder's. This is its only link with the ObjectCalcer +dependency graph. An ObjectHolder keeps a reference to its ObjectCalcer. + + +1.5 ObjectDrawer: An intelligent struct keeping some data about how to + draw an ObjectImp on screen. + +An ObjectDrawer is used by an ObjectHolder to keep information about +how to draw an ObjectImp on the window. It is really nothing more +than a struct with some convenience methods. It does not have any +virtual methods, or have any complex semantics. It keeps information +like the thickness of an object, its color, and whether or not it is +hidden. + + +2. Interesting Issues +--------------------- + +Here, I explain some parts of the design that may at first look +difficult to understand. This part assumes you have read the above. + + +2.1 Text labels + +Text labels in Kig are designed in a pretty flexible +way. I will explain all the classes involved. + +2.1.1 TextImp + +First of all, there is the TextImp class. It is an ObjectImp ( +cf. supra ), and thus represents a piece of text that can be drawn on +the document. It contains a QString ( the text to be shown ), a +coordinate ( the location to draw it ), and a boolean saying whether a +frame should be drawn around it. As with all ObjectImp's, it does not +contain any code for calculating it, or how it behaves on user input. +Most of this is handled by the TextType class. + +2.1.2 TextType + +The TextType class is an implementation of an ObjectType. It contains +code specifying how to calculate a TextImp from its parents, and for +how it behaves on user input. A text object has at least three +parents, and can handle any number of optional arguments. The three +mandatory arguments are an int, which is set to 1 or 0 depending on +whether the label needs a surrounding box, a PointImp, containing the +location of the text label, and a string containing the text of the +label. The text can contain tokens like '%1', '%2' etc. Every +additional argument is used to replace the lowest-numbered of those +tokens, with its string representation. The function +ObjectImp::fillInNextEscape is used for this. + +For example, if a TextType has the following parents: +a IntImp with value 0 +a PointImp with value (0,0) +a String with value "This segment is %1 units long." +a DoubleImp with value 3.9 + +This would result in a string being drawn at the coordinate (0,0), +with no surrounding box, and showing the text "This segment is 3.9 +units long.". + +All this gives labels in Kig a lot of flexibility. + +2.2 Locuses + +Locuses are a mathematical concept that has been modelled in Kig. +Loosely defined, a locus is the mathematical shape defined by the set +of points that a certain point moves through while another point is +moved over its constraints. This can be used to define mathematical +objects like conics, and various other things. It has been modelled +in Kig in the most flexible way I can imagine, and I must say that I'm +proud of this design. + +2.2.1 Constrained points + +In the implementation of this, we use the concept of constrained +points. This is a point that is attached to a certain curve. It is +implemented in Kig by the ConstrainedPointType, which takes a CurveImp +and a DoubleImp as parents and calculates a Point from these by using +the CurveImp::getPoint function. + +2.2.2 The Implementation + +When a Locus is constructed by the user, Kig receives two points, at +least one of which is a Constrained point, and the other one somehow +depends on the first. This is checked before trying to construct a +Locus, and the user is not allowed to try to construct locuses from +other sorts of points. + +Next, Kig takes a look at the ObjectCalcer hierarchy. We look at the +smallest part of the hierarchy that contains all paths from the first +point to the second point. We then determine all objects that are not +*on* one of those paths ( meaning that they are not calculated from +the first point, or another object that is on one of those paths ), +but that are parents of one or more objects that are on those paths. +I call this set of objects the "side of the path" sometimes in the +code. The function that finds them is called sideOfTreePath. + +Next, an ObjectHierarchy object is constructed, which stores the way +to calculate the second point from the first point and the objects +from the previous paragraph. + +An object is then constructed that has as parent the curve parent that +the first point is constrained to, the HierarchyImp containing the +ObjectHierarchy from the previous paragraph, and all the objects from +the "side of the tree". This new object is an ObjectTypeCalcer with +the LocusType as its type. In its calc() function, it calculates a +LocusImp by taking the objecthierarchy and substituting all the +current values of the objects from the "side of the path", resulting +in an ObjectHierarchy that takes one PointImp and calculates another +PointImp from that. The LocusImp then contains the new +ObjectHierarchy and the current value of the curve that the first +point is constrained to. In the drawing function of this LocusImp, +points on the curve are calculated, and then the hierarchy is used to +calculated from those points the location of the second point. A +dynamic feedback algorithm, which has been written with a lot of help +from the mathematician "Franco Pasquarelli" is used to determine which +of the points on the curve should be used. + +2.2.3 The Rationale + +The above explanation may seem very complicated, but I am very much +convinced that this *is* the proper way to handle locuses. I will +here try explain why I think it is superior to the much simpler +implementation that is used by much other programs. + +The basic alternative implementation involves just keeping a pointer +to the first and second point in the locus object, and when the locus +is drawn, the first point is moved over all its possible locations, +the second point is calculated, and a point is drawn at its new +location. + +The reason I think that this is a bad implementation is that it is not +possible to model the real dependency relations properly in this +scheme. For example, the locus object would then be made dependent on +the constrained point. This is wrong because when the constrained +point moves within the limits of the curve constraining it, the locus +does by definition not change. Also, if the constrained point is +redefined so that it is no longer constrained to any curve, this is a +major problem, because it would invalidate the locus. Another point +is that in practice, the locus depends on more objects than its +parents alone. This is not a good thing, because it makes it +impossible to optimise drawing of the objects, using the information +about which objects depend on which others, because this information +is invalid. + +The reason we need to calculate the "side of the path" above is that, +together with the curve that the first point is constrained to, these +are the objects that the locus is really dependent on. + +The current Kig system correctly models all dependency relations to +the extent possible, while keeping a correct implementation. + + diff --git a/kig/FEATURES b/kig/FEATURES new file mode 100644 index 00000000..012f49f1 --- /dev/null +++ b/kig/FEATURES @@ -0,0 +1,44 @@ +FEATURES +here are some nice Kig features i'd like to show off :) +- More than 40 various nice objects: from the easiest ones ( points ), + up to lines, circles, conics, cubics, and more. See the Objects + menu... +- Macros: the power of constructing new objects using the builtin + ones. If you want to try, open macrotest.kig, then click the + button to the left of the fullscreen button, select the three + corners of the triangle, then click the button again, click the + circle, click the button again, type in a name. Then check out the + new type with the other circle types. You can't really see the + difference between built-in types and macro's +- I've given quite some attention to making Kig as intuitive to use as + possible. It has various unique features like the nice RMB menu's, + the intuitive construction mode, etc. +- is a KPart: try opening macrotest.kig from konqueror, if it doesn't + work yet, open your kcontrol, then go to "File Browsing"->"File + Associations", click add, group:"application", name:"x-kig", open + the "embedding" tab page for the new type, click "add", find + "KigPart" (if you installed Kig correctly (did you get the + "--prefix" on your configure right?) , it should be there), and add + it. then try opening macrotest.kig again +- Kig supports various file formats... See the filters directory. + Currently Kig has read-write support for native files ( which is an + imho clean xml format ) and for the older version of the Kig file + format; read support for KGeo, KSeg and, partially, for Dr. Geo + and Cabri files. This is an area where we plan working on... +- Kig can export a document in several formats: the most common are + images, and you can choose among the types supported by Qt and KDE. + Moreover, the other supported formats are: XFig files, and SVG and + LaTeX documents. These are useful since not all other programs + support Kig files yet... ;) +- Kig is very compatible with the program it was intended to replace, + KGeo. It supports all of its Object types and most of its file + format... +- There is a very flexible mechanism for supporting TextLabels with + variable parts.. Check out the text label wizard for more + explanation... +- nice support for Locuses, inspired by KSeg's version of this. Check + the documentation and examples/locustest.kig for more info.. +- flexible transformation facility + +PLANNED FEATURES +For a list of planned features, check out the TODO file. diff --git a/kig/Makefile.am b/kig/Makefile.am new file mode 100644 index 00000000..b2483db0 --- /dev/null +++ b/kig/Makefile.am @@ -0,0 +1,58 @@ +if KIG_COMPILE_PYTHON_SCRIPTING +scriptingdir = scripting +scriptinglib = scripting/libscripting.la +else +scriptingdir = +scriptinglib = +endif + +SUBDIRS = \ + objects \ + misc \ + modes \ + icons \ + filters \ + examples \ + kig \ + mimetypes \ + macros \ + kfile \ + data \ + pykig \ + $(scriptingdir) + +kde_module_LTLIBRARIES = libkigpart.la +libkigpart_la_SOURCES = dummy.cpp +libkigpart_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) +libkigpart_la_LIBADD = $(LIB_KPARTS) \ + misc/libmisc.la objects/libobjects.la filters/libfilters.la \ + modes/libmodes.la kig/libkigparttemp.la $(scriptinglib) + +messages: rc.cpp + rm -f tips.cpp + $(EXTRACTRC) */*.rc >> rc.cpp + $(EXTRACTRC) */*.ui >> rc.cpp + (cd data && $(PREPARETIPS) > ../tips.cpp) + for file in macros/*.kigt; do \ + cat "$$file" | grep '<Name>' | sed -e 's/^ *<Name>\([^<]*\)<\/Name>/i18n( "\1" );/' | sed -e 's/&/\&/g' >> rc.cpp; \ + cat "$$file" | grep '<Description>' | sed -e 's/^ *<Description>\([^<]*\)<\/Description>/i18n( "\1" );/' | sed -e 's/&/\&/g' >> rc.cpp; \ + cat "$$file" | grep '<UseText>' | sed -e 's/^ *<UseText>\([^<]*\)<\/UseText>/i18n( "\1" );/' | sed -e 's/&/\&/g' >> rc.cpp; \ + cat "$$file" | grep '<SelectStatement>' | sed -e 's/^ *<SelectStatement>\([^<]*\)<\/SelectStatement>/i18n( "\1" );/' | sed -e 's/&/\&/g' >> rc.cpp; \ + done + $(XGETTEXT) tips.cpp rc.cpp filters/*.h kig/*.h misc/*.h modes/*.h objects/*.h scripting/*.h */*.cc kig/*.cpp misc/*.cpp modes/*.cpp -o $(podir)/kig.pot + $(XGETTEXT) kfile/kfile_drgeo.cpp kfile/kfile_drgeo.h -o $(podir)/kfile_drgeo.pot + $(XGETTEXT) kfile/kfile_kig.cpp kfile/kfile_kig.h -o $(podir)/kfile_kig.pot + +CLEANFILES = dummy.cpp + +dummy.cpp: + touch dummy.cpp + +TAGS: + find -name '*.cc' -o -name '*.cpp' -o -name '*.h' | etags - + +# Want to do the scripting part separately, but +# include everything else. +DOXYGEN_EXCLUDE = python-scripting-api-dox-mainpage.dox +DOXYGEN_SET_RECURSIVE = YES +include ../admin/Doxyfile.am diff --git a/kig/README.Developers b/kig/README.Developers new file mode 100644 index 00000000..753533e4 --- /dev/null +++ b/kig/README.Developers @@ -0,0 +1,46 @@ +Hi, here are some things that might not be immediately clear : + +- There are different source dirs ( kig, objects, misc, filters and + modes ) ( just in case this wasn't clear.. ) + +- for testing code, i don't really know how you're supposed to do this with + kparts, so i just do a "make install" every time, and then simply run the + program, you prolly want to do "./configure --enable-debug=full + --prefix=/usr/local/kde3" (meanwhile, i found out, the solution is in a + developer faq on developer.kde.org + (http://developer.kde.org/documentation/other/developer-faq.html#q67.1) + +- There is some documentation about the design in the file DESIGN + +- if you want to contribute, your work is more than welcome, no matter where + you want to help: translation, coding, art, just send us a mail at + kde-edu-devel@kde.org (preferably before you start, so you won't be + doing unnecessary work or something like that), if you have cvs access, + you can of course always commit to cvs + +HISTORY + +I started Kig because we were using "Cabri" in school, a proprietary +windows-only app. When I started looking for free alternatives that ran on +linux, i found two: KGeo and KSeg. Both had features and limitations, KSeg +had most features, but was ugly. Furthermore, it was qt-only, which did +allow it to run on windows too, but made it not fit into kde. KGeo was +fully KDE, but didn't have all the features that KSeg had. + +I first wanted to merge both together, but the code was rather incompatible, +so I wanted to simply extend KGeo. Since I was planning to make it a kpart +app, i started with "kapptemplate --kpart-app", and then started coding. I +was first planning to use lots of code from both kgeo and kseg, but i ended +up recoding everything since in many cases it was faster than porting stuff, +and i wanted to change rather much in the code. + +I did get much inspiration from the source of both programs, and i used some +source and many pictures from KGeo, I would therefore want to thank both +developers for their work. + +I also want to thank everyone who contributed something to Kig or to +free software in general. + +Cheers, + +The Kig Developers diff --git a/kig/README.boost-python1.30-gcc3.2 b/kig/README.boost-python1.30-gcc3.2 new file mode 100644 index 00000000..cafda75e --- /dev/null +++ b/kig/README.boost-python1.30-gcc3.2 @@ -0,0 +1,19 @@ +There is a bug in Boost.Python version 1.30 when compiled with GCC +3.2. This causes an error on compiling. It is a known problem, and +due to the stricter typename checking in more recent GCC versions. +The new Boost.Python version will contain a fix for this problem, but +it might take some more months for this version to become available, +and some distributions ( including Debian, not including Red Hat ( at +this time, 22-9-2003, at least ) ). In the mean time, I have included +a patch to the boost.python headers in the Kig distribution that fixes +this problem. It is called boost-python1.30-gcc3.2.patch, and should +be applied in the following way. + +Open a console window, and go to the directory containing the +Boost.Python headers. This will most likely be called something like +"/usr/include/boost/python/". Then execute the following commands ( +before giving these commands, replace "/dir/to/kig/distribution/" by +the directory where you put the downloaded kig distribution ): + +cd object +patch make_ptr_instance.hpp < /dir/to/kig/distribution/boost-python1.30-gcc3.2.patch diff --git a/kig/README.in b/kig/README.in new file mode 100644 index 00000000..692e03c3 --- /dev/null +++ b/kig/README.in @@ -0,0 +1,9 @@ +Kig v@KIGVERSION@ +Kig developers <kde-edu-devel@kde.org> +---------------------------------------------------------------------- +Kig: KDE Interactive Geometry +Kig is a program for use in math classes in high school, to allow +students to interactively explore geometric concepts. For more +information: check out the documentation ( open "help:/kig" in +konqueror...) + diff --git a/kig/TODO b/kig/TODO new file mode 100644 index 00000000..8acd9949 --- /dev/null +++ b/kig/TODO @@ -0,0 +1,157 @@ +* bugs + +- There is a pretty strange bug in kig when you use the accel keys. + Open Kig, type p to start constructing a point. Click somewhere to + construct one. Now click p again, and press escape to cancel the + construction. It will not work, and you will have to construct as + many points as times you pressed escape ( or perhaps the times you + pressed esc square or sth like that ). Anyway, this is due to how + Kig works with some strange Qt event loop stuff to make its modes + work, along with a strange way in kaccel of working. I have a + patch against kdecore/kaccel.cpp that should fix it, but the real + fix is to get rid of the entire event loop stuff and think of a + better way to manage all this. pino: seems pressing the stop button + n times, where n are the times you pressed escape, cancel all the + constructions, so the problem is the behaviour of escape. + +- The polar coordinate system blocks Kig when zooming. Select the + polar coordinate system, use the Select shown area tool ( or your + favorite zooming tool ) and select a specific part of the document. + Smaller parts of the document cause bigger problems. pino: this is + due to the polar grid: try to make zoom if there is no grid. + +* I/O: filters, exporters, ... + +- add other command line options, like:<br /> + -e, --export-to FORMAT file.kig => Kig will export file.kig ( or any + other supported format ) into file.ext ( even more than one file ). + The output format depends on the FORMAT string. domi: this is more + difficult, because the export plugins require extra parameters. + E.g. the ImageExporter needs an image size etc. + +- filters: more input filters; improve the existent ones ( see + filters/*-filter-status.txt ); add the possibility to ignore errors + on loading + +- "export to *": add stuff like java applets, kmplot documents, etc... + +* objects + +- Two new transformations: projection on a line, orthogonally and + according to a given direction. As a mathematician, I'm supposed to + have a grudge against these, as they don't fit the definition for + affine transformations ( the matrix has to be regular ) ;), but I + suppose high-school students may find them useful. However,they + would give rather useless results for e.g. lines ( all curves are + almost always projected on a line, segment or ray that you wouldn't + see because it's equal to the line we project upon ). It would be + useful for points though. + +- make DoubleImp a "visible" object with uses like Dr. Geo numeric + values. + +- Provide some nice stuff for differential geometry: velocity vector + of a curve, curvature vector, osculating parabole. Most of this is + not too difficult to implement, but very cool :) + +- create a formula label to display math formulas in the document, and + allow for importing from/exporting to other formula formats such as + KFormula, OOFormula and MathML. + +- other types of fillable shapes, like arc sector, arc segment... + +- defined integrals, as a particular case of filled shapes. + +- improve *a lot* the transformation support for cubics. + +* GUI + +- make stuff from RMB menu's accessible from other places as well. + +- Add the possibility to select, via new dialog, one or more types of + object. + +- add a magnifying glass/zoom window to magnify "on-the-fly" a part of + the document. + +- improve the KTextEditor interface in the script code wizard. + +- make the dialogs not pop up over the main window. + +- add QTooltip and QWhatsThis to the widgets in the various dialogs + (export dialogs, types dialog, ...) + +* core + +- When selecting an argument of a certain type, maybe we should check + whether the required arguments are really there, before telling the + user to select them. Then we could give an error telling the user + to first construct the other objects. An exception should be made + for points of course. + +- Add the possibility to attach text label also to angles. + +- add the possibility to transform more than one object at one time, + using the popup menu. For example, I select two circles, I choose + Transform->Translate form the RMB menu and then Kig should ask me for + a vector to use to translate all the selected objects. + +- add support for work with other measure units (cm, inches, etc...) + than just pixels... + +- extend ObjectFactory::sensiblePointCalcer to also construct + intersection points of stuff... (pino: done for lines) + +- when moving an object that wants to move its parents, try to check if + it is itself not an indirect child of one of the parents it is + trying to move, and forbid the move in that case, as it will lead + to chaotic behaviour. I am not sure if this is really well + possible, but I have to look at it. + +- add intersection types: arc-arc; arc-conic; arc-cubic; conic-cubic; + cubic-cubic; locus-other object. + +- rework the ObjectConstructor and GUIAction stuff into something more + general, and more clean. See the comment for + ObjectConstructor::constructMode(). + +- figure out a way to allow the user to enable and disable certain + features. E.g. I have been asked to allow the user to limit himself + to compass-ruler constructions.. + +- add another viewmode ( which would be completely orthogonal to the + KigMode concept ), where you can more clearly see the dependencies + in a figure. Something with colours, numberings, and/or a tree-like + text representation of the dependencies.. + +* scripting + +- Python scripting: export cubics for real, improve existing API. + +- make the scripting system work more like the macro system. Make it + a way to define new object types that can be reused more than once, + instead of making it just a way to add *one* object. + +- support for more scripting languages than just Python. + +* other + +- document mathematical i18n strings, so that the translators have a + clue about how to translate them ! + +- write cabri-filter-status.txt. + +- add "Tools": easy tools designed for geometry, like Angle converter and + so on... + +- save a "session", i.e.: record how a document is moved, and save it to + e.g. a flash file or something like that. + +- koffice support ? + +* future ? + +- there should be a way to link a figure to a (html?) file containing + exercises. Teachers would be able to create exercises for Kig. + I'm thinking of doing this with HTML (& KHTML) + scripting ( but + this is _distant_ future.. ) diff --git a/kig/VERSION.in b/kig/VERSION.in new file mode 100644 index 00000000..1a7e468b --- /dev/null +++ b/kig/VERSION.in @@ -0,0 +1 @@ +Kig v@KIGVERSION@ diff --git a/kig/boost-python1.30-gcc3.2.patch b/kig/boost-python1.30-gcc3.2.patch new file mode 100644 index 00000000..5b87a2b3 --- /dev/null +++ b/kig/boost-python1.30-gcc3.2.patch @@ -0,0 +1,13 @@ +Only in python/converter: as_to_python_function.hpp~ +diff -u -r python-old/object/make_ptr_instance.hpp python/object/make_ptr_instance.hpp +--- python-old/object/make_ptr_instance.hpp 2003-04-22 14:35:28.000000000 +0200 ++++ python/object/make_ptr_instance.hpp 2003-06-28 15:09:05.000000000 +0200 +@@ -34,7 +34,7 @@ + template <class U> + static inline PyTypeObject* get_class_object_impl(U const volatile* p) + { +- PyTypeObject* derived = get_derived_class_object(is_polymorphic<U>::type(), p); ++ PyTypeObject* derived = get_derived_class_object(typename is_polymorphic<U>::type(), p); + if (derived) + return derived; + return converter::registered<T>::converters.get_class_object(); diff --git a/kig/configure.in.bot b/kig/configure.in.bot new file mode 100644 index 00000000..a1e0bbde --- /dev/null +++ b/kig/configure.in.bot @@ -0,0 +1,40 @@ +if test x$kig_warn_about_disabling_python = xyes; then + cat << EOF + +Kig Python scripting support has been disabled, because you +are missing the necessary headers and/or libraries. +In order to use Kig Python scripting, you need to have +Python installed, along with its development package ( e.g. +libpython-dev ), and also the Boost.Python library, along with +its development package ( e.g. libboost-python-dev ). + +Kig will continue to function without Python scripting support, +but you will not be able to created Python scripted objects, or +load Kig files that use them. + +Installing the libraries: +The python development libraries themselves are almost certainly +packaged by your favourite distribution. Look for a package name +like libpython-dev, with possibly a version number inserted +somewhere in the name.. For the Boost.Python libs, check this +documentation: "http://boost.org/more/download.html" and +"http://boost.org/more/download.html#Installation" or look for +a package like libboost-python-dev or libboost-dev.. +EOF +fi + +if test "x$kig_enable_python_scripting" = "xyes"; then + # tell users about the Boost.Python 1.30 + GCC 3.2+ problem and patch. + cat << EOF + +Kig WARNING +=========== +There is a problem using unpatched Boost.Python 1.30 in combination +with GCC 3.2 and above. In some distributions, like Debian sarge, +this problem has been fixed, but in other distributions, the problem +may still be there. If you encounter problems while compiling Kig, +then you should try to re-run configure with the option +"--disable-kig-python-scripting". This may fix the compilation, but +you will not be able to use the Kig Python Scripting. +EOF +fi diff --git a/kig/configure.in.in b/kig/configure.in.in new file mode 100644 index 00000000..fa4cd0c7 --- /dev/null +++ b/kig/configure.in.in @@ -0,0 +1,251 @@ +kig_version=0.10.7 + +kde_save_LIBS=$LIBS +LIBS="$LIBS -lm" +KDE_CHECK_FUNC_EXT(trunc) +LIBS="$kde_save_LIBS" + +KDE_LANG_CPLUSPLUS + +# this variable is set to true if we need to warn the user that Python +# scripting support has been disabled due to missing headers or libs +# or whatever.. It's not set if the user explicitly disabled python +# scripting ( "./configure --disable-kig-python-scripting" ). We +# complain about this at the end of the ./configure script. Check out +# configure.in.bot for the code.. +kig_warn_about_disabling_python="no" + +# this var is set to yes if we want to compile python scripting, and to +# no otherwise +kig_enable_python_scripting="yes" + +# this var is set to no if we want to disable support for compressed files +# (for compatibility reasons with kde 3.1) +kig_enable_compressed_files="yes" + +AC_DEFUN([KIG_PYTHON_NOT_FOUND], [ + AC_MSG_WARN( + [[Kig needs the Python and Boost.Python libraries and their headers \ +installed for its Python scripting support. One of both was not \ +found, or the versions were incompatible, and Python scripting will be disabled.]] ); + kig_warn_about_disabling_python="yes" + kig_enable_python_scripting="no" ] ) + +AC_ARG_ENABLE( kig-python-scripting, + [ --disable-kig-python-scripting Disable Kig Python Scripting support], + [ kig_enable_python_scripting=$enableval ], + [ kig_enable_python_scripting=yes] ) + +AC_ARG_ENABLE( kig-compressed-files, + [ --disable-kig-compressed-files Disable Kig Compressed Files support], + [ kig_enable_compressed_files=$enableval ], + [ kig_enable_compressed_files=yes] ) + +dnl domi: we use some macro's by Ben Burton from the Regina program, +dnl to check for the availability of a good python+boost.python +dnl combination. They are included here, the end is marked at the +dnl bottom. I have changed it only by removing some macro's, and by +dnl making the other call KIG_PYTHON_NOT_FOUND instead of +dnl REGINA_DO_NOT_COMPILE, and REGINA_WARN_*. +dnl +dnl Regina - A Normal Surface Theory Calculator +dnl Configure Script Macros +dnl +dnl Copyright (c) 2002-2003, Ben Burton +dnl For further details contact Ben Burton (bab@debian.org). +dnl +dnl This file is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU General Public License as +dnl published by the Free Software Foundation; either version 2 of the +dnl License, or (at your option) any later version. +dnl +dnl This file is distributed in the hope that it will be useful, but +dnl WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public +dnl License along with this program; if not, write to the Free +dnl Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +dnl MA 02110-1301, USA. +dnl +dnl Note that this copyright notice applies only to macros beginning +dnl with REGINA_. Other macros found in acinclude.m4 have been taken +dnl from external sources; these macros are stored in separate files +dnl in the admin/ subdirectory and copyright notices can be found in +dnl these separate files (and in the comments provided with the macros +dnl themselves). +dnl + +dnl ----------------------------------------------------------------- +dnl +dnl Macros written for Regina +dnl +dnl ----------------------------------------------------------------- + +dnl +dnl REGINA_LIB_BOOST_PYTHON(TARGET-LIST, REQUIRED-BY) +dnl +dnl Checks for a usable boost.python installation. +dnl Issues a warning and adds <TARGET-LIST> (which may consist of +dnl several targets) to $DO_NOT_COMPILE if boost.python is missing. +dnl +dnl AC_SUBST()s the following variables: +dnl +dnl BOOST_PYTHON_INCLUDES: The compiler flags required for +dnl building against boost.python, +dnl including flags for building against +dnl python itself. +dnl BOOST_PYTHON_LIBS: The linker flags required for building +dnl against boost.python. +dnl PYTHON_LIBS: The linker flags required for building against +dnl python itself. +dnl +dnl AC_DEFINE()s the following variables: +dnl +dnl HAVE_BOOST_PYTHON: Defined as 1 if we have a usable boost.python +dnl installation, or remains undefined otherwise. +dnl +dnl Example: REGINA_LIB_BOOST_PYTHON(PYTHON, [the Python interface]) +dnl +AC_DEFUN([REGINA_LIB_BOOST_PYTHON], [ + AC_LANG_PUSH(C++) + KDE_CHECK_HEADERS([boost/shared_ptr.hpp], [ + __regina_py_save_cxxflags="$CXXFLAGS" + __regina_py_save_ldflags="$LDFLAGS" + __regina_py_save_libs="$LIBS" + __regina_py_ok=0 + for pyver in python python2.5 python2.4 python2.3 python2.2; do + for incdir in "/usr/include/$pyver" "/usr/local/include/$pyver" \ + "/usr/$pyver/include" "/usr/local/$pyver/include" \ + "$prefix/include/$pyver" "$prefix/$pyver/include"; do + CXXFLAGS="$__regina_py_save_cxxflags -I$incdir" + LDFLAGS="$__regina_py_save_ldflags -shared" + + # Check for python includes. + AC_TRY_COMPILE([ + #include <Python.h> + ], [ + PyObject obj; + ], [ + # Check for compatibility with boost.python. + AC_MSG_CHECKING([for boost.python with $incdir/Python.h]) + AC_TRY_COMPILE([ + #include <boost/python.hpp> + const char* greet() { return "Hello world!"; } + BOOST_PYTHON_MODULE(hello) { boost::python::def("greet", greet); } + ], [ + /* No main body. */ + ], [ + AC_MSG_RESULT([yes]) + + # Check for -lpython. + for pylib in "$pyver" python python2.3 python2.2; do + for pylibdir in "/usr/lib" "/usr/local/lib" "/usr/lib/$pyver/config" \ + "/usr/local/lib/$pyver/config"; do + for extralibs in "" "-lpthread -lm -lutil -ldl"; do + AC_MSG_CHECKING([for $pylibdir/lib$pylib and $extralibs with $incdir/Python.h]) + LDFLAGS="$__regina_py_save_ldflags -L$pylibdir" + LIBS="-l$pylib $extralibs" + AC_TRY_LINK([ + #include <Python.h> + ], [ + Py_Initialize(); Py_Finalize(); + ], [ + AC_MSG_RESULT([yes]) + for bplib in "-lboost_python-mt" "-lboost_python-gcc-mt-1_32" "-lboost_python-gcc-mt-1_31" "-lboost_python-gcc-mt" "-lboost_python"; do + AC_MSG_CHECKING([compilation of a boost.python program with $bplib]) + LDFLAGS="$__regina_py_save_ldflags -L$pylibdir" + LIBS="-l$pylib $bplib $extralibs" + AC_TRY_LINK([ + #include <boost/python.hpp> + const char* greet() { return "Hello world!"; } + BOOST_PYTHON_MODULE(hello) { boost::python::def("greet", greet); } + ], [ + /* No main body. */ + ], [ + AC_MSG_RESULT([yes]) + # And we're done! + BOOST_PYTHON_INCLUDES="-I$incdir" + BOOST_PYTHON_LIBS="$bplib" + PYTHON_LIBS="-l$pylib $extralibs" + PYTHON_LDFLAGS="-L$pylibdir" + __regina_py_ok=1 + ], [ + AC_MSG_RESULT([no]) + ]) + if test "$__regina_py_ok" = "1"; then break; fi + done + ], [ + AC_MSG_RESULT([no]) + ]) + if test "$__regina_py_ok" = "1"; then break; fi + done + if test "$__regina_py_ok" = "1"; then break; fi + done + if test "$__regina_py_ok" = "1"; then break; fi + done + ], [ + AC_MSG_RESULT([no]) + ]) + ]) + if test "$__regina_py_ok" = "1"; then break; fi + done + if test "$__regina_py_ok" = "1"; then break; fi + done + + CXXFLAGS="$__regina_py_save_cxxflags" + LDFLAGS="$__regina_py_save_ldflags" + LIBS="$__regina_py_save_libs" + + if test "$__regina_py_ok" = "1"; then + AC_DEFINE(HAVE_BOOST_PYTHON, 1, + [Define to 1 if you have a usable boost.python installation.]) + else + BOOST_PYTHON_INCLUDES= + BOOST_PYTHON_LIBS= + KIG_PYTHON_NOT_FOUND + fi + ], [ + KIG_PYTHON_NOT_FOUND + ]) + AC_LANG_POP(C++) + AC_SUBST(BOOST_PYTHON_INCLUDES) + AC_SUBST(BOOST_PYTHON_LIBS) + AC_SUBST(PYTHON_LDFLAGS) + AC_SUBST(PYTHON_LIBS) +]) + +dnl This is the end of the macro's copied from Ben Burton's Regina +dnl program. + +if test "x$kig_enable_python_scripting" != xno; then + kig_enable_python_scripting="yes" # either yes or no.. + + kig_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS=`echo $CXXFLAGS | sed -e 's/-fno-exceptions//g'` + REGINA_LIB_BOOST_PYTHON( PYTHON, Kig Python Scripting ) + CXXFLAGS="$kig_save_CXXFLAGS" +fi + +if test "x$kig_enable_python_scripting" != xno; then + AC_DEFINE( KIG_ENABLE_PYTHON_SCRIPTING, 1, [Defined if Kig Python scripting is enabled] ) +fi + +AM_CONDITIONAL(KIG_COMPILE_PYTHON_SCRIPTING, test x$kig_enable_python_scripting != xno) + +if test "x$kig_enable_compressed_files" = xno; then + CXXFLAGS="$CXXFLAGS -DKIG_NO_COMPRESSED_FILES" +fi + +AC_SUBST( KIGVERSION, $kig_version ) +AC_DEFINE_UNQUOTED( KIGVERSION, "$kig_version", [The current Kig version as a string] ) + +KDE_CHECK_HEADERS([ieeefp.h]) + +# apparently the KDE build system wants to see "dnl AC_OUTPUT( ... )", +# not a normal AC_OUTPUT +dnl AC_OUTPUT( kig/kig.lsm ) +dnl AC_OUTPUT( kig/README ) +dnl AC_OUTPUT( kig/package-kig.sh ) +dnl AC_OUTPUT( kig/VERSION ) diff --git a/kig/data/Makefile.am b/kig/data/Makefile.am new file mode 100644 index 00000000..9ba0da28 --- /dev/null +++ b/kig/data/Makefile.am @@ -0,0 +1,3 @@ +appdatadir = $(kde_datadir)/kig + +appdata_DATA = tips diff --git a/kig/data/tips b/kig/data/tips new file mode 100644 index 00000000..e50f386f --- /dev/null +++ b/kig/data/tips @@ -0,0 +1,57 @@ +<tip category="Kig"> +<html> +<p>One of the most powerful tools in Kig are the menus that you can +enter by right-clicking on an object, or on some empty space in the +document. You can use them to give objects names, change their colors +and line styles, and lots of other interesting things.</p> +</html> +</tip> + +<tip category="Kig"> +<html> +<p>You can construct new points without using the menu or the toolbar, simply +clicking somewhere on the Kig document with the <em>middle mouse +button</em>.</p> +</html> +</tip> + +<tip category="Kig"> +<html> +<p>Kig can open several file formats: its files (<code>.kig</code> files), +<em>KGeo</em> files, <em>KSeg</em> files, and, partially, <em>Dr. Geo</em> +and <em>Cabri™</em> files.</p> +</html> +</tip> + +<tip category="Kig"> +<html> +<p>Kig has more than 40 objects and 10 transformations you can construct and use +in your documents: open the <em>Objects</em> menu to see them all.</p> +</html> +</tip> + +<tip category="Kig"> +<html> +<p>You can use the selected objects to start the construction of an object +which requires the selected objects as arguments. For example, if you have two +points selected, you can choose <em>Start->Circle by Three Points</em> from the +popup menu to start constructing a circle by three points.</p> +</html> +</tip> + +<tip category="Kig"> +<html> +<p>Kig can extends its object set using external macros. You can find some +interesting macro on Kig website: +<a href="http://edu.kde.org/kig">http://edu.kde.org/kig</a>.</p> +</html> +</tip> + +<tip category="Kig"> +<html> +<p>If you have more than one object under the mouse, and you want to select any +of them, you can click with the <em>left mouse button</em>, while holding the +<em>Shift</em> key, to get a list of the objects under the mouse cursor which +you can then select from.</p> +</html> +</tip> diff --git a/kig/examples/Makefile.am b/kig/examples/Makefile.am new file mode 100644 index 00000000..095f1a85 --- /dev/null +++ b/kig/examples/Makefile.am @@ -0,0 +1,11 @@ +noinst_DATA= \ + cubic-locus.kig \ + ellipse.kig \ + figure_angle.fgeo \ + figure_manyobjects.fgeo \ + fregier.kigt \ + locustest.kig \ + parabolaBDF.kigt \ + session_alotofthings.fgeo \ + sine-curve.kig \ + sine-curve.png diff --git a/kig/examples/cubic-locus.kig b/kig/examples/cubic-locus.kig new file mode 100644 index 00000000..92d3cd20 --- /dev/null +++ b/kig/examples/cubic-locus.kig @@ -0,0 +1,91 @@ +<!DOCTYPE KigDocument> +<KigDocument Version="0.7.1" > + <CoordinateSystem>Euclidean</CoordinateSystem> + <Hierarchy> + <Data type="double" id="1" >-2.27219</Data> + <Data type="int" id="2" >1</Data> + <Data type="double" id="3" >1.98431</Data> + <Data type="double" id="4" >1.90809</Data> + <Data type="double" id="5" >2.0933</Data> + <Data type="double" id="6" >-2.09014</Data> + <Data type="double" id="7" >-1.85202</Data> + <Data type="double" id="8" >1.53769</Data> + <Data type="hierarchy" id="9" > + <input requirement="point" id="1" /> + <input requirement="point" id="2" /> + <input requirement="point" id="3" /> + <input requirement="int" id="4" /> + <intermediate action="calc" type="CircleBCP" id="5" > + <arg>2</arg> + <arg>1</arg> + </intermediate> + <intermediate action="calc" type="RayAB" id="6" > + <arg>3</arg> + <arg>1</arg> + </intermediate> + <result action="calc" type="ConicLineIntersection" id="7" > + <arg>5</arg> + <arg>6</arg> + <arg>4</arg> + </result> + </Data> + <Data type="double" id="10" >3.70405</Data> + <Data type="double" id="11" >0.738799</Data> + <Object type="FixedPoint" id="12" > + <Parent id="3" /> + <Parent id="5" /> + </Object> + <Object type="FixedPoint" id="13" > + <Parent id="6" /> + <Parent id="1" /> + </Object> + <Object type="FixedPoint" id="14" > + <Parent id="7" /> + <Parent id="8" /> + </Object> + <Object type="FixedPoint" id="15" > + <Parent id="10" /> + <Parent id="4" /> + </Object> + <Object type="LineAB" id="16" > + <Parent id="15" /> + <Parent id="12" /> + </Object> + <Object type="ConstrainedPoint" id="17" > + <Parent id="11" /> + <Parent id="16" /> + </Object> + <Object type="Locus" id="18" > + <Parent id="9" /> + <Parent id="16" /> + <Parent id="14" /> + <Parent id="13" /> + <Parent id="2" /> + </Object> + <Object type="CircleBCP" id="19" > + <Parent id="14" /> + <Parent id="17" /> + </Object> + <Object type="RayAB" id="20" > + <Parent id="13" /> + <Parent id="17" /> + </Object> + <Object type="ConicLineIntersection" id="21" > + <Parent id="19" /> + <Parent id="20" /> + <Parent id="2" /> + </Object> + </Hierarchy> + <View> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="15" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="21" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="12" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="18" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="14" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="16" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="20" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="13" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="19" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="17" /> + </View> +</KigDocument> diff --git a/kig/examples/ellipse.kig b/kig/examples/ellipse.kig new file mode 100644 index 00000000..442ba664 --- /dev/null +++ b/kig/examples/ellipse.kig @@ -0,0 +1,29 @@ +<!DOCTYPE KigDocument> +<KigDocument Version="0.4.0" > + <CoordinateSystem>Euclidean</CoordinateSystem> + <Objects> + <Data type="double" id="1" >-5.76253</Data> + <Data type="double" id="2" >0.831135</Data> + <Data type="double" id="3" >1.77309</Data> + <Data type="double" id="4" >1.20053</Data> + <Data type="double" id="5" >-2.29024</Data> + <Data type="double" id="6" >4.15567</Data> + <Object width="5" shown="true" type="FixedPoint" id="7" color="#0000ff" > + <Parent id="1" /> + <Parent id="2" /> + </Object> + <Object width="5" shown="true" type="FixedPoint" id="8" color="#0000ff" > + <Parent id="3" /> + <Parent id="4" /> + </Object> + <Object width="5" shown="true" type="FixedPoint" id="9" color="#0000ff" > + <Parent id="5" /> + <Parent id="6" /> + </Object> + <Object width="-1" shown="true" type="EllipseBFFP" id="10" color="#0000ff" > + <Parent id="7" /> + <Parent id="8" /> + <Parent id="9" /> + </Object> + </Objects> +</KigDocument> diff --git a/kig/examples/figure_angle.fgeo b/kig/examples/figure_angle.fgeo new file mode 100644 index 00000000..22db1113 --- /dev/null +++ b/kig/examples/figure_angle.fgeo @@ -0,0 +1,30 @@ +<?xml version="1.0"?> +<drgenius> + <drgeo name="Figure 1" scale="30.000000" origin_x="0.000000" origin_y="0.000000" grid="False"> + <point id="827A5C0" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-5.833333</x> + <y>2.950000</y> + </point> + <point id="8248660" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-4.666666</x> + <y>-0.783334</y> + </point> + <point id="822A6B0" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>2.333334</x> + <y>-0.216667</y> + </point> + <line id="822BC80" type="2pts" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""> + <parent ref="827A5C0"/> + <parent ref="8248660"/> + </line> + <line id="8249A48" type="2pts" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""> + <parent ref="822A6B0"/> + <parent ref="8248660"/> + </line> + <angle id="82B86D8" type="3pts" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""> + <parent ref="822A6B0"/> + <parent ref="8248660"/> + <parent ref="827A5C0"/> + </angle> + </drgeo> +</drgenius> diff --git a/kig/examples/figure_manyobjects.fgeo b/kig/examples/figure_manyobjects.fgeo new file mode 100644 index 00000000..c03634e3 --- /dev/null +++ b/kig/examples/figure_manyobjects.fgeo @@ -0,0 +1,72 @@ +<?xml version="1.0"?> +<drgenius> + <drgeo name="Figure 1" scale="30.000000" origin_x="0.000000" origin_y="0.000000" grid="False"> + <point id="82257B8" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-6.700000</x> + <y>2.250000</y> + </point> + <point id="8250458" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>0.266667</x> + <y>2.083333</y> + </point> + <point id="8250D30" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-0.800000</x> + <y>-2.516667</y> + </point> + <point id="8255158" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-2.766667</x> + <y>1.883333</y> + </point> + <point id="8226648" type="Middle_2pts" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <parent ref="82257B8"/> + <parent ref="8255158"/> + </point> + <halfLine id="8225B10" type="2pts" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""> + <parent ref="8250D30"/> + <parent ref="8250458"/> + </halfLine> + <point id="82043A0" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-2.833333</x> + <y>-0.983334</y> + </point> + <point id="8115668" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>1.500000</x> + <y>1.883333</y> + </point> + <point id="82AED28" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-0.566667</x> + <y>3.983333</y> + </point> + <segment id="82B0888" type="2pts" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""> + <parent ref="82257B8"/> + <parent ref="82AED28"/> + </segment> + <line id="82B1B80" type="2pts" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""> + <parent ref="82043A0"/> + <parent ref="8115668"/> + </line> + <point id="82B26F0" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-5.533333</x> + <y>-0.383333</y> + </point> + <vector id="82A3948" type="2pts" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""> + <Ox>-2.800000</Ox> + <Oy>1.883333</Oy> + <parent ref="8255158"/> + <parent ref="82B26F0"/> + </vector> + <point id="82B4358" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>3.166667</x> + <y>-0.683333</y> + </point> + <circle id="82B5918" type="2pts" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""> + <parent ref="82B4358"/> + <parent ref="8115668"/> + </circle> + <arcCircle id="82B5D30" type="3pts" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""> + <parent ref="82257B8"/> + <parent ref="82B26F0"/> + <parent ref="8250D30"/> + </arcCircle> + </drgeo> +</drgenius> diff --git a/kig/examples/fregier.kigt b/kig/examples/fregier.kigt new file mode 100644 index 00000000..7f986fec --- /dev/null +++ b/kig/examples/fregier.kigt @@ -0,0 +1,61 @@ +<!DOCTYPE KigMacroFile> +<KigMacroFile Number="1" Version="0.4.0" > + <Macro> + <Name>fregier</Name> + <Description></Description> + <Construction> + <input requirement="conic" id="1" /> + <input requirement="point" id="2" /> + <intermediate action="calc" type="ConicPolarLine" id="3" > + <arg>1</arg> + <arg>2</arg> + </intermediate> + <intermediate action="calc" type="LinePerpend" id="4" > + <arg>3</arg> + <arg>2</arg> + </intermediate> + <intermediate action="push" type="int" id="5" >1</intermediate> + <intermediate action="calc" type="ConicLineIntersection" id="6" > + <arg>1</arg> + <arg>4</arg> + <arg>5</arg> + </intermediate> + <intermediate action="calc" type="MidPoint" id="7" > + <arg>2</arg> + <arg>6</arg> + </intermediate> + <intermediate action="calc" type="LinePerpend" id="8" > + <arg>4</arg> + <arg>7</arg> + </intermediate> + <intermediate action="push" type="int" id="9" >-1</intermediate> + <intermediate action="calc" type="ConicLineIntersection" id="10" > + <arg>1</arg> + <arg>8</arg> + <arg>9</arg> + </intermediate> + <intermediate action="calc" type="RayAB" id="11" > + <arg>2</arg> + <arg>10</arg> + </intermediate> + <intermediate action="calc" type="LinePerpend" id="12" > + <arg>11</arg> + <arg>2</arg> + </intermediate> + <intermediate action="push" type="int" id="13" >1</intermediate> + <intermediate action="calc" type="ConicLineIntersection" id="14" > + <arg>1</arg> + <arg>12</arg> + <arg>13</arg> + </intermediate> + <intermediate action="calc" type="SegmentAB" id="15" > + <arg>10</arg> + <arg>14</arg> + </intermediate> + <result action="calc" type="LineLineIntersection" id="16" > + <arg>15</arg> + <arg>4</arg> + </result> + </Construction> + </Macro> +</KigMacroFile> diff --git a/kig/examples/locustest.kig b/kig/examples/locustest.kig new file mode 100644 index 00000000..42f1ccc0 --- /dev/null +++ b/kig/examples/locustest.kig @@ -0,0 +1,116 @@ +<!DOCTYPE KigDocument> +<KigDocument Version="0.4.0" > + <CoordinateSystem>Euclidean</CoordinateSystem> + <Objects> + <Data type="double" id="1" >9.99611</Data> + <Data type="double" id="2" >9.67971</Data> + <Data type="double" id="3" >9.18808</Data> + <Data type="double" id="4" >-2.03061</Data> + <Data type="double" id="5" >-1.55367</Data> + <Data type="double" id="6" >13.0171</Data> + <Data type="double" id="7" >-9.63092</Data> + <Data type="double" id="8" >9.86598</Data> + <Data type="double" id="9" >-10.4258</Data> + <Data type="double" id="10" >-10.3351</Data> + <Data type="double" id="11" >0.725237</Data> + <Data type="hierarchy" id="12" > + <input requirement="point" id="1" /> + <input requirement="point" id="2" /> + <input requirement="line" id="3" /> + <input requirement="point" id="4" /> + <input requirement="point" id="5" /> + <intermediate action="calc" type="LineAB" id="6" > + <arg>1</arg> + <arg>2</arg> + </intermediate> + <intermediate action="calc" type="LineLineIntersection" id="7" > + <arg>6</arg> + <arg>3</arg> + </intermediate> + <intermediate action="calc" type="LineAB" id="8" > + <arg>7</arg> + <arg>4</arg> + </intermediate> + <intermediate action="calc" type="LineAB" id="9" > + <arg>1</arg> + <arg>5</arg> + </intermediate> + <result action="calc" type="LineLineIntersection" id="10" > + <arg>8</arg> + <arg>9</arg> + </result> + </Data> + <Object width="5" shown="true" type="FixedPoint" id="13" color="#0000ff" > + <Parent id="1" /> + <Parent id="2" /> + </Object> + <Object width="5" shown="true" type="FixedPoint" id="14" color="#0000ff" > + <Parent id="3" /> + <Parent id="4" /> + </Object> + <Object width="5" shown="true" type="FixedPoint" id="15" color="#0000ff" > + <Parent id="5" /> + <Parent id="6" /> + </Object> + <Object width="5" shown="true" type="FixedPoint" id="16" color="#0000ff" > + <Parent id="7" /> + <Parent id="8" /> + </Object> + <Object width="5" shown="true" type="FixedPoint" id="17" color="#0000ff" > + <Parent id="9" /> + <Parent id="10" /> + </Object> + <Object width="-1" shown="true" type="LineAB" id="18" color="#0000ff" > + <Parent id="15" /> + <Parent id="14" /> + </Object> + <Object width="-1" shown="true" type="LineAB" id="19" color="#0000ff" > + <Parent id="16" /> + <Parent id="13" /> + </Object> + <Object width="-1" shown="true" type="LineAB" id="20" color="#0000ff" > + <Parent id="16" /> + <Parent id="17" /> + </Object> + <Object width="-1" shown="true" type="LineAB" id="21" color="#0000ff" > + <Parent id="15" /> + <Parent id="17" /> + </Object> + <Object width="-1" shown="true" type="LineLineIntersection" id="22" color="#0000ff" > + <Parent id="19" /> + <Parent id="18" /> + </Object> + <Object width="5" shown="true" type="ConstrainedPoint" id="23" color="#0000ff" > + <Parent id="21" /> + <Parent id="11" /> + </Object> + <Object width="-1" shown="true" type="Locus" id="24" color="#0000ff" > + <Parent id="21" /> + <Parent id="12" /> + <Parent id="22" /> + <Parent id="20" /> + <Parent id="14" /> + <Parent id="13" /> + </Object> + <Object width="-1" shown="true" type="LineAB" id="25" color="#0000ff" > + <Parent id="23" /> + <Parent id="13" /> + </Object> + <Object width="-1" shown="true" type="LineAB" id="26" color="#0000ff" > + <Parent id="23" /> + <Parent id="22" /> + </Object> + <Object width="-1" shown="true" type="LineLineIntersection" id="27" color="#0000ff" > + <Parent id="26" /> + <Parent id="20" /> + </Object> + <Object width="-1" shown="true" type="LineAB" id="28" color="#0000ff" > + <Parent id="27" /> + <Parent id="14" /> + </Object> + <Object width="-1" shown="true" type="LineLineIntersection" id="29" color="#0000ff" > + <Parent id="28" /> + <Parent id="25" /> + </Object> + </Objects> +</KigDocument> diff --git a/kig/examples/parabolaBDF.kigt b/kig/examples/parabolaBDF.kigt new file mode 100644 index 00000000..ea9253ab --- /dev/null +++ b/kig/examples/parabolaBDF.kigt @@ -0,0 +1,28 @@ +<!DOCTYPE KigMacroFile> +<KigMacroFile Number="1" Version="0.4.0" > + <Macro> + <Name>parabolaBDF</Name> + <Description></Description> + <Construction> + <input requirement="line" id="1" /> + <input requirement="point" id="2" /> + <intermediate action="calc" type="LinePerpend" id="3" > + <arg>1</arg> + <arg>2</arg> + </intermediate> + <intermediate action="calc" type="LineLineIntersection" id="4" > + <arg>3</arg> + <arg>1</arg> + </intermediate> + <intermediate action="calc" type="MidPoint" id="5" > + <arg>2</arg> + <arg>4</arg> + </intermediate> + <result action="calc" type="ConicBDFP" id="6" > + <arg>1</arg> + <arg>2</arg> + <arg>5</arg> + </result> + </Construction> + </Macro> +</KigMacroFile> diff --git a/kig/examples/session_alotofthings.fgeo b/kig/examples/session_alotofthings.fgeo new file mode 100644 index 00000000..967dada9 --- /dev/null +++ b/kig/examples/session_alotofthings.fgeo @@ -0,0 +1,162 @@ +<?xml version="1.0"?> +<drgenius> + <drgeo name="Figure 1" scale="30.000000" origin_x="0.000000" origin_y="0.000000" grid="False"> + <point id="82076B0" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>0.766667</x> + <y>2.450000</y> + </point> + <point id="8281F08" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-3.300000</x> + <y>2.650000</y> + </point> + <point id="82535F0" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-7.900000</x> + <y>-2.583333</y> + </point> + <point id="822F448" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-7.800000</x> + <y>0.116667</y> + </point> + <point id="8208228" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-4.533333</x> + <y>1.216667</y> + </point> + <point id="822FB88" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-8.566667</x> + <y>3.116667</y> + </point> + <point id="82B08A0" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-7.066667</x> + <y>1.783334</y> + </point> + <line id="82C3C40" type="2pts" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""> + <parent ref="8281F08"/> + <parent ref="82076B0"/> + </line> + <arcCircle id="82C2F40" type="3pts" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""> + <parent ref="8208228"/> + <parent ref="822F448"/> + <parent ref="82535F0"/> + </arcCircle> + <halfLine id="82A86B8" type="2pts" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""> + <parent ref="822FB88"/> + <parent ref="82B08A0"/> + </halfLine> + <point id="82C8738" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-1.066667</x> + <y>-0.416667</y> + </point> + <point id="82C4DC8" type="Intersection" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name="" extra="0"> + <parent ref="82A86B8"/> + <parent ref="82C2F40"/> + </point> + <point id="82C5230" type="Intersection" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name="" extra="1"> + <parent ref="82A86B8"/> + <parent ref="82C2F40"/> + </point> + <line id="82BF640" type="perpendicular" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""> + <parent ref="82C8738"/> + <parent ref="82C3C40"/> + </line> + <point id="83030E0" type="Reflexion" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <parent ref="82C4DC8"/> + <parent ref="82BF640"/> + </point> + </drgeo> + <text name="Testo 1">Figure 1 contains: +- some points; +- an arc; +- a line; +- a perpendicular; +- a ray; +- an intersection point; +- a reflection point; + +Figure 2 contains: +- some points; +- 2 lines; +- a midpoint; +- a parallel; +- 2 intersection points; + +Figure 3 contains: +- some points; +- a circle; +- a vector; +- a moved circle; +</text> + <drgeo name="Figure 2" scale="30.000000" origin_x="0.000000" origin_y="0.000000" grid="False"> + <point id="831A340" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-5.200000</x> + <y>0.216667</y> + </point> + <point id="82D1958" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>1.200000</x> + <y>3.116667</y> + </point> + <point id="82EB588" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-7.666667</x> + <y>1.650000</y> + </point> + <line id="8337158" type="2pts" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""> + <parent ref="82EB588"/> + <parent ref="831A340"/> + </line> + <point id="8339778" type="Middle_2pts" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <parent ref="831A340"/> + <parent ref="82D1958"/> + </point> + <line id="8322C38" type="parallel" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""> + <parent ref="8339778"/> + <parent ref="8337158"/> + </line> + <point id="8322E28" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-6.200000</x> + <y>-1.483334</y> + </point> + <line id="831E530" type="2pts" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""> + <parent ref="8322E28"/> + <parent ref="82D1958"/> + </line> + <point id="8356028" type="Intersection" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name="" extra="0"> + <parent ref="831E530"/> + <parent ref="8337158"/> + </point> + <point id="8320CC8" type="Intersection" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name="" extra="0"> + <parent ref="8322C38"/> + <parent ref="831E530"/> + </point> + </drgeo> + <drgeo name="Figure 3" scale="30.000000" origin_x="0.000000" origin_y="0.000000" grid="False"> + <point id="837C5E0" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-6.433334</x> + <y>2.050000</y> + </point> + <point id="83380E8" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-1.400000</x> + <y>2.583333</y> + </point> + <point id="83565C8" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-1.300000</x> + <y>-0.350000</y> + </point> + <point id="82FBCC8" type="Free" color="Red" thickness="Dashed" style="Round" filled="False" masked="False" name=""> + <x>-3.433333</x> + <y>-0.883333</y> + </point> + <vector id="8108B10" type="2pts" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""> + <Ox>-6.433334</Ox> + <Oy>2.050000</Oy> + <parent ref="837C5E0"/> + <parent ref="83380E8"/> + </vector> + <circle id="83A2B10" type="2pts" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""> + <parent ref="82FBCC8"/> + <parent ref="83565C8"/> + </circle> + <circle id="83D6820" type="Translation" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""> + <parent ref="83A2B10"/> + <parent ref="8108B10"/> + </circle> + </drgeo> +</drgenius> diff --git a/kig/examples/sine-curve.kig b/kig/examples/sine-curve.kig new file mode 100644 index 00000000..e20f24d4 --- /dev/null +++ b/kig/examples/sine-curve.kig @@ -0,0 +1,55 @@ +<!DOCTYPE KigDocument> +<KigDocument Version="0.5.1" > + <CoordinateSystem>Euclidean</CoordinateSystem> + <Objects> + <Data internal="true" type="double" id="1" >-3.50779</Data> + <Data internal="true" type="double" id="2" >3.80326</Data> + <Data internal="true" type="double" id="3" >5.49045</Data> + <Data internal="true" type="double" id="4" >-0.492505</Data> + <Data internal="true" type="double" id="5" >0.65293</Data> + <Data internal="true" type="hierarchy" id="6" > + <input requirement="any" id="1" /> + <input requirement="string" id="2" /> + <intermediate action="calc" type="PythonCompileType" id="3" > + <arg>2</arg> + </intermediate> + <result action="calc" type="PythonExecuteType" id="4" > + <arg>3</arg> + <arg>1</arg> + </result> + </Data> + <Data internal="true" type="string" id="7" >def calc( arg1 ): + c = arg1.coordinate(); + y = sin( c.x ); + return Point( Coordinate( c.x, y ) ) +</Data> + <Object width="-1" internal="false" shown="true" type="FixedPoint" id="8" color="#0000ff" > + <Parent id="1" /> + <Parent id="2" /> + </Object> + <Object width="-1" internal="false" shown="true" type="FixedPoint" id="9" color="#0000ff" > + <Parent id="3" /> + <Parent id="4" /> + </Object> + <Object width="-1" internal="true" shown="true" type="PythonCompileType" id="10" color="#0000ff" > + <Parent id="7" /> + </Object> + <Object width="-1" internal="false" shown="true" type="LineAB" id="11" color="#0000ff" > + <Parent id="8" /> + <Parent id="9" /> + </Object> + <Object width="-1" internal="false" shown="true" type="ConstrainedPoint" id="12" color="#0000ff" > + <Parent id="11" /> + <Parent id="5" /> + </Object> + <Object width="-1" internal="false" shown="true" type="Locus" id="13" color="#0000ff" > + <Parent id="11" /> + <Parent id="6" /> + <Parent id="7" /> + </Object> + <Object width="-1" internal="false" shown="true" type="PythonExecuteType" id="14" color="#0000ff" > + <Parent id="10" /> + <Parent id="12" /> + </Object> + </Objects> +</KigDocument> diff --git a/kig/examples/sine-curve.png b/kig/examples/sine-curve.png Binary files differnew file mode 100644 index 00000000..dce01c74 --- /dev/null +++ b/kig/examples/sine-curve.png diff --git a/kig/examples/trifolium-of-delongchamps.kig b/kig/examples/trifolium-of-delongchamps.kig new file mode 100644 index 00000000..8b4f1fa3 --- /dev/null +++ b/kig/examples/trifolium-of-delongchamps.kig @@ -0,0 +1,163 @@ +<!DOCTYPE KigDocument> +<KigDocument axes="1" grid="1" CompatibilityVersion="0.7.0" Version="0.9.1" > + <CoordinateSystem>Euclidean</CoordinateSystem> + <Hierarchy> + <Data type="double" id="1" >0</Data> + <Data type="int" id="2" >0</Data> + <Data type="double" id="3" >0</Data> + <Data type="double" id="4" >4</Data> + <Data type="double" id="5" >0</Data> + <Data type="double" id="6" >0</Data> + <Data type="string" id="7" >%1</Data> + <Data type="string" id="8" >O</Data> + <Data type="string" id="9" >%1</Data> + <Data type="double" id="10" >0</Data> + <Data type="int" id="11" >0</Data> + <Data type="double" id="12" >-0.740892</Data> + <Data type="hierarchy" id="13" > + <input requirement="point" id="1" /> + <input requirement="point" id="2" /> + <input requirement="any" id="3" /> + <input requirement="any" id="4" /> + <input requirement="point" id="5" /> + <input requirement="line" id="6" /> + <intermediate action="calc" type="LineAB" id="7" > + <arg>2</arg> + <arg>1</arg> + </intermediate> + <intermediate action="calc" type="LinePerpend" id="8" > + <arg>7</arg> + <arg>5</arg> + </intermediate> + <intermediate action="calc" type="LineLineIntersection" id="9" > + <arg>8</arg> + <arg>7</arg> + </intermediate> + <intermediate action="calc" type="LineReflection" id="10" > + <arg>9</arg> + <arg>6</arg> + </intermediate> + <intermediate action="calc" type="LinePerpend" id="11" > + <arg>7</arg> + <arg>10</arg> + </intermediate> + <result action="calc" type="LineLineIntersection" id="12" > + <arg>11</arg> + <arg>7</arg> + </result> + </Data> + <Data type="double" id="14" >1.53353</Data> + <Data type="double" id="15" >0.0197571</Data> + <Data type="string" id="16" >Trifolium of de Longchamps</Data> + <Data type="double" id="17" >0.427352</Data> + <Data type="int" id="18" >0</Data> + <Data type="double" id="19" >0</Data> + <Data type="string" id="20" >M</Data> + <Data type="point" id="21" > + <x>-2.36097</x> + <y>2.74624</y> + </Data> + <Object type="FixedPoint" id="22" > + <Parent id="4" /> + <Parent id="1" /> + </Object> + <Object type="FixedPoint" id="23" > + <Parent id="6" /> + <Parent id="5" /> + </Object> + <Object type="FixedPoint" id="24" > + <Parent id="12" /> + <Parent id="14" /> + </Object> + <Object type="Label" id="25" > + <Parent id="11" /> + <Parent id="21" /> + <Parent id="16" /> + </Object> + <Object type="RelativePoint" id="26" > + <Parent id="15" /> + <Parent id="3" /> + <Parent id="22" /> + </Object> + <Object type="LineAB" id="27" > + <Parent id="23" /> + <Parent id="22" /> + </Object> + <Object type="RelativePoint" id="28" > + <Parent id="10" /> + <Parent id="19" /> + <Parent id="23" /> + </Object> + <Object type="CircleBCP" id="29" > + <Parent id="23" /> + <Parent id="24" /> + </Object> + <Object type="Label" id="30" > + <Parent id="18" /> + <Parent id="26" /> + <Parent id="7" /> + <Parent id="20" /> + </Object> + <Object type="Label" id="31" > + <Parent id="2" /> + <Parent id="28" /> + <Parent id="9" /> + <Parent id="8" /> + </Object> + <Object type="ConstrainedPoint" id="32" > + <Parent id="17" /> + <Parent id="29" /> + </Object> + <Object type="Locus" id="33" > + <Parent id="13" /> + <Parent id="29" /> + <Parent id="23" /> + <Parent id="6" /> + <Parent id="5" /> + <Parent id="22" /> + <Parent id="27" /> + </Object> + <Object type="LineAB" id="34" > + <Parent id="23" /> + <Parent id="32" /> + </Object> + <Object type="LinePerpend" id="35" > + <Parent id="34" /> + <Parent id="22" /> + </Object> + <Object type="LineLineIntersection" id="36" > + <Parent id="35" /> + <Parent id="34" /> + </Object> + <Object type="LineReflection" id="37" > + <Parent id="36" /> + <Parent id="27" /> + </Object> + <Object type="LinePerpend" id="38" > + <Parent id="34" /> + <Parent id="37" /> + </Object> + <Object type="LineLineIntersection" id="39" > + <Parent id="38" /> + <Parent id="34" /> + </Object> + </Hierarchy> + <View> + <Draw width="-1" point-style="Round" namecalcer="none" style="SolidLine" shown="true" color="#0000ff" object="30" /> + <Draw width="-1" point-style="Round" namecalcer="none" style="SolidLine" shown="true" color="#0000ff" object="33" /> + <Draw width="-1" point-style="Round" namecalcer="none" style="SolidLine" shown="true" color="#0000ff" object="31" /> + <Draw width="-1" point-style="Round" namecalcer="none" style="SolidLine" shown="false" color="#0000ff" object="29" /> + <Draw width="-1" point-style="Round" namecalcer="none" style="SolidLine" shown="false" color="#0000ff" object="24" /> + <Draw width="-1" point-style="Round" namecalcer="8" style="SolidLine" shown="true" color="#0000ff" object="23" /> + <Draw width="-1" point-style="Round" namecalcer="none" style="SolidLine" shown="false" color="#0000ff" object="34" /> + <Draw width="-1" point-style="Round" namecalcer="none" style="SolidLine" shown="true" color="#0000ff" object="39" /> + <Draw width="-1" point-style="Round" namecalcer="20" style="SolidLine" shown="true" color="#0000ff" object="22" /> + <Draw width="-1" point-style="Round" namecalcer="none" style="SolidLine" shown="true" color="#0000ff" object="27" /> + <Draw width="-1" point-style="Round" namecalcer="none" style="SolidLine" shown="false" color="#0000ff" object="38" /> + <Draw width="-1" point-style="Round" namecalcer="none" style="SolidLine" shown="false" color="#0000ff" object="35" /> + <Draw width="-1" point-style="Round" namecalcer="none" style="SolidLine" shown="false" color="#0000ff" object="37" /> + <Draw width="-1" point-style="Round" namecalcer="none" style="SolidLine" shown="true" color="#0000ff" object="32" /> + <Draw width="-1" point-style="Round" namecalcer="none" style="SolidLine" shown="false" color="#0000ff" object="36" /> + <Draw width="-1" point-style="Round" namecalcer="none" style="SolidLine" shown="true" color="#0000ff" object="25" /> + </View> +</KigDocument> diff --git a/kig/filters/Makefile.am b/kig/filters/Makefile.am new file mode 100644 index 00000000..bb827b5a --- /dev/null +++ b/kig/filters/Makefile.am @@ -0,0 +1,38 @@ +INCLUDES=$(all_includes) + +METASOURCES = AUTO + +noinst_LTLIBRARIES=libfilters.la +noinst_HEADERS= \ + cabri-filter.h \ + exporter.h \ + filter.h \ + filters-common.h \ + imageexporteroptions.h \ + kgeo-filter.h \ + kgeo-resource.h \ + kseg-defs.h \ + kseg-filter.h \ + latexexporter.h \ + native-filter.h \ + svgexporter.h \ + drgeo-filter.h \ + drgeo-filter-chooser.h +libfilters_la_SOURCES= \ + cabri-filter.cc \ + exporter.cc \ + filter.cc \ + filters-common.cc \ + imageexporteroptions.cc \ + imageexporteroptionsbase.ui \ + kgeo-filter.cc \ + kseg-filter.cc \ + latexexporter.cc \ + latexexporteroptions.ui \ + native-filter.cc \ + svgexporter.cc \ + svgexporteroptions.ui \ + drgeo-filter.cc \ + drgeo-filter-chooser.cc \ + drgeo-filter-chooserbase.ui + diff --git a/kig/filters/cabri-filter.cc b/kig/filters/cabri-filter.cc new file mode 100644 index 00000000..6d19027b --- /dev/null +++ b/kig/filters/cabri-filter.cc @@ -0,0 +1,577 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#include "cabri-filter.h" + +#include "../kig/kig_document.h" +#include "../kig/kig_part.h" +#include "../misc/coordinate.h" +#include "../objects/arc_type.h" +#include "../objects/bogus_imp.h" +#include "../objects/circle_type.h" +#include "../objects/conic_types.h" +#include "../objects/curve_imp.h" +#include "../objects/line_imp.h" +#include "../objects/line_type.h" +#include "../objects/object_calcer.h" +#include "../objects/object_drawer.h" +#include "../objects/object_factory.h" +#include "../objects/object_holder.h" +#include "../objects/other_imp.h" +#include "../objects/other_type.h" +#include "../objects/point_type.h" +#include "../objects/polygon_type.h" +#include "../objects/transform_types.h" +#include "../objects/vector_type.h" + +#include <qcolor.h> +#include <qfile.h> +#include <qregexp.h> + +#include <kdebug.h> +#include <klocale.h> + +/** + * a line: + * "Nr": Type, Unknown, "CN:"NumberOfParents, "VN:"Unknown + * Color, FillType, Thickness, "DS":Dots, "GT":SpecialAppearance, Visible, Fixed + * ["Const": Parents] ["Val": Constants] + * + * Nr: Simple sequential numbering of the objects in a file. + * Type: seen so far: Pt, Axes, Line, Cir + * NumberOfParents: The number of parents that will be specified in + * Parents + * Color: + * R -> red + * O -> purple + * Y -> yellow + * P -> dark purple + * V -> dark blue + * Bl -> blue + * lBl -> bright blue + * G -> bright green + * dG -> dark green + * Br -> brown + * dBr -> beige + * lGr -> light grey + * Gr -> grey + * dGr -> dark grey + * B -> black + * FillType: + * W -> not filled ( white ? ) + * all values of the Color item are valid here too.. + * Thickness: + * t -> thin + * tT -> medium + * T -> Thick + * Dots: + * a specification for how a line should be drawn ( with many + * dots, with short lines, it's a pretty generic format: the first + * number signifies the number of sequential dots to draw first, and + * the next is the sum of this first number with the number of + * spaces to leave before starting a new segment. + * SpecialAppearance: + * a number indicating some way of specially drawing an object. This + * can be modified using the "Modify Appearance" button in + * Cabri. For example, for a segment, the number indicates the + * amount of ticks to put on the segment, to indicate + * correspondances between segments.. + * Visible: + * V means visible, I means invisible + * Fixed: + * St means fix this object ( if you move one of its parents, it + * won't move ), nSt ( the default ) means don't fix this object. + * Parents: + * The numbers of the objects this object depends on + * Constants: + * Constants whose meaning depends on the type of object. E.g. for + * a point, this means first x, then y component. + */ + +struct CabriObject +{ + uint id; + QCString type; + uint numberOfParents; + QColor color; + QColor fillColor; + int thick; + int lineSegLength; + int lineSegSplit; + int specialAppearanceSwitch; + bool visible; + bool fixed; + std::vector<int> parents; + std::vector<double> data; +}; + +KigFilterCabri::KigFilterCabri() +{ +} + +KigFilterCabri::~KigFilterCabri() +{ +} + +bool KigFilterCabri::supportMime( const QString& mime ) +{ + // ugly hack to avoid duplicate extension ( XFig and Cabri files + // have the same .fig extension ). + return ( mime == "image/x-xfig" ) || + ( mime == "application/x-cabri" ); +} + +static QString readLine( QFile& file ) +{ + QString ret; + file.readLine( ret, 10000L ); + if ( ret[ret.length() - 1] == '\n' ) + ret.truncate( ret.length() - 1 ); + if ( ret[ret.length() - 1] == '\r' ) + ret.truncate( ret.length() - 1 ); + return ret; +} + +static QColor translatecolor( const QString& s ) +{ + if ( s == "R" ) return Qt::red; + if ( s == "O" ) return Qt::magenta; + if ( s == "Y" ) return Qt::yellow; + if ( s == "P" ) return Qt::darkMagenta; + if ( s == "V" ) return Qt::darkBlue; + if ( s == "Bl" ) return Qt::blue; + if ( s == "lBl" ) return Qt::cyan; // TODO: bright blue + if ( s == "G" ) return Qt::green; + if ( s == "dG" ) return Qt::darkGreen; + if ( s == "Br" ) return QColor( 165, 42, 42 ); + if ( s == "dBr" ) return QColor( 128, 128, 0 ); + if ( s == "lGr" ) return Qt::lightGray; + if ( s == "Gr" ) return Qt::gray; + if ( s == "dGr" ) return Qt::darkGray; + if ( s == "B" ) return Qt::black; + if ( s == "W" ) return Qt::white; + + kdDebug() << k_funcinfo << "unknown color: " << s << endl; + return Qt::black; +} + +bool KigFilterCabri::readObject( QFile& f, CabriObject& myobj ) +{ + // there are 4 lines per object in the file, so we read them all + // four now. + QString line1, line2, line3, s; + QString file = f.name(); + line1 = readLine( f ); + line2 = readLine( f ); + line3 = readLine( f ); + // ignore line 4, it is empty.. + s = readLine( f ); + + QRegExp firstlinere( "^([^:]+): ([^,]+), ([^,]+), CN:([^,]*), VN:(.*)$" ); + if ( ! firstlinere.exactMatch( line1 ) ) + KIG_FILTER_PARSE_ERROR; + + bool ok; + QString tmp; + + tmp = firstlinere.cap( 1 ); + myobj.id = tmp.toInt( &ok ); + if ( !ok ) KIG_FILTER_PARSE_ERROR; + + tmp = firstlinere.cap( 2 ); + myobj.type = tmp.latin1(); + + tmp = firstlinere.cap( 3 ); + // i have no idea what this number means.. + + tmp = firstlinere.cap( 4 ); + myobj.numberOfParents = tmp.toInt( &ok ); + if ( ! ok ) KIG_FILTER_PARSE_ERROR; + + tmp = firstlinere.cap( 5 ); + // i have no idea what this number means.. + + QRegExp secondlinere( "^([^,]+), ([^,]+), ([^,]+), DS:([^ ]+) ([^,]+), GT:([^,]+), ([^,]+), (.*)$" ); + if ( ! secondlinere.exactMatch( line2 ) ) + KIG_FILTER_PARSE_ERROR; + + tmp = secondlinere.cap( 1 ); + myobj.color = translatecolor( tmp ); +// if ( ! color.isValid() ) KIG_FILTER_PARSE_ERROR; + + tmp = secondlinere.cap( 2 ); + myobj.fillColor = translatecolor( tmp ); +// if ( ! fillcolor.isValid() ) KIG_FILTER_PARSE_ERROR; + + tmp = secondlinere.cap( 3 ); + myobj.thick = tmp == "t" ? 1 : tmp == "tT" ? 2 : 3; + + tmp = secondlinere.cap( 4 ); + myobj.lineSegLength = tmp.toInt( &ok ); + if ( ! ok ) KIG_FILTER_PARSE_ERROR; + + tmp = secondlinere.cap( 5 ); + myobj.lineSegSplit = tmp.toInt( &ok ); + if ( ! ok ) KIG_FILTER_PARSE_ERROR; + + tmp = secondlinere.cap( 6 ); + myobj.specialAppearanceSwitch = tmp.toInt( &ok ); + if ( ! ok ) KIG_FILTER_PARSE_ERROR; + + tmp = secondlinere.cap( 7 ); + myobj.visible = tmp == "V"; + + tmp = secondlinere.cap( 8 ); + myobj.fixed = tmp == "St"; + + QRegExp thirdlinere( "^(Const: ([^,]*),? ?)?(Val: (.*))?$" ); + if ( ! thirdlinere.exactMatch( line3 ) ) + KIG_FILTER_PARSE_ERROR; + + tmp = thirdlinere.cap( 2 ); + QStringList parentsids = QStringList::split( ' ', tmp ); + for ( QStringList::iterator i = parentsids.begin(); + i != parentsids.end(); ++i ) + { + myobj.parents.push_back( ( *i ).toInt( &ok ) ); + if ( ! ok ) KIG_FILTER_PARSE_ERROR; + } + if ( myobj.parents.size() != myobj.numberOfParents ) + KIG_FILTER_PARSE_ERROR; + + tmp = thirdlinere.cap( 4 ); + QStringList valIds = QStringList::split( ' ', tmp ); + for ( QStringList::iterator i = valIds.begin(); + i != valIds.end(); ++i ) + { + myobj.data.push_back( ( *i ).toDouble( &ok ) ); + if ( ! ok ) KIG_FILTER_PARSE_ERROR; + } +// kdDebug() +// << k_funcinfo << endl +// << "id = " << myobj.id << endl +// << "type = " << myobj.type << endl +// << "numberOfParents = " << myobj.numberOfParents << endl +// << "color = " << myobj.color.name() << endl +// << "fillcolor = " << myobj.fillColor.name() << endl +// << "thick = " << myobj.thick << endl +// << "lineseglength = " << myobj.lineSegLength << endl +// << "linesegsplit = " << myobj.lineSegSplit << endl +// << "specialAppearanceSwitch = " << myobj.specialAppearanceSwitch << endl +// << "visible = " << visible << endl +// << "fixed = " << myobj.fixed << endl +// << "parents =" << endl; +// for ( std::vector<int>::iterator i = myobj.parents.begin(); i != myobj.parents.end(); ++i ) +// kdDebug() << " " << *i << endl; +// kdDebug() << "vals = " << endl; +// for ( std::vector<double>::iterator i = myobj.data.begin(); i != myobj.data.end(); ++i ) +// kdDebug() << " " << *i << endl; + + return true; +} + +KigDocument* KigFilterCabri::load( const QString& file ) +{ + QFile f( file ); + if ( ! f.open( IO_ReadOnly ) ) + { + fileNotFound( file ); + return 0; + } + + KigDocument* ret = new KigDocument(); + + QString s = readLine( f ); + QString a = s.left( 21 ); + QString b = s.mid( 21 ); + if( a != "FIGURE CabriII vers. " || + ( b != "DOS 1.0" && b != "MS-Windows 1.0" ) ) + { + if ( s.left( 5 ) == "#FIG " ) + { + notSupported( file, i18n( "This is an XFig file, not a Cabri figure." ) ); + return 0; + } + else + KIG_FILTER_PARSE_ERROR; + } + + // next we have: + // line 2: empty line + // line 3: window dimensions -> we don't need/use that... + // line 4: empty line + // line 5 through 8: center point + // line 9 through 12: axes + // so we skip 11 lines... + for( int i = 0; i != 11; ++i) + s = readLine( f ); + + // all Cabri files seem to at least have these center and axes... + if ( f.atEnd() ) + KIG_FILTER_PARSE_ERROR; + + std::vector<ObjectHolder*> holders; + std::vector<ObjectCalcer*> calcers; + + const ObjectFactory* fact = ObjectFactory::instance(); + + std::vector<ObjectCalcer*> args; + ObjectCalcer* oc = 0; + + while ( ! f.atEnd() ) + { + CabriObject obj; + // we do one object each iteration.. + if ( !readObject( f, obj ) ) + return 0; + +// reading linestyle + Qt::PenStyle ls = Qt::SolidLine; + if ( ( obj.lineSegLength > 1 ) && ( obj.lineSegLength < 6 ) && + ( obj.lineSegSplit > 1 ) && ( obj.lineSegSplit <= 10 ) ) + ls = Qt::DotLine; + else if ( ( obj.lineSegLength >= 6 ) && ( obj.lineSegSplit > 10 ) ) + ls = Qt::DashLine; + int ps = 0; + + args.clear(); + for ( std::vector<int>::iterator i = obj.parents.begin(); + i != obj.parents.end(); ++i ) + args.push_back( calcers[*i-3] ); + + // two fake objects at the start ( origin and axes.. ) + if ( obj.id != calcers.size() + 3 ) KIG_FILTER_PARSE_ERROR; + oc = 0; + if ( obj.type == "Pt" ) + { + if ( ! args.empty() ) KIG_FILTER_PARSE_ERROR; + if ( obj.data.size() != 2 ) KIG_FILTER_PARSE_ERROR; + + switch ( obj.specialAppearanceSwitch ) + { + case 0: + { + obj.thick -= 1; + break; + } + case 2: + { + obj.thick += 1; + break; + } + case 3: + { + obj.thick += 1; + ps = 1; + break; + } + case 4: + { + obj.thick += 2; + ps = 4; + break; + } + } + // different sizes for points.. + obj.thick *= 2; + + oc = fact->fixedPointCalcer( Coordinate( obj.data[0], obj.data[1] ) ); + } + else if ( obj.type == "Cir" ) + { + if ( args.size() == 1 ) + { + if ( obj.data.size() != 1 ) KIG_FILTER_PARSE_ERROR; + ObjectConstCalcer* radc = + new ObjectConstCalcer( new DoubleImp( obj.data[0] ) ); + args.push_back( radc ); + oc = new ObjectTypeCalcer( CircleBPRType::instance(), args ); + } + else if ( args.size() == 2 ) + { + if ( ! obj.data.empty() ) KIG_FILTER_PARSE_ERROR; + oc = new ObjectTypeCalcer( CircleBCPType::instance(), args ); + } + else KIG_FILTER_PARSE_ERROR; + } + else if ( obj.type == "Line" || obj.type == "Ray" || obj.type == "Seg" || + obj.type == "Vec" ) + { + if ( args.size() == 1 ) + { + if ( obj.data.size() != 2 ) KIG_FILTER_PARSE_ERROR; + Coordinate vect( obj.data[0], obj.data[1] ); + ObjectConstCalcer* vectorcalcer = + new ObjectConstCalcer( new VectorImp( Coordinate( 0, 0 ), vect ) ); + args.push_back( vectorcalcer ); + ObjectTypeCalcer* secondpoint = + new ObjectTypeCalcer( TranslatedType::instance(), args ); + secondpoint->calc( *ret ); + args[1] = secondpoint; + } + if ( args.size() != 2 ) KIG_FILTER_PARSE_ERROR; + const ObjectType* t = 0; + if ( obj.type == "Line" ) t = LineABType::instance(); + else if ( obj.type == "Ray" ) t = RayABType::instance(); + else if ( obj.type == "Seg" ) t = SegmentABType::instance(); + else if ( obj.type == "Vec" ) t = VectorType::instance(); + else assert( t ); + oc = new ObjectTypeCalcer( t, args ); + } + else if ( obj.type == "Pt/" ) + { + // different sizes for points.. + obj.thick *= 2; + if ( args.size() != 1 || obj.data.size() != 2 ) + KIG_FILTER_PARSE_ERROR; + ObjectCalcer* parent = args[0]; + if ( !parent->imp()->inherits( CurveImp::stype() ) ) + KIG_FILTER_PARSE_ERROR; + const CurveImp* curve = static_cast<const CurveImp*>( parent->imp() ); + Coordinate pt = Coordinate( obj.data[0], obj.data[1] ); + double param = curve->getParam( pt, *ret ); + args.push_back( new ObjectConstCalcer( new DoubleImp( param ) ) ); + oc = new ObjectTypeCalcer( ConstrainedPointType::instance(), args ); + } + else if ( obj.type == "Perp" || obj.type == "Par" ) + { + if ( args.size() != 2 || obj.data.size() != 0 ) + KIG_FILTER_PARSE_ERROR; + const ObjectType* t = 0; + if ( obj.type == "Perp" ) t = LinePerpendLPType::instance(); + else if ( obj.type == "Par" ) t = LineParallelLPType::instance(); + else assert( false ); + oc = new ObjectTypeCalcer( t, args ); + } + else if ( obj.type == "Arc" ) + { + if ( args.size() != 3 || ! obj.data.empty() ) + KIG_FILTER_PARSE_ERROR; + oc = new ObjectTypeCalcer( ArcBTPType::instance(), args ); + } + else if ( obj.type == "Con" ) + { + if ( args.size() != 5 || !obj.data.empty() ) + KIG_FILTER_PARSE_ERROR; + oc = new ObjectTypeCalcer( ConicB5PType::instance(), args ); + } + else if ( obj.type == "Mid" ) + { + if ( args.size() == 2 ) + { + ObjectCalcer* c = + new ObjectTypeCalcer( SegmentABType::instance(), args ); + c->calc( *ret ); + args.clear(); + args.push_back( c ); + } + // midpoint -> this can be the midpoint of a segment, two + // points, or a vector... + if ( args.size() != 1 || !obj.data.empty() ) + KIG_FILTER_PARSE_ERROR; + ObjectCalcer* parent = args[0]; + if ( parent->imp()->inherits( SegmentImp::stype() ) ) + oc = fact->propertyObjectCalcer( parent, "mid-point" ) ; + else if ( parent->imp()->inherits( VectorImp::stype() ) ) + oc = fact->propertyObjectCalcer( parent, "vect-mid-point" ); + else KIG_FILTER_PARSE_ERROR; + } + else if ( obj.type == "PBiss" ) + { + if ( args.size() == 2 ) + { + ObjectCalcer* c = + new ObjectTypeCalcer( SegmentABType::instance(), args ); + c->calc( *ret ); + args.clear(); + args.push_back( c ); + } + if ( args.size() != 1 || !obj.data.empty() ) + KIG_FILTER_PARSE_ERROR; + ObjectCalcer* parent = args[0]; + ObjectCalcer* midpoint = 0; + if ( parent->imp()->inherits( SegmentImp::stype() ) ) + midpoint = fact->propertyObjectCalcer( parent, "mid-point" ) ; +// else if ( parent->imp()->inherits( VectorImp::stype() ) ) +// midpoint = fact->propertyObjectCalcer( parent, "vect-mid-point" ); + else KIG_FILTER_PARSE_ERROR; + midpoint->calc( *ret ); + args.push_back( midpoint ); + oc = new ObjectTypeCalcer( LinePerpendLPType::instance(), args ); + } + else if ( obj.type == "Pol" ) + { + if ( args.size() < 3 || !obj.data.empty() ) + KIG_FILTER_PARSE_ERROR; + oc = new ObjectTypeCalcer( PolygonBNPType::instance(), args ); + } + else if ( obj.type == "Locus" ) + { + if ( args.size() != 2 || !obj.data.empty() ) + KIG_FILTER_PARSE_ERROR; + oc = fact->locusCalcer( args[0], args[1] ); + } + else if ( obj.type == "Refl" ) + { + if ( args.size() != 2 || !obj.data.empty() ) + KIG_FILTER_PARSE_ERROR; + oc = new ObjectTypeCalcer( LineReflectionType::instance(), args ); + } + else if ( obj.type == "Sym" ) + { + if ( args.size() != 2 || !obj.data.empty() ) + KIG_FILTER_PARSE_ERROR; + oc = new ObjectTypeCalcer( PointReflectionType::instance(), args ); + } + else if ( obj.type == "Tran" ) + { + if ( args.size() != 2 || !obj.data.empty() ) + KIG_FILTER_PARSE_ERROR; + oc = new ObjectTypeCalcer( TranslatedType::instance(), args ); + } + else + { + notSupported( file, i18n( "This Cabri file contains a \"%1\" object, " + "which Kig does not currently support." ).arg( obj.type ) ); + return 0; + } + + if ( oc == 0 ) KIG_FILTER_PARSE_ERROR; + + oc->calc( *ret ); + calcers.push_back( oc ); + ObjectDrawer* d = new ObjectDrawer( obj.color, obj.thick, obj.visible, ls, ps ); + ObjectHolder* oh = new ObjectHolder( oc, d ); + holders.push_back( oh ); + + oc = 0; + } + + ret->addObjects( holders ); + ret->setGrid( false ); + ret->setAxes( false ); + return ret; +} + +KigFilterCabri* KigFilterCabri::instance() +{ + static KigFilterCabri t; + return &t; +} diff --git a/kig/filters/cabri-filter.h b/kig/filters/cabri-filter.h new file mode 100644 index 00000000..5760040f --- /dev/null +++ b/kig/filters/cabri-filter.h @@ -0,0 +1,55 @@ +// This file is part of Kig, a KDE program for Interactive Geometry... +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_FILTERS_CABRI_FILTER_H +#define KIG_FILTERS_CABRI_FILTER_H + +#include "filter.h" + +struct CabriObject; + +class QFile; + +/** + * This is an import filter for the output of the commercial program + * Cabri ("CAhier de BRouillon Interactif" or something like that), + * which is being pushed by Texas Instruments, but only exists for + * the Winblows(tm) platform and some TI scientific calculator... + * + * \note + * This is completely free code, i have not looked at any Cabri source + * code, and have implemented this implementation of the Cabri file + * format from zero, by just looking at the input and output from a + * (properly licensed) Cabri program... + */ +class KigFilterCabri + : public KigFilter +{ + KigFilterCabri(); + ~KigFilterCabri(); +public: + static KigFilterCabri* instance(); + + bool supportMime ( const QString& mime ); + KigDocument* load ( const QString& fromfile ); +private: + bool readObject( QFile& f, CabriObject& myobj ); + +}; + +#endif diff --git a/kig/filters/drgeo-filter-chooser.cc b/kig/filters/drgeo-filter-chooser.cc new file mode 100644 index 00000000..424a3462 --- /dev/null +++ b/kig/filters/drgeo-filter-chooser.cc @@ -0,0 +1,65 @@ +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> +// Copyright (C) 2004 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "drgeo-filter-chooser.h" +#include "drgeo-filter-chooser.moc" + +#include <klistbox.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kpushbutton.h> +#include <kstdguiitem.h> + +KigFilterDrgeoChooser::KigFilterDrgeoChooser( const QStringList& l ) + : KigFilterDrgeoChooserBase( 0, "drgeo_filter", true ) +{ + OKButton->setGuiItem( KStdGuiItem::ok() ); + CancelButton->setGuiItem( KStdGuiItem::cancel() ); + + FigureListBox->insertStringList( l ); + + connect( OKButton, SIGNAL( clicked() ), SLOT( slotOKPressed() ) ); + connect( CancelButton, SIGNAL( clicked() ), SLOT( slotCancelPressed() ) ); + connect( FigureListBox, SIGNAL( executed( QListBoxItem* ) ), SLOT( slotExecuted( QListBoxItem* ) ) ); +} + +void KigFilterDrgeoChooser::slotOKPressed() +{ + const int r = FigureListBox->currentItem(); + if ( r == -1 ) + { + KMessageBox::sorry( 0, i18n( "Please select a figure." ) ); + return; + } + done( r ); +} + +void KigFilterDrgeoChooser::slotCancelPressed() +{ + done( -1 ); +} + +KigFilterDrgeoChooser::~KigFilterDrgeoChooser() +{ + +} + +void KigFilterDrgeoChooser::slotExecuted( QListBoxItem* i ) +{ + done( FigureListBox->index( i ) ); +} diff --git a/kig/filters/drgeo-filter-chooser.h b/kig/filters/drgeo-filter-chooser.h new file mode 100644 index 00000000..fd4ce606 --- /dev/null +++ b/kig/filters/drgeo-filter-chooser.h @@ -0,0 +1,41 @@ +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef DRGEOFILTERCHOOSER_H +#define DRGEOFILTERCHOOSER_H + +#include "drgeo-filter-chooserbase.h" + +class QListBoxItem; +class QStringList; + +class KigFilterDrgeoChooser + : public KigFilterDrgeoChooserBase +{ + Q_OBJECT + +public: + KigFilterDrgeoChooser( const QStringList& l ); + ~KigFilterDrgeoChooser(); +public slots: + void slotOKPressed(); + void slotCancelPressed(); + void slotExecuted( QListBoxItem* ); +}; + +#endif diff --git a/kig/filters/drgeo-filter-chooserbase.ui b/kig/filters/drgeo-filter-chooserbase.ui new file mode 100644 index 00000000..829d84cc --- /dev/null +++ b/kig/filters/drgeo-filter-chooserbase.ui @@ -0,0 +1,140 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>KigFilterDrgeoChooserBase</class> +<widget class="QDialog"> + <property name="name"> + <cstring>KigFilterDrgeoChooserBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>300</width> + <height>202</height> + </rect> + </property> + <property name="caption"> + <string>Dr. Geo Filter</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>ExplanationTextLabel</cstring> + </property> + <property name="text"> + <string>The current Dr. Geo file contains more than one figure. +Please select which to import:</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout1</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="KListBox"> + <property name="name"> + <cstring>FigureListBox</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="Line"> + <property name="name"> + <cstring>Line1</cstring> + </property> + <property name="frameShape"> + <enum>HLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout7</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <spacer> + <property name="name"> + <cstring>spacer</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KPushButton"> + <property name="name"> + <cstring>OKButton</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&OK</string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>CancelButton</cstring> + </property> + <property name="text"> + <string>&Cancel</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klistbox.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/kig/filters/drgeo-filter-status.txt b/kig/filters/drgeo-filter-status.txt new file mode 100644 index 00000000..db399eae --- /dev/null +++ b/kig/filters/drgeo-filter-status.txt @@ -0,0 +1,50 @@ +Dr. Geo filter status +===================== + +Objects imported +---------------- + +Points most, On_curve (most) +Rays ok +Vectors ok +Lines most +Circles most +Numberics most (as a label) +Angles 3pts +Arcs ok +Locuses ok +Transformations most +Intersections most +Scripts as a label +Equations ok +Polygons ok + +Colors ok +Visibility ok +Styles ok +Grid ok + +Objects not imported +-------------------- + +Points Coordinate(*) +Lines pt_slope(*) +Circle radius(*) +Numberics distance_pt_circle +Angles vectors +Transformations scaling(*) +Intersections arc-circle(**), locus-other object(**) + +(*) objects which depend on numerics +(**) objects which currently are not implemented in Kig + +Objects ignored +--------------- + +boundingBox +customUI + +Known problems +-------------- + +* Point(On_curve): does not work with locuses diff --git a/kig/filters/drgeo-filter.cc b/kig/filters/drgeo-filter.cc new file mode 100644 index 00000000..8fa306cc --- /dev/null +++ b/kig/filters/drgeo-filter.cc @@ -0,0 +1,809 @@ +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> +// Copyright (C) 2004 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "drgeo-filter.h" + +#include "drgeo-filter-chooser.h" +#include "filters-common.h" + +#include "../kig/kig_document.h" +#include "../kig/kig_part.h" +#include "../misc/common.h" +#include "../misc/coordinate.h" +#include "../objects/angle_type.h" +#include "../objects/arc_type.h" +#include "../objects/bogus_imp.h" +#include "../objects/circle_imp.h" +#include "../objects/circle_type.h" +#include "../objects/conic_imp.h" +#include "../objects/conic_types.h" +#include "../objects/curve_imp.h" +#include "../objects/intersection_types.h" +#include "../objects/line_imp.h" +#include "../objects/line_type.h" +#include "../objects/object_calcer.h" +#include "../objects/object_drawer.h" +#include "../objects/object_factory.h" +#include "../objects/object_holder.h" +#include "../objects/object_type.h" +#include "../objects/other_imp.h" +#include "../objects/other_type.h" +#include "../objects/point_imp.h" +#include "../objects/point_type.h" +#include "../objects/polygon_type.h" +#include "../objects/transform_types.h" +#include "../objects/vector_type.h" + +#include <math.h> + +#include <qfile.h> +#include <qnamespace.h> + +#include <klocale.h> + +#undef DRGEO_DEBUG +//#define DRGEO_DEBUG + +struct DrGeoHierarchyElement +{ + QString id; + std::vector<QString> parents; +}; + +KigFilterDrgeo::KigFilterDrgeo() +{ +} + +KigFilterDrgeo::~KigFilterDrgeo() +{ +} + +bool KigFilterDrgeo::supportMime( const QString& mime ) +{ + return mime == "application/x-drgeo"; +} + +KigDocument* KigFilterDrgeo::load( const QString& file ) +{ + QFile f( file ); + if ( ! f.open( IO_ReadOnly ) ) + { + fileNotFound( file ); + return 0; + } + + QStringList figures; + QDomDocument doc( "drgenius" ); + if ( !doc.setContent( &f ) ) + KIG_FILTER_PARSE_ERROR; + QDomElement main = doc.documentElement(); + int nmacros = 0; + // reading figures... + for ( QDomNode n = main.firstChild(); ! n.isNull(); n = n.nextSibling() ) + { + QDomElement e = n.toElement(); + if ( e.isNull() ) continue; + else if ( e.tagName() == "drgeo" ) + figures.append( e.attribute( "name" ) ); + else if ( e.tagName() == "macro" ) + nmacros++; + } + if ( figures.isEmpty() ) { + if( nmacros > 0 ) + warning( i18n( "The Dr. Geo file \"%1\" is a macro file so it contains no " + "figures." ).arg( file ) ); + else + warning( i18n( "There are no figures in Dr. Geo file \"%1\"." ).arg( file ) ); + return false; + } + + int nfig = figures.count(); + // no figures, no party... + if ( nfig == 0 ) + return 0; + + int myfig = 0; + + if ( nfig > 1 ) + { + // Dr. Geo file has more than 1 figure, let the user choose one... + KigFilterDrgeoChooser* c = new KigFilterDrgeoChooser( figures ); + myfig = c->exec(); + delete c; + } + +#ifdef DRGEO_DEBUG + kdDebug() << "drgeo file " << file << endl; +#endif + int curfig = -1; + + for ( QDomNode n = main.firstChild(); ! n.isNull(); n = n.nextSibling() ) + { + QDomElement e = n.toElement(); + if ( e.isNull() ) continue; + else if ( e.tagName() == "drgeo" ) + { + curfig += 1; + if ( curfig == myfig ) + { +#ifdef DRGEO_DEBUG + kdDebug() << "- Figure: '" << e.attribute("name") << "'" << endl; +#endif + bool grid = !e.attribute( "grid" ).isEmpty() && + ( e.attribute( "grid" ) != "False" ); + return importFigure( e.firstChild(), file, grid ); + } + } + } + + return 0; +} + +int convertDrgeoIndex( const std::vector<DrGeoHierarchyElement> es, const QString myid ) +{ + for ( uint i = 0; i < es.size(); ++i ) + if ( es[i].id == myid ) + return i; + return -1; +} + +const Coordinate convertDrgeoLineParam( const double param, const LineData& line ) +{ + const double n = ( param - 0.5 ) * M_PI; + const Coordinate c = line.dir() / line.dir().length(); + const Coordinate p = line.a + tan( n ) * c; + return p; +} + +const Coordinate convertDrgeoHalflineParam( const double param, const LineData& line ) +{ + const double n = param * M_PI * 0.5; + const Coordinate c = line.dir() / line.dir().length(); + const Coordinate p = line.a + tan( n ) * c; + return p; +} + +KigDocument* KigFilterDrgeo::importFigure( QDomNode f, const QString& file, const bool grid ) +{ + KigDocument* ret = new KigDocument(); + + using namespace std; + std::vector<DrGeoHierarchyElement> elems; + int withoutid = 0; + + // 1st: fetch relationships and build an appropriate structure + for (QDomNode a = f; ! a.isNull(); a = a.nextSibling() ) + { + QDomElement domelem = a.toElement(); + if ( domelem.isNull() ) continue; + else + { + DrGeoHierarchyElement elem; +#ifdef DRGEO_DEBUG + kdDebug() << " * " << domelem.tagName() << "(" << domelem.attribute("type") << ")" << endl; +#endif + for ( QDomNode c = domelem.firstChild(); ! c.isNull(); c = c.nextSibling() ) + { + QDomElement ce = c.toElement(); + if ( ce.isNull() ) continue; + else if ( ce.tagName() == "parent" ) + elem.parents.push_back( ce.attribute( "ref" ) ); + } + QString curid = domelem.attribute( "id" ); + elem.id = !curid.isNull() ? curid : QString::number( withoutid++ ) ; + elems.push_back( elem ); + } + } + +#ifdef DRGEO_DEBUG + QString x; + kdDebug() << "+++ elems" << endl; + for ( uint i = 0; i < elems.size(); ++i ) + { + x = ""; + for ( uint j = 0; j < elems[i].parents.size(); ++j ) + { + x += elems[i].parents[j] + "_"; + } + kdDebug() << " --> " << i << " - " << elems[i].id << " - " << x << endl; + } +#endif + + // 2nd: let's draw! + int curid = 0; + const ObjectFactory* fact = ObjectFactory::instance(); + std::vector<ObjectHolder*> holders; + std::vector<ObjectHolder*> holders2; + ObjectTypeCalcer* oc = 0; + ObjectCalcer* oc2 = 0; + int nignored = 0; + + // there's no need to sort the objects because it seems that DrGeo objects + // appear in the right order... so let's go! + for (QDomNode a = f; ! a.isNull(); a = a.nextSibling() ) + { +#ifdef DRGEO_DEBUG + kdDebug() << "+++ id: " << curid << endl; +#endif + const DrGeoHierarchyElement& el = elems[curid]; + std::vector<ObjectCalcer*> parents; + for ( uint j = 0; j < el.parents.size(); ++j ) + { + int parentid = convertDrgeoIndex( elems, el.parents[j] ); + if ( parentid == -1 ) + KIG_FILTER_PARSE_ERROR; + parents.push_back( holders[parentid-nignored]->calcer() ); + }; + QDomElement domelem = a.toElement(); + +#ifdef DRGEO_DEBUG + if ( parents.size() > 0 ) + for ( uint j = 0; j < parents.size(); ++j ) + { + kdDebug() << "+++++++++ parent[" << j << "]: " << parents[j] << " - " + << parents[j]->imp()->type()->internalName() << endl; + } + else + kdDebug() << "+++++++++ parents: NO" << endl; + kdDebug() << "+++++++++ " << domelem.tagName() << " - " << domelem.attribute("type") << endl; +#endif + + if ( domelem.isNull() ) continue; + else if ( domelem.tagName() == "point" ) + { + QString xs; + QString ys; + QString values; + for ( QDomNode c = domelem.firstChild(); ! c.isNull(); c = c.nextSibling() ) + { + QDomElement ce = c.toElement(); + if ( ce.isNull() ) continue; + else if ( ce.tagName() == "x" ) + xs = ce.text(); + else if ( ce.tagName() == "y" ) + ys = ce.text(); + else if ( ce.tagName() == "value" ) + values = ce.text(); + } + if ( domelem.attribute( "type" ) == "Free" ) + { + bool ok; + bool ok2; + double x = xs.toDouble( &ok ); + double y = ys.toDouble( &ok2 ); + if ( ! ( ok && ok2 ) ) + KIG_FILTER_PARSE_ERROR; + oc = fact->fixedPointCalcer( Coordinate( x, y ) ); + } + else if ( domelem.attribute( "type" ) == "Middle_2pts" ) + oc = new ObjectTypeCalcer( MidPointType::instance(), parents ); + else if ( domelem.attribute( "type" ) == "Middle_segment" ) + { + if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR; + if ( !parents[0]->imp()->inherits( SegmentImp::stype() ) ) + KIG_FILTER_PARSE_ERROR; + ObjectPropertyCalcer* o1 = fact->propertyObjectCalcer( parents[0], "end-point-A" ); + o1->calc( *ret ); + ObjectPropertyCalcer* o2 = fact->propertyObjectCalcer( parents[0], "end-point-B" ); + o2->calc( *ret ); + std::vector<ObjectCalcer*> args; + args.push_back( o1 ); + args.push_back( o2 ); + oc = new ObjectTypeCalcer( MidPointType::instance(), args ); + } + else if ( domelem.attribute( "type" ) == "On_curve" ) + { + bool ok3; + double value = values.toDouble( &ok3 ); + if ( ! ok3 ) + KIG_FILTER_PARSE_ERROR; + if ( ( parents[0]->imp()->inherits( CircleImp::stype() ) ) || + ( parents[0]->imp()->inherits( SegmentImp::stype() ) ) ) + oc = fact->constrainedPointCalcer( parents[0], value ); + else if ( parents[0]->imp()->inherits( LineImp::stype() ) ) + { + const LineData l = static_cast<const LineImp*>( parents[0]->imp() )->data(); + const Coordinate p = convertDrgeoLineParam( value, l ); + oc = fact->constrainedPointCalcer( parents[0], p, *ret ); + } + else if ( parents[0]->imp()->inherits( RayImp::stype() ) ) + { + const LineData l = static_cast<const RayImp*>( parents[0]->imp() )->data(); + const Coordinate p = convertDrgeoHalflineParam( value, l ); + oc = fact->constrainedPointCalcer( parents[0], p, *ret ); + } + else if ( parents[0]->imp()->inherits( ArcImp::stype() ) ) + oc = fact->constrainedPointCalcer( parents[0], 1 - value ); + else + { +// oc = fact->constrainedPointCalcer( parents[0], value ); + notSupported( file, i18n( "This Dr. Geo file contains a \"%1 %2\" object, " + "which Kig does not currently support." ).arg( domelem.tagName() ).arg( + domelem.attribute( "type" ) ) ); + return false; + } + } + else if ( domelem.attribute( "type" ) == "Intersection" ) + { + if ( ( parents[0]->imp()->inherits( AbstractLineImp::stype() ) ) && + ( parents[1]->imp()->inherits( AbstractLineImp::stype() ) ) ) + oc = new ObjectTypeCalcer( LineLineIntersectionType::instance(), parents ); + else + { + bool ok; + int which = domelem.attribute( "extra" ).toInt( &ok ); + if ( !ok ) KIG_FILTER_PARSE_ERROR; + if ( which == 1 ) which = -1; + else if ( which == 0 ) which = 1; + else KIG_FILTER_PARSE_ERROR; + std::vector<ObjectCalcer*> args = parents; + const ObjectType* type = 0; + args.push_back( new ObjectConstCalcer( new IntImp( which ) ) ); + if ( ( parents[0]->imp()->inherits( CircleImp::stype() ) ) && + ( parents[1]->imp()->inherits( CircleImp::stype() ) ) ) + type = CircleCircleIntersectionType::instance(); + else if ( ( parents[0]->imp()->inherits( CircleImp::stype() ) && + parents[1]->imp()->inherits( AbstractLineImp::stype() ) ) || + ( parents[1]->imp()->inherits( CircleImp::stype() ) && + parents[0]->imp()->inherits( AbstractLineImp::stype() ) ) ) + type = ConicLineIntersectionType::instance(); + else if ( ( parents[0]->imp()->inherits( ArcImp::stype() ) && + parents[1]->imp()->inherits( AbstractLineImp::stype() ) ) || + ( parents[1]->imp()->inherits( ArcImp::stype() ) && + parents[0]->imp()->inherits( AbstractLineImp::stype() ) ) ) + type = ArcLineIntersectionType::instance(); + else + { + notSupported( file, i18n( "This Dr. Geo file contains an intersection type, " + "which Kig does not currently support." ) ); + return false; + } + oc = new ObjectTypeCalcer( type, args ); + } + } + else if ( domelem.attribute( "type" ) == "Reflexion" ) + oc = new ObjectTypeCalcer( LineReflectionType::instance(), parents ); + else if ( domelem.attribute( "type" ) == "Symmetry" ) + oc = new ObjectTypeCalcer( PointReflectionType::instance(), parents ); + else if ( domelem.attribute( "type" ) == "Translation" ) + oc = new ObjectTypeCalcer( TranslatedType::instance(), parents ); + else if ( domelem.attribute( "type" ) == "Rotation" ) + oc = new ObjectTypeCalcer( RotationType::instance(), parents ); + else + { + notSupported( file, i18n( "This Dr. Geo file contains a \"%1 %2\" object, " + "which Kig does not currently support." ).arg( domelem.tagName() ).arg( + domelem.attribute( "type" ) ) ); + return false; + } +#ifdef DRGEO_DEBUG + kdDebug() << "+++++++++ oc:" << oc << endl; +#endif + } + else if( ( domelem.tagName() == "line" ) || + ( domelem.tagName() == "halfLine" ) || + ( domelem.tagName() == "segment" ) || + ( domelem.tagName() == "vector" ) || + ( domelem.tagName() == "circle" ) || + ( domelem.tagName() == "arcCircle" ) || + ( domelem.tagName() == "polygon" ) ) + { + const ObjectType* type = 0; + if ( domelem.attribute( "type" ) == "2pts" ) + { + if( domelem.tagName() == "line" ) + type = LineABType::instance(); + else if( domelem.tagName() == "halfLine" ) + type = RayABType::instance(); + else if( domelem.tagName() == "segment" ) + type = SegmentABType::instance(); + else if( domelem.tagName() == "vector" ) + type = VectorType::instance(); + else if( domelem.tagName() == "circle" ) + type = CircleBCPType::instance(); + else + { + notSupported( file, i18n( "This Dr. Geo file contains a \"%1 %2\" object, " + "which Kig does not currently support." ).arg( domelem.tagName() ).arg( + domelem.attribute( "type" ) ) ); + return false; + } + oc = new ObjectTypeCalcer( type, parents ); + } + else if( domelem.attribute( "type" ) == "3pts" ) + { + if( domelem.tagName() == "arcCircle" ) + type = ArcBTPType::instance(); + else + { + notSupported( file, i18n( "This Dr. Geo file contains a \"%1 %2\" object, " + "which Kig does not currently support." ).arg( domelem.tagName() ).arg( + domelem.attribute( "type" ) ) ); + return false; + } + oc = new ObjectTypeCalcer( type, parents ); + } + else if( domelem.attribute( "type" ) == "segment" ) + { + if( domelem.tagName() == "circle" ) + { + type = CircleBPRType::instance(); + ObjectPropertyCalcer* o = fact->propertyObjectCalcer( parents[1], "length" ); + o->calc( *ret ); + ObjectCalcer* a = parents[0]; + parents.clear(); + parents.push_back( a ); + parents.push_back( o ); + } + else + { + notSupported( file, i18n( "This Dr. Geo file contains a \"%1 %2\" object, " + "which Kig does not currently support." ).arg( domelem.tagName() ).arg( + domelem.attribute( "type" ) ) ); + return false; + } + oc = new ObjectTypeCalcer( type, parents ); + } + else if( domelem.attribute( "type" ) == "npts" ) + { + if( domelem.tagName() == "polygon" ) + { + if ( parents.size() < 3 ) KIG_FILTER_PARSE_ERROR; + type = PolygonBNPType::instance(); + } + else + { + notSupported( file, i18n( "This Dr. Geo file contains a \"%1 %2\" object, " + "which Kig does not currently support." ).arg( domelem.tagName() ).arg( + domelem.attribute( "type" ) ) ); + return false; + } + oc = new ObjectTypeCalcer( type, parents ); + } + else if ( domelem.attribute( "type" ) == "perpendicular" ) + oc = new ObjectTypeCalcer( LinePerpendLPType::instance(), parents ); + else if ( domelem.attribute( "type" ) == "parallel" ) + oc = new ObjectTypeCalcer( LineParallelLPType::instance(), parents ); + else if ( domelem.attribute( "type" ) == "Reflexion" ) + oc = new ObjectTypeCalcer( LineReflectionType::instance(), parents ); + else if ( domelem.attribute( "type" ) == "Symmetry" ) + oc = new ObjectTypeCalcer( PointReflectionType::instance(), parents ); + else if ( domelem.attribute( "type" ) == "Translation" ) + oc = new ObjectTypeCalcer( TranslatedType::instance(), parents ); + else if ( domelem.attribute( "type" ) == "Rotation" ) + oc = new ObjectTypeCalcer( RotationType::instance(), parents ); + else + { + notSupported( file, i18n( "This Dr. Geo file contains a \"%1 %2\" object, " + "which Kig does not currently support." ).arg( domelem.tagName() ).arg( + domelem.attribute( "type" ) ) ); + return false; + } +#ifdef DRGEO_DEBUG + kdDebug() << "+++++++++ oc:" << oc << endl; +#endif + } + else if( ( domelem.tagName() == "numeric" ) || + ( domelem.tagName() == "equation" ) ) + { + QString xs; + QString ys; + QString value; + for ( QDomNode c = domelem.firstChild(); ! c.isNull(); c = c.nextSibling() ) + { + QDomElement ce = c.toElement(); + if ( ce.isNull() ) continue; + else if ( ce.tagName() == "x" ) + xs = ce.text(); + else if ( ce.tagName() == "y" ) + ys = ce.text(); + else if ( ce.tagName() == "value" ) + value = ce.text(); + } + bool ok; + bool ok2; + double x = xs.toDouble( &ok ); + double y = ys.toDouble( &ok2 ); + if ( ! ( ok && ok2 ) ) + KIG_FILTER_PARSE_ERROR; + Coordinate m( x, y ); + // types of 'numeric' + // ugly hack to show value numerics... + if ( domelem.attribute( "type" ) == "value" ) + { + bool ok3; + double dvalue = value.toDouble( &ok3 ); + if ( ok3 ) + value = QString( "%1" ).arg( dvalue, 0, 'g', 3 ); + oc = fact->labelCalcer( value, m, false, std::vector<ObjectCalcer*>(), *ret ); + } + else if ( domelem.attribute( "type" ) == "pt_abscissa" ) + { + if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR; + oc = filtersConstructTextObject( m, parents[0], "coordinate-x", *ret, false ); + } + else if ( domelem.attribute( "type" ) == "pt_ordinate" ) + { + if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR; + oc = filtersConstructTextObject( m, parents[0], "coordinate-y", *ret, false ); + } + else if ( domelem.attribute( "type" ) == "segment_length" ) + { + if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR; + oc = filtersConstructTextObject( m, parents[0], "length", *ret, false ); + } + else if ( domelem.attribute( "type" ) == "circle_perimeter" ) + { + if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR; + oc = filtersConstructTextObject( m, parents[0], "circumference", *ret, false ); + } + else if ( domelem.attribute( "type" ) == "arc_length" ) + { + if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR; + oc = filtersConstructTextObject( m, parents[0], "arc-length", *ret, false ); + } + else if ( domelem.attribute( "type" ) == "distance_2pts" ) + { + if ( parents.size() != 2 ) KIG_FILTER_PARSE_ERROR; + ObjectTypeCalcer* so = new ObjectTypeCalcer( SegmentABType::instance(), parents ); + so->calc( *ret ); + oc = filtersConstructTextObject( m, so, "length", *ret, false ); + } + else if ( domelem.attribute( "type" ) == "vector_norm" ) + { + if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR; + oc = filtersConstructTextObject( m, parents[0], "length", *ret, false ); + } + else if ( domelem.attribute( "type" ) == "vector_abscissa" ) + { + if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR; + oc = filtersConstructTextObject( m, parents[0], "length-x", *ret, false ); + } + else if ( domelem.attribute( "type" ) == "vector_ordinate" ) + { + if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR; + oc = filtersConstructTextObject( m, parents[0], "length-y", *ret, false ); + } + else if ( domelem.attribute( "type" ) == "slope" ) + { + if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR; + oc = filtersConstructTextObject( m, parents[0], "slope", *ret, false ); + } + else if ( domelem.attribute( "type" ) == "distance_pt_line" ) + { + if ( parents.size() != 2 ) KIG_FILTER_PARSE_ERROR; + std::vector<ObjectCalcer*> args; + args.push_back( parents[1] ); + args.push_back( parents[0] ); + ObjectTypeCalcer* po = new ObjectTypeCalcer( LinePerpendLPType::instance(), args ); + po->calc( *ret ); + args.clear(); + args.push_back( parents[1] ); + args.push_back( po ); + ObjectTypeCalcer* io = new ObjectTypeCalcer( LineLineIntersectionType::instance(), args ); + io->calc( *ret ); + args.clear(); + args.push_back( parents[0] ); + args.push_back( io ); + ObjectTypeCalcer* so = new ObjectTypeCalcer( SegmentABType::instance(), args ); + so->calc( *ret ); + oc = filtersConstructTextObject( m, so, "length", *ret, false ); + } + // types of 'equation' + else if ( domelem.attribute( "type" ) == "line" ) + { + if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR; + oc = filtersConstructTextObject( m, parents[0], "equation", *ret, false ); + } + else if ( domelem.attribute( "type" ) == "circle" ) + { + if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR; + oc = filtersConstructTextObject( m, parents[0], "simply-cartesian-equation", *ret, false ); + } + else + { + notSupported( file, i18n( "This Dr. Geo file contains a \"%1 %2\" object, " + "which Kig does not currently support." ).arg( domelem.tagName() ).arg( + domelem.attribute( "type" ) ) ); + return false; + } +#ifdef DRGEO_DEBUG + kdDebug() << "+++++++++ oc:" << oc << endl; +#endif + } + else if ( domelem.tagName() == "angle" ) + { + if ( domelem.attribute( "type" ) == "3pts" ) + { + if ( parents.size() != 3 ) KIG_FILTER_PARSE_ERROR; + oc = new ObjectTypeCalcer( HalfAngleType::instance(), parents ); + } + else + { + notSupported( file, i18n( "This Dr. Geo file contains a \"%1 %2\" object, " + "which Kig does not currently support." ).arg( domelem.tagName() ).arg( + domelem.attribute( "type" ) ) ); + return false; + } +#ifdef DRGEO_DEBUG + kdDebug() << "+++++++++ oc:" << oc << endl; +#endif + } + else if ( domelem.tagName() == "script" ) + { + QString xs; + QString ys; + QString text; + for ( QDomNode c = domelem.firstChild(); ! c.isNull(); c = c.nextSibling() ) + { + QDomElement ce = c.toElement(); + if ( ce.isNull() ) continue; + else if ( ce.tagName() == "x" ) + xs = ce.text(); + else if ( ce.tagName() == "y" ) + ys = ce.text(); + else if ( ce.tagName() == "code" ) + text = ce.text(); + } + bool ok; + bool ok2; + double x = xs.toDouble( &ok ); + double y = ys.toDouble( &ok2 ); + if ( ! ( ok && ok2 ) ) + KIG_FILTER_PARSE_ERROR; + // since Kig doesn't support Guile scripts, it will write script's text + // in a label, so the user can freely see the code and make whatever + // he/she wants + // possible idea: construct a new script object with the parents of Guile + // one and the Guile code inserted commented... depends on a better + // handling of arguments in scripts? + if ( domelem.attribute( "type" ) == "nitems" ) + oc = fact->labelCalcer( text, Coordinate( x, y ), false, std::vector<ObjectCalcer*>(), *ret ); + else + { + notSupported( file, i18n( "This Dr. Geo file contains a \"%1 %2\" object, " + "which Kig does not currently support." ).arg( domelem.tagName() ).arg( + domelem.attribute( "type" ) ) ); + return false; + } + } + else if ( domelem.tagName() == "locus" ) + { + if ( domelem.attribute( "type" ) == "None" ) + oc = fact->locusCalcer( parents[0], parents[1] ); + else + { + notSupported( file, i18n( "This Dr. Geo file contains a \"%1 %2\" object, " + "which Kig does not currently support." ).arg( domelem.tagName() ).arg( + domelem.attribute( "type" ) ) ); + return false; + } +#ifdef DRGEO_DEBUG + kdDebug() << "+++++++++ oc:" << oc << endl; +#endif + } + else if ( ( domelem.tagName() == "boundingBox" ) || + ( domelem.tagName() == "customUI" ) ) + { + // ignoring these elements, since they are not useful to us... + nignored++; + } + else + { +#ifdef DRGEO_DEBUG + kdDebug() << ">>>>>>>>> UNKNOWN OBJECT" << endl; +#endif + notSupported( file, i18n( "This Dr. Geo file contains a \"%1 %2\" object, " + "which Kig does not currently support." ).arg( domelem.tagName() ).arg( + domelem.attribute( "type" ) ) ); + return false; + } + curid++; + if ( oc == 0 ) + continue; + +// reading color + QColor co( domelem.attribute( "color" ) ); + if ( ! co.isValid() ) + if ( domelem.attribute( "color" ) == "Bordeaux" ) + co.setRgb( 145, 0, 0 ); + else + co = Qt::blue; +// reading width and style +// Dashed -> the little one +// Normal -> the medium +// Thick -> the biggest one + int w = -1; + Qt::PenStyle s = Qt::SolidLine; + if ( domelem.tagName() == "point" ) + { + if ( domelem.attribute( "thickness" ) == "Normal" ) + w = 7; + else if ( domelem.attribute( "thickness" ) == "Thick" ) + w = 9; + } + else + { + if ( domelem.attribute( "thickness" ) == "Dashed" ) + s = Qt::DotLine; + if ( domelem.attribute( "thickness" ) == "Thick" ) + w = 2; + } + QString ps = domelem.attribute( "style" ); + int pointstyle = ObjectDrawer::pointStyleFromString( ps ); +// show this object? + bool show = ( ( domelem.attribute( "masked" ) != "True" ) && + ( domelem.attribute( "masked" ) != "Alway" ) ); +// costructing the ObjectDrawer* + ObjectDrawer* d = new ObjectDrawer( co, w, show, s, pointstyle ); +// reading object name + QString strname = domelem.attribute( "name" ); + ObjectConstCalcer* name = new ObjectConstCalcer( new StringImp( strname ) ); + +// creating the ObjectHolder* + ObjectHolder* o = new ObjectHolder( oc, d, name ); + holders.push_back( o ); +// calc() +#ifdef DRGEO_DEBUG + kdDebug() << ">>>>>>>>> calc" << endl; +#endif + holders[curid-1-nignored]->calc( *ret ); + + if ( domelem.tagName() == "point" ) + { + if ( !strname.isEmpty() ) + { + std::vector<ObjectCalcer*> args2; + args2.push_back( o->nameCalcer() ); + oc2 = fact->attachedLabelCalcer( QString::fromLatin1( "%1" ), oc, + static_cast<const PointImp*>( oc->imp() )->coordinate(), + false, args2, *ret ); + co = Qt::black; + } + } + else if ( domelem.tagName() == "angle" ) + { + oc2 = filtersConstructTextObject( + static_cast<const PointImp*>( holders[curid-1-nignored]->calcer()->parents()[1]->imp() )->coordinate(), + holders[curid-1-nignored]->calcer(), "angle-degrees", *ret, false ); + } + + oc = 0; + + if ( oc2 != 0 ) + { + oc2->calc( *ret ); + ObjectDrawer* d2 = new ObjectDrawer( co ); + ObjectHolder* o2 = new ObjectHolder( oc2, d2 ); + holders2.push_back( o2 ); + oc2 = 0; + } + } + + ret->addObjects( holders ); + ret->addObjects( holders2 ); + ret->setGrid( grid ); + ret->setAxes( grid ); + return ret; +} + +KigFilterDrgeo* KigFilterDrgeo::instance() +{ + static KigFilterDrgeo f; + return &f; +} diff --git a/kig/filters/drgeo-filter.h b/kig/filters/drgeo-filter.h new file mode 100644 index 00000000..7485fd5b --- /dev/null +++ b/kig/filters/drgeo-filter.h @@ -0,0 +1,46 @@ +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> +// Copyright (C) 2004 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_FILTERS_DRGEO_FILTER_H +#define KIG_FILTERS_DRGEO_FILTER_H + +#include "filter.h" + +class QDomNode; +class KigDocument; +class QString; + +/** + * This is an import filter for the GNOME geometry program DrGeo. + */ +class KigFilterDrgeo + : public KigFilter +{ +protected: + KigFilterDrgeo(); + ~KigFilterDrgeo(); +public: + static KigFilterDrgeo* instance(); + + bool supportMime( const QString& mime ); + KigDocument* load( const QString& file ); +private: + KigDocument* importFigure( QDomNode f, const QString& file, const bool grid ); +}; + +#endif diff --git a/kig/filters/exporter.cc b/kig/filters/exporter.cc new file mode 100644 index 00000000..c2ece44e --- /dev/null +++ b/kig/filters/exporter.cc @@ -0,0 +1,624 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "exporter.h" + +#include "imageexporteroptions.h" +#include "latexexporter.h" +#include "svgexporter.h" + +#include "../kig/kig_document.h" +#include "../kig/kig_part.h" +#include "../kig/kig_view.h" +#include "../misc/common.h" +#include "../misc/kigfiledialog.h" +#include "../misc/kigpainter.h" +#include "../objects/circle_imp.h" +#include "../objects/line_imp.h" +#include "../objects/object_drawer.h" +#include "../objects/object_holder.h" +#include "../objects/object_imp.h" +#include "../objects/other_imp.h" +#include "../objects/point_imp.h" +#include "../objects/text_imp.h" + +#include <qcheckbox.h> +#include <qcolor.h> +#include <qfile.h> +#include <qiconset.h> +#include <qtextstream.h> + +#include <kaction.h> +#include <kiconloader.h> +#include <kimageio.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <knuminput.h> + +#include <map> + +// we need this for storing colors in a std::map.. +static bool operator<( const QColor& a, const QColor& b ) +{ + return a.rgb() < b.rgb(); +} + +class ExporterAction + : public KAction +{ + KigExporter* mexp; + const KigPart* mdoc; + KigWidget* mw; +public: + ExporterAction( const KigPart* doc, KigWidget* w, + KActionCollection* parent, KigExporter* exp ); + void slotActivated(); +}; + +ExporterAction::ExporterAction( const KigPart* doc, KigWidget* w, + KActionCollection* parent, KigExporter* exp ) + : KAction( exp->menuEntryName(), KShortcut(), 0, 0, parent ), + mexp( exp ), mdoc( doc ), mw( w ) +{ + QString iconstr = exp->menuIcon(); + if ( iconstr.isEmpty() ) + return; + KIconLoader* l = doc->instance()->iconLoader(); + QPixmap icon = l->loadIcon( iconstr, KIcon::Small, 16, KIcon::DefaultState, 0L, true ); + if ( !icon.isNull() ) + setIconSet( QIconSet( icon ) ); +} + +void ExporterAction::slotActivated() +{ + mexp->run( *mdoc, *mw ); +} + +KigExporter::~KigExporter() +{ +} + +ImageExporter::~ImageExporter() +{ +} + +QString ImageExporter::exportToStatement() const +{ + return i18n( "&Export to image" ); +} + +QString ImageExporter::menuEntryName() const +{ + return i18n( "&Image..." ); +} + +QString ImageExporter::menuIcon() const +{ + return "image"; +} + +void ImageExporter::run( const KigPart& doc, KigWidget& w ) +{ + static bool kimageioRegistered = false; + if ( ! kimageioRegistered ) + { + KImageIO::registerFormats(); + kimageioRegistered = true; + } + + KigFileDialog* kfd = new KigFileDialog( + QString::null, KImageIO::pattern( KImageIO::Writing ), + i18n( "Export as Image" ), &w ); + kfd->setOptionCaption( i18n( "Image Options" ) ); + ImageExporterOptions* opts = new ImageExporterOptions( 0L, w.size() ); + kfd->setOptionsWidget( opts ); + opts->WidthInput->setValue( w.size().width() ); + opts->HeightInput->setValue( w.size().height() ); + opts->showGridCheckBox->setChecked( doc.document().grid() ); + opts->showAxesCheckBox->setChecked( doc.document().axes() ); + if ( !kfd->exec() ) + return; + + QString filename = kfd->selectedFile(); + bool showgrid = opts->showGridCheckBox->isOn(); + bool showaxes = opts->showAxesCheckBox->isOn(); + int width = opts->WidthInput->value(); + int height = opts->HeightInput->value(); + + delete opts; + delete kfd; + + QString type = KImageIO::type( filename ); + if ( type.isNull() ) + { + KMessageBox::sorry( &w, i18n( "Sorry, this file format is not supported." ) ); + return; + }; + + kdDebug() << k_funcinfo << type << endl; + + QFile file( filename ); + if ( ! file.open( IO_WriteOnly ) ) + { + KMessageBox::sorry( &w, + i18n( "The file \"%1\" could not be opened. Please check if the file permissions are set correctly." ) + .arg( filename ) ); + return; + }; + + QPixmap img( QSize( width, height ) ); + img.fill( Qt::white ); + KigPainter p( ScreenInfo( w.screenInfo().shownRect(), img.rect() ), &img, doc.document() ); + p.setWholeWinOverlay(); + p.drawGrid( doc.document().coordinateSystem(), showgrid, showaxes ); + // FIXME: show the selections ? + p.drawObjects( doc.document().objects(), false ); + if ( ! img.save( filename, type.latin1() ) ) + { + KMessageBox::error( &w, i18n( "Sorry, something went wrong while saving to image \"%1\"" ).arg( filename ) ); + } + +} + +KigExportManager::KigExportManager() +{ + mexporters.push_back( new ImageExporter ); + // working on this one ;) + mexporters.push_back( new XFigExporter ); + mexporters.push_back( new LatexExporter ); + mexporters.push_back( new SVGExporter ); +} + +KigExportManager::~KigExportManager() +{ + for ( uint i = 0; i < mexporters.size(); ++i ) + delete mexporters[i]; +} + +void KigExportManager::addMenuAction( const KigPart* doc, KigWidget* w, + KActionCollection* coll ) +{ + KActionMenu* m = + new KActionMenu( i18n( "&Export To" ), coll, "file_export" ); + for ( uint i = 0; i < mexporters.size(); ++i ) + m->insert( new ExporterAction( doc, w, coll, mexporters[i] ) ); +} + +KigExportManager* KigExportManager::instance() +{ + static KigExportManager m; + return &m; +} + +XFigExporter::~XFigExporter() +{ +} + +QString XFigExporter::exportToStatement() const +{ + return i18n( "Export to &XFig file" ); +} + + +QString XFigExporter::menuEntryName() const +{ + return i18n( "&XFig File..." ); +} + +QString XFigExporter::menuIcon() const +{ + return "kig_xfig"; +} + +class XFigExportImpVisitor + : public ObjectImpVisitor +{ + QTextStream& mstream; + ObjectHolder* mcurobj; + const KigWidget& mw; + Rect msr; + std::map<QColor, int> mcolormap; + int mnextcolorid; + int mcurcolorid; + + QPoint convertCoord( const Coordinate& c ) + { + Coordinate ret = ( c - msr.bottomLeft() ); + ret.y = msr.height() - ret.y; +// kdDebug() << "msr: " << msr << endl +// << "ret: " << ret << endl +// << "c: " << c << endl; + ret *= 9450; + ret /= msr.width(); + return ret.toQPoint(); + } + + void emitLine( const Coordinate& a, const Coordinate& b, int width, bool vector = false ); +public: + void visit( ObjectHolder* obj ); + void mapColor( const ObjectDrawer* obj ); + + XFigExportImpVisitor( QTextStream& s, const KigWidget& w ) + : mstream( s ), mw( w ), msr( mw.showingRect() ), + mnextcolorid( 32 ) + { + // predefined colors in XFig.. + mcolormap[Qt::black] = 0; + mcolormap[Qt::blue] = 1; + mcolormap[Qt::green] = 2; + mcolormap[Qt::cyan] = 3; + mcolormap[Qt::red] = 4; + mcolormap[Qt::magenta] = 5; + mcolormap[Qt::yellow] = 6; + mcolormap[Qt::white] = 7; + } + void visit( const LineImp* imp ); + void visit( const PointImp* imp ); + void visit( const TextImp* imp ); + void visit( const AngleImp* imp ); + void visit( const VectorImp* imp ); + void visit( const LocusImp* imp ); + void visit( const CircleImp* imp ); + void visit( const ConicImp* imp ); + void visit( const CubicImp* imp ); + void visit( const SegmentImp* imp ); + void visit( const RayImp* imp ); + void visit( const ArcImp* imp ); +}; + +void XFigExportImpVisitor::mapColor( const ObjectDrawer* obj ) +{ + if ( ! obj->shown() ) return; + QColor color = obj->color(); + if ( mcolormap.find( color ) == mcolormap.end() ) + { + int newcolorid = mnextcolorid++; + mstream << "0 " + << newcolorid << " " + << color.name() << "\n"; + mcolormap[color] = newcolorid; + } +} + +void XFigExportImpVisitor::visit( ObjectHolder* obj ) +{ + if ( ! obj->drawer()->shown() ) return; + assert( mcolormap.find( obj->drawer()->color() ) != mcolormap.end() ); + mcurcolorid = mcolormap[ obj->drawer()->color() ]; + mcurobj = obj; + obj->imp()->visit( this ); +} + +void XFigExportImpVisitor::visit( const LineImp* imp ) +{ + Coordinate a = imp->data().a; + Coordinate b = imp->data().b; + calcBorderPoints( a, b, msr ); + int width = mcurobj->drawer()->width(); + if ( width == -1 ) width = 1; + + if ( a != b ) + emitLine( a, b, width ); +} + +void XFigExportImpVisitor::emitLine( const Coordinate& a, const Coordinate& b, int width, bool vector ) +{ + mstream << "2 "; // polyline type; + mstream << "1 "; // polyline subtype; + mstream << "0 "; // line_style: Solid + mstream << width << " "; // thickness: *1/80 inch + mstream << mcurcolorid << " "; // pen_color: default + mstream << "7 "; // fill_color: white + mstream << "50 "; // depth: 50 + mstream << "-1 "; // pen_style: unused by XFig + mstream << "-1 "; // area_fill: no fill + mstream << "0.000 "; // style_val: the distance between dots and + // dashes in case of dotted or dashed lines.. + mstream << "0 "; // join_style: Miter + mstream << "0 "; // cap_style: Butt + mstream << "-1 "; // radius in case of an arc-box, but we're a + // polyline, so nothing here.. + if ( ! vector ) + mstream << "0 "; // forward arrow: no + else + mstream << "1 "; // forward arrow: yes + mstream << "0 "; // backward arrow: no + mstream << "2"; // a two points polyline.. + + mstream << "\n\t "; + + if ( vector ) + { + // first the arrow line in case of a vector.. + mstream << "1 " // arrow_type: closed triangle + << "1 " // arrow_style: filled with pen color.. + << "1.00 " // arrow_thickness: 1 + << "195.00 " // arrow_width + << "165.00 " // arrow_height + << "\n\t"; + } + QPoint ca = convertCoord( a ); + QPoint cb = convertCoord( b ); + + mstream << ca.x() << " " << ca.y() << " " << cb.x() << " " << cb.y() << "\n"; +} + +void XFigExportImpVisitor::visit( const PointImp* imp ) +{ + const QPoint center = convertCoord( imp->coordinate() ); + int width = mcurobj->drawer()->width(); + if ( width == -1 ) width = 5; + width *= 10; + + mstream << "1 " // Ellipse type + << "3 " // circle defined by radius subtype + << "0 "; // line_style: Solid + mstream << "1 " << " " // thickness: *1/80 inch + << mcurcolorid << " " // pen_color: default + << mcurcolorid << " " // fill_color: black + << "50 " // depth: 50 + << "-1 " // pen_style: unused by XFig + << "20 " // area_fill: full saturation of the fill color + << "0.000 " // style_val: the distance between dots and + // dashes in case of dotted or dashed lines.. + << "1 " // direction: always 1 + << "0.0000 " // angle: the radius of the x-axis: 0 + << center.x() << " " << center.y() << " " // the center.. + << width << " " << width << " " // radius_x and radius_y + << center.x() << " " // start_x and start_y, appear + << center.y() << " " // unused.. + << center.x() + width << " " // end_x and end_y, + << center.y() << "\n"; // appear unused too... +} + +void XFigExportImpVisitor::visit( const TextImp* imp ) +{ + QString text = imp->text(); + QPoint coord = convertCoord( imp->surroundingRect().bottomLeft() ); + + mstream << "4 " // text type + << "0 " // subtype: left justfied + << mcurcolorid << " " // color: black + << "50 " // depth: 50 + << "-1 " // pen style: unused + << "0 " // font: default + << "11 " // font-size: 11 + << "0 " // angle + << "0 " // font-flags: all the defaults.. + << "500 500 " // height, width: large enough.. + << coord.x() << " " // x, y + << coord.y() << " " + << text.ascii() << "\\001" // text, terminated by \001 + << "\n"; +} + +void XFigExportImpVisitor::visit( const AngleImp* ) +{ +} + +void XFigExportImpVisitor::visit( const VectorImp* imp ) +{ + int width = mcurobj->drawer()->width(); + if ( width == -1 ) width = 1; + emitLine( imp->a(), imp->b(), width, true ); +} + +void XFigExportImpVisitor::visit( const LocusImp* ) +{ + +} + +void XFigExportImpVisitor::visit( const CircleImp* imp ) +{ + const QPoint center = convertCoord( imp->center() ); + const int radius = + ( convertCoord( imp->center() + Coordinate( imp->radius(), 0 ) ) - center ).x(); + + mstream << "1 " // Ellipse type + << "3 " // circle defined by radius subtype + << "0 "; // line_style: Solid + int width = mcurobj->drawer()->width(); + if ( width == -1 ) width = 1; + mstream << width << " " // thickness: *1/80 inch + << mcurcolorid << " " // pen_color: default + << "7 " // fill_color: white + << "50 " // depth: 50 + << "-1 " // pen_style: unused by XFig + << "-1 " // area_fill: no fill + << "0.000 " // style_val: the distance between dots and + // dashes in case of dotted or dashed lines.. + << "1 " // direction: always 1 + << "0.0000 " // angle: the radius of the x-axis: 0 + << center.x() << " " << center.y() << " " // the center.. + << radius << " " << radius << " " // radius_x and radius_y + << center.x() << " " // start_x and start_y, appear + << center.y() << " " // unused.. + << center.x() + radius << " " // end_x and end_y, + << center.y() << "\n"; // appear unused too... +} + +void XFigExportImpVisitor::visit( const ConicImp* imp ) +{ + int width = mcurobj->drawer()->width(); + if ( width == -1 ) width = 1; + if ( imp->conicType() == 1 ) + { + // an ellipse, this is good, cause this allows us to export to a + // real ellipse type.. + const ConicPolarData data = imp->polarData(); + + // gather some necessary data.. + // the angle of the x axis.. + double anglex = atan2( data.esintheta0, data.ecostheta0 ); + // the exentricity + double e = hypot( data.esintheta0, data.ecostheta0 ); + // the x radius is easy to find.. + double radiusx = data.pdimen / ( 1 - e * e ); + // the y radius is a bit harder: we first find the distance from + // the focus point to the mid point of the two focuses, this is: + double d = -e * data.pdimen / ( 1 - e * e ); + // the distance from the first focus to the intersection of the + // second axis with the conic equals radiusx: + double r = radiusx; + double radiusy = sqrt( r*r - d*d ); + + Coordinate center = data.focus1 - Coordinate( cos( anglex ), sin( anglex ) ).normalize( d ); + const QPoint qcenter = convertCoord( center ); + const int radius_x = ( convertCoord( center + Coordinate( radiusx, 0 ) ) - + convertCoord( center ) ).x(); + const int radius_y = ( convertCoord( center + Coordinate( radiusy, 0 ) ) - + convertCoord( center ) ).x(); + const QPoint qpoint2 = convertCoord( center + Coordinate( -sin( anglex ), cos( anglex ) ) * radiusy ); + + mstream << "1 " // ellipse type + << "1 " // subtype: ellipse defined by readii + << "0 " // line_style: Solid + << width << " " // thickness + << mcurcolorid << " " // pen_color: black + << "7 " // fill_color: white + << "50 " // depth: 50 + << "-1 " // pan_style: not used + << "-1 " // area_fill: no fill + << "0.000 " // style_val: not used + << "1 " // direction: always 1 + << anglex << " " // angle of the main axis + << qcenter.x() << " " // center + << qcenter.y() << " " + << radius_x << " " // radiuses + << radius_y << " " + << qcenter.x() << " " // start point + << qcenter.y() << " " + << qpoint2.x() << " " // end point + << qpoint2.y() << " "; + } + else return; +} + +void XFigExportImpVisitor::visit( const CubicImp* ) +{ +} + +void XFigExportImpVisitor::visit( const SegmentImp* imp ) +{ + Coordinate a = imp->data().a; + Coordinate b = imp->data().b; + int width = mcurobj->drawer()->width(); + if ( width == -1 ) width = 1; + + emitLine( a, b, width ); +} + +void XFigExportImpVisitor::visit( const RayImp* imp ) +{ + Coordinate a = imp->data().a; + Coordinate b = imp->data().b; + calcRayBorderPoints( a, b, msr ); + + int width = mcurobj->drawer()->width(); + if ( width == -1 ) width = 1; + + emitLine( a, b, width ); +} + +void XFigExportImpVisitor::visit( const ArcImp* imp ) +{ + const Coordinate center = imp->center(); + const double radius = imp->radius(); + const double startangle = imp->startAngle(); + const double endangle = startangle + imp->angle(); + const double middleangle = ( startangle + endangle ) / 2; + const Coordinate ad = Coordinate( cos( startangle ), sin( startangle ) ).normalize( radius ); + const Coordinate bd = Coordinate( cos( middleangle ), sin( middleangle ) ).normalize( radius ); + const Coordinate cd = Coordinate( cos( endangle ), sin( endangle ) ).normalize( radius ); + const QPoint a = convertCoord( center + ad ); + const QPoint b = convertCoord( center + bd ); + const QPoint c = convertCoord( center + cd ); + const QPoint cent = convertCoord( center ); + + mstream << "5 " // Ellipse type + << "1 " // subtype: open ended arc + << "0 "; // line_style: Solid + int width = mcurobj->drawer()->width(); + if ( width == -1 ) width = 1; + mstream << width << " " // thickness: *1/80 inch + << mcurcolorid << " " // pen_color: default + << "7 " // fill_color: white + << "50 " // depth: 50 + << "-1 " // pen_style: unused by XFig + << "-1 " // area_fill: no fill + << "0.000 " // style_val: the distance between dots and + // dashes in case of dotted or dashed lines.. + << "0 "; // cap_style: Butt.. + // 0 is clockwise, 1 is counterclockwise . + int direction = imp->angle() > 0 ? 1 : 0; + // direction next + mstream << direction << " " // direction.. + << "0 " // forward_arrow: no + << "0 " // backward_arrow: no + << cent.x() << " " << cent.y() << " " // the center.. + << a.x() << " " << a.y() << " " // x1, y1 + << b.x() << " " << b.y() << " " // x2, y2 + << c.x() << " " << c.y() << " " // x3, y3 + << "\n"; +} + +void XFigExporter::run( const KigPart& doc, KigWidget& w ) +{ + KigFileDialog* kfd = new KigFileDialog( + ":document", i18n( "*.fig|XFig Documents (*.fig)" ), + i18n( "Export as XFig File" ), &w ); + if ( !kfd->exec() ) + return; + + QString file_name = kfd->selectedFile(); + + delete kfd; + + QFile file( file_name ); + if ( ! file.open( IO_WriteOnly ) ) + { + KMessageBox::sorry( &w, i18n( "The file \"%1\" could not be opened. Please " + "check if the file permissions are set correctly." ) + .arg( file_name ) ); + return; + }; + QTextStream stream( &file ); + stream << "#FIG 3.2\n"; + stream << "Landscape\n"; + stream << "Center\n"; + stream << "Metric\n"; + stream << "A4\n"; + stream << "100.00\n"; + stream << "Single\n"; + stream << "-2\n"; + stream << "1200 2\n"; + + std::vector<ObjectHolder*> os = doc.document().objects(); + XFigExportImpVisitor visitor( stream, w ); + + for ( std::vector<ObjectHolder*>::const_iterator i = os.begin(); + i != os.end(); ++i ) + { + visitor.mapColor( ( *i )->drawer() ); + }; + + for ( std::vector<ObjectHolder*>::const_iterator i = os.begin(); + i != os.end(); ++i ) + { + visitor.visit( *i ); + }; +} diff --git a/kig/filters/exporter.h b/kig/filters/exporter.h new file mode 100644 index 00000000..4fc74453 --- /dev/null +++ b/kig/filters/exporter.h @@ -0,0 +1,102 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_FILTERS_EXPORTER_H +#define KIG_FILTERS_EXPORTER_H + +#include <vector> + +class QString; +class KigDocument; +class KigPart; +class KigWidget; +class KActionCollection; + +class KigExporter; + +class KigExportManager +{ + std::vector<KigExporter*> mexporters; + KigExportManager(); + ~KigExportManager(); +public: + static KigExportManager* instance(); + void addMenuAction( const KigPart* doc, KigWidget* w, + KActionCollection* coll ); +}; + +/** + * Base class for a Kig exporter. + * + * Subclass it and implement its methods to have it working. + */ +class KigExporter +{ +public: + virtual ~KigExporter(); + + /** + * Returns a statement like i18n( "Export to image" ) + */ + virtual QString exportToStatement() const = 0; + /** + * Returns a string like i18n( "Image..." ) + */ + virtual QString menuEntryName() const = 0; + /** + * Returns a string with the name of the icon + */ + virtual QString menuIcon() const = 0; + + /** + * Do what you need to do. It's up to the individual exporters to + * ask the user for which file to export to etc., because they can + * do a much better job at that.. + */ + virtual void run( const KigPart& doc, KigWidget& w ) = 0; +}; + +/** + * This exporter takes care of the "Export to Image" stuff.. + */ +class ImageExporter + : public KigExporter +{ +public: + ~ImageExporter(); + QString exportToStatement() const; + QString menuEntryName() const; + QString menuIcon() const; + void run( const KigPart& doc, KigWidget& w ); +}; + +/** + * Guess what this one does ;) + * It exports to the XFig file format, as documented in the file + * FORMAT3.2 in the XFig source distribution. + */ +class XFigExporter + : public KigExporter +{ +public: + ~XFigExporter(); + QString exportToStatement() const; + QString menuEntryName() const; + QString menuIcon() const; + void run( const KigPart& doc, KigWidget& w ); +}; +#endif diff --git a/kig/filters/filter.cc b/kig/filters/filter.cc new file mode 100644 index 00000000..70290e8a --- /dev/null +++ b/kig/filters/filter.cc @@ -0,0 +1,114 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#include "filter.h" + +#include "kgeo-filter.h" +#include "cabri-filter.h" +#include "native-filter.h" +#include "kseg-filter.h" +#include "drgeo-filter.h" + +#include <kmessagebox.h> +#include <klocale.h> + +KigFilters* KigFilters::sThis; + +KigFilter* KigFilters::find(const QString& mime) +{ + for (vect::iterator i = mFilters.begin(); i != mFilters.end(); ++i) + { + if ((*i)->supportMime(mime)) return *i; + }; + return 0; +} + +KigFilters::KigFilters() +{ + mFilters.push_back( KigFilterKGeo::instance() ); + mFilters.push_back( KigFilterKSeg::instance() ); + mFilters.push_back( KigFilterCabri::instance() ); + mFilters.push_back( KigFilterNative::instance() ); + mFilters.push_back( KigFilterDrgeo::instance() ); +} + +KigFilters* KigFilters::instance() +{ + return sThis ? sThis : ( sThis = new KigFilters() ); +} + +KigFilter::KigFilter() +{ +} + +KigFilter::~KigFilter() +{ +} + +bool KigFilter::supportMime( const QString& ) +{ + return false; +} + +void KigFilter::fileNotFound( const QString& file ) const +{ + KMessageBox::sorry( 0, + i18n( "The file \"%1\" could not be opened. " + "This probably means that it does not " + "exist, or that it cannot be opened due to " + "its permissions" ).arg( file ) ); +} + +void KigFilter::parseError( const QString& file, const QString& explanation ) const +{ + const QString text = + i18n( "An error was encountered while parsing the file \"%1\". It " + "cannot be opened." ).arg( file ); + const QString title = i18n( "Parse Error" ); + + if ( explanation.isNull() ) + KMessageBox::sorry( 0, text, title ); + else + KMessageBox::detailedSorry( 0, text, explanation, title ); +} + +void KigFilter::notSupported( const QString& file, const QString& explanation ) const +{ + KMessageBox::detailedSorry( 0, + i18n( "Kig cannot open the file \"%1\"." ).arg( file ), + explanation, i18n( "Not Supported" ) ); +} + +void KigFilter::warning( const QString& explanation ) const +{ + KMessageBox::information( 0, explanation ); +} + +bool KigFilters::save( const KigDocument& data, const QString& tofile ) +{ + return KigFilterNative::instance()->save( data, tofile ); +} + +/* +bool KigFilters::save( const KigDocument& data, QTextStream& stream ) +{ + return KigFilterNative::instance()->save( data, stream ); +} +*/ diff --git a/kig/filters/filter.h b/kig/filters/filter.h new file mode 100644 index 00000000..f5d8b0f9 --- /dev/null +++ b/kig/filters/filter.h @@ -0,0 +1,96 @@ +// This file is part of Kig, a KDE program for Interactive Geometry... +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef FILTER_H +#define FILTER_H + +#include <qstring.h> + +#include <vector> + +class KigFilter; +class ScreenInfo; +class KigDocument; + +/** + * This singleton class handles all the input filters. + */ +class KigFilters +{ +public: + static KigFilters* instance(); + KigFilter* find (const QString& mime); + +// bool save ( const KigDocument& data, QTextStream& stream ); + /** + * saving is always done with the native filter. We don't support + * output filters.. + */ + bool save ( const KigDocument& data, const QString& outfile ); +protected: + KigFilters(); + static KigFilters* sThis; + typedef std::vector<KigFilter*> vect; + vect mFilters; +}; + +// KigFilter::load functions should use this macro to conveniently +// return a very useful parse error in a filter's load function.. +#define KIG_FILTER_PARSE_ERROR \ + { \ + QString locs = i18n( "An error was encountered at " \ + "line %1 in file %2." ) \ + .arg( __LINE__ ).arg( __FILE__ ); \ + parseError( file, locs ); \ + return 0; \ + } + +/** + * This is the base class for an input filter. + * + * All the needed work to add a new input filter to Kig is + * subclassing this class and implement supportMime() and load(). + */ +class KigFilter +{ +protected: + // shows errors to the user.. + void fileNotFound( const QString& file ) const; + void parseError( const QString& file, const QString& explanation = QString::null ) const; + void notSupported( const QString& file, const QString& explanation ) const; + void warning( const QString& explanation ) const; +public: + KigFilter(); + virtual ~KigFilter(); + + /** + * can the filter handle the mimetype \p mime ? + */ + virtual bool supportMime ( const QString& mime ); + + /** + * load file \p fromfile and build a KigDocument from it.. If this + * function returns 0, that means that an error occurred while + * loading ( implementations of this function are responsible for + * showing an error message themselves, using the above error + * functions ). If this functions returns non-0, the caller owns + * the returned KigDocument ( that was allocated with "new" ). + */ + virtual KigDocument* load ( const QString& fromfile ) = 0; +}; +#endif diff --git a/kig/filters/filters-common.cc b/kig/filters/filters-common.cc new file mode 100644 index 00000000..d25c2e14 --- /dev/null +++ b/kig/filters/filters-common.cc @@ -0,0 +1,39 @@ +// Copyright (C) 2004 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "filters-common.h" + +#include <vector> + +#include <qcstring.h> +#include <qstring.h> + +#include "../objects/object_calcer.h" +#include "../objects/object_factory.h" + +ObjectTypeCalcer* filtersConstructTextObject( + const Coordinate& c, ObjectCalcer* o, + const QCString& arg, const KigDocument& doc, bool needframe ) +{ + const ObjectFactory* fact = ObjectFactory::instance(); + ObjectCalcer* propo = fact->propertyObjectCalcer( o, arg ); + propo->calc( doc ); + std::vector<ObjectCalcer*> args; + args.push_back( propo ); + return fact->labelCalcer( QString::fromLatin1( "%1" ), c, needframe, + args, doc ); +} diff --git a/kig/filters/filters-common.h b/kig/filters/filters-common.h new file mode 100644 index 00000000..c46facc4 --- /dev/null +++ b/kig/filters/filters-common.h @@ -0,0 +1,30 @@ +// Copyright (C) 2004 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +class ObjectTypeCalcer; +class Coordinate; +class ObjectCalcer; +class QCString; +class KigDocument; + +/** + * constructs a text object with text "%1", location \p c, and variable + * parts given by the argument \p arg of obj \p o. + */ +ObjectTypeCalcer* filtersConstructTextObject( + const Coordinate& c, ObjectCalcer* o, + const QCString& arg, const KigDocument& doc, bool needframe ); diff --git a/kig/filters/imageexporteroptions.cc b/kig/filters/imageexporteroptions.cc new file mode 100644 index 00000000..cb996926 --- /dev/null +++ b/kig/filters/imageexporteroptions.cc @@ -0,0 +1,57 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "imageexporteroptions.h" +#include "imageexporteroptions.moc" + +#include <qcheckbox.h> +#include <qsize.h> + +#include <knuminput.h> + +ImageExporterOptions::ImageExporterOptions( QWidget* parent, const QSize& s ) + : ImageExporterOptionsBase( parent, "imageexporteroptions" ), msize( s ), + minternallysettingstuff( false ) +{ + keepAspectRatio->setChecked( true ); + connect( WidthInput, SIGNAL( valueChanged( int ) ), this, SLOT( slotWidthChanged( int ) ) ); + connect( HeightInput, SIGNAL( valueChanged( int ) ), this, SLOT( slotHeightChanged( int ) ) ); +} + +ImageExporterOptions::~ImageExporterOptions() +{ +} + +void ImageExporterOptions::slotWidthChanged( int w ) +{ + if ( ! minternallysettingstuff && keepAspectRatio->isOn() ) + { + minternallysettingstuff = true; + HeightInput->setValue( w * msize.height() / msize.width() ); + minternallysettingstuff = false; + }; +} + +void ImageExporterOptions::slotHeightChanged( int h ) +{ + if ( ! minternallysettingstuff && keepAspectRatio->isOn() ) + { + minternallysettingstuff = true; + WidthInput->setValue( h * msize.width() / msize.height() ); + minternallysettingstuff = false; + }; +} diff --git a/kig/filters/imageexporteroptions.h b/kig/filters/imageexporteroptions.h new file mode 100644 index 00000000..1ef6a855 --- /dev/null +++ b/kig/filters/imageexporteroptions.h @@ -0,0 +1,45 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_FILTERS_IMAGEEXPORTEROPTIONS_H +#define KIG_FILTERS_IMAGEEXPORTEROPTIONS_H + +#include "imageexporteroptionsbase.h" + +class QSize; + +class ImageExporterOptions + : public ImageExporterOptionsBase +{ + Q_OBJECT + + QSize msize; + + // this is set by slotWidthChanged() when they set the other input + // widget's value, to avoid reacting to internal changes to the + // value like to user changes... + bool minternallysettingstuff; +public: + ImageExporterOptions( QWidget* parent, const QSize& s ); + ~ImageExporterOptions(); + +protected slots: + void slotWidthChanged( int ); + void slotHeightChanged( int ); +}; + +#endif diff --git a/kig/filters/imageexporteroptionsbase.ui b/kig/filters/imageexporteroptionsbase.ui new file mode 100644 index 00000000..03145dec --- /dev/null +++ b/kig/filters/imageexporteroptionsbase.ui @@ -0,0 +1,152 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>ImageExporterOptionsBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>ImageExporterOptionsBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>250</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="title"> + <string>Resolution</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout2_2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>WidthLabel_2</cstring> + </property> + <property name="text"> + <string>Width:</string> + </property> + </widget> + <widget class="KIntNumInput"> + <property name="name"> + <cstring>WidthInput</cstring> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="suffix"> + <string> pixels</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout3_2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>HeightLabel_2</cstring> + </property> + <property name="text"> + <string>Height:</string> + </property> + </widget> + <widget class="KIntNumInput"> + <property name="name"> + <cstring>HeightInput</cstring> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="suffix"> + <string> pixels</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>keepAspectRatio</cstring> + </property> + <property name="text"> + <string>&Keep aspect ratio</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>Options</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>showGridCheckBox</cstring> + </property> + <property name="text"> + <string>Show grid</string> + </property> + </widget> + <widget class="QCheckBox" row="0" column="1"> + <property name="name"> + <cstring>showAxesCheckBox</cstring> + </property> + <property name="text"> + <string>Show axes</string> + </property> + </widget> + </grid> + </widget> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> +</includehints> +</UI> diff --git a/kig/filters/kgeo-filter.cc b/kig/filters/kgeo-filter.cc new file mode 100644 index 00000000..da26457d --- /dev/null +++ b/kig/filters/kgeo-filter.cc @@ -0,0 +1,374 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#include "kgeo-filter.h" + +#include "kgeo-resource.h" +#include "filters-common.h" + +#include "../kig/kig_part.h" +#include "../kig/kig_document.h" +#include "../objects/angle_type.h" +#include "../objects/bogus_imp.h" +#include "../objects/circle_imp.h" +#include "../objects/circle_type.h" +#include "../objects/intersection_types.h" +#include "../objects/line_type.h" +#include "../objects/object_calcer.h" +#include "../objects/object_drawer.h" +#include "../objects/object_factory.h" +#include "../objects/object_holder.h" +#include "../objects/other_type.h" +#include "../objects/point_imp.h" +#include "../objects/point_type.h" +#include "../objects/text_type.h" +#include "../objects/transform_types.h" +#include "../objects/vector_type.h" + +#include <ksimpleconfig.h> + +#include <algorithm> +#include <functional> + +bool KigFilterKGeo::supportMime( const QString& mime ) +{ + return mime == "application/x-kgeo"; +} + +KigDocument* KigFilterKGeo::load( const QString& sFrom ) +{ + // kgeo uses a KSimpleConfig to save its contents... + KSimpleConfig config ( sFrom ); + + loadMetrics ( &config ); + return loadObjects ( sFrom, &config ); +} + +void KigFilterKGeo::loadMetrics(KSimpleConfig* c ) +{ + c->setGroup("Main"); + xMax = c->readNumEntry("XMax", 16); + yMax = c->readNumEntry("YMax", 11); + grid = c->readBoolEntry( "Grid", true ); + axes = c->readBoolEntry( "Axes", true ); + // the rest is not relevant to us (yet ?)... +} + +struct KGeoHierarchyElement +{ + int id; + std::vector<int> parents; +}; + +static void visitElem( std::vector<KGeoHierarchyElement>& ret, + const std::vector<KGeoHierarchyElement>& elems, + std::vector<bool>& seen, + int i ) +{ + if ( !seen[i] ) + { + for ( uint j = 0; j < elems[i].parents.size(); ++j ) + visitElem( ret, elems, seen, elems[i].parents[j] ); + ret.push_back( elems[i] ); + seen[i] = true; + }; +} + +static std::vector<KGeoHierarchyElement> sortElems( const std::vector<KGeoHierarchyElement> elems ) +{ + std::vector<KGeoHierarchyElement> ret; + std::vector<bool> seenElems( elems.size(), false ); + for ( uint i = 0; i < elems.size(); ++i ) + visitElem( ret, elems, seenElems, i ); + return ret; +} + +KigDocument* KigFilterKGeo::loadObjects( const QString& file, KSimpleConfig* c ) +{ + KigDocument* ret = new KigDocument(); + + using namespace std; + + QString group; + bool ok = true; + c->setGroup("Main"); + int number = c->readNumEntry ("Number"); + + // first we determine the parent relationships, and sort the + // elements in an order that we can be sure all of an object's + // parents will have been handled before it is handled itself.. + // ( aka topological sort of the parent relations graph.. + std::vector<KGeoHierarchyElement> elems; + elems.reserve( number ); + + for ( int i = 0; i < number; ++i ) + { + KGeoHierarchyElement elem; + elem.id = i; + group.setNum( i + 1 ); + group.prepend( "Object " ); + c->setGroup( group ); + QStrList parents; + c->readListEntry( "Parents", parents ); + elems.push_back( elem ); + for ( const char* parent = parents.first(); parent; parent = parents.next() ) + { + int parentIndex = QString::fromLatin1( parent ).toInt( &ok ); + if ( ! ok ) KIG_FILTER_PARSE_ERROR; + if ( parentIndex != 0 ) + elems[i].parents.push_back( parentIndex - 1 ); + }; + }; + + std::vector<KGeoHierarchyElement> sortedElems = sortElems( elems ); + std::vector<ObjectHolder*> os; + os.resize( number, 0 ); + const ObjectFactory* factory = ObjectFactory::instance(); + + // now we iterate over the elems again in the newly determined + // order.. + for ( uint i = 0; i < sortedElems.size(); ++i ) + { + const KGeoHierarchyElement& e = sortedElems[i]; + int id = e.id; + group.setNum( id + 1 ); + group.prepend( "Object " ); + c->setGroup( group ); + int objID = c->readNumEntry( "Geo" ); + + std::vector<ObjectCalcer*> parents; + for ( uint j = 0; j < e.parents.size(); ++j ) + { + int parentid = e.parents[j]; + parents.push_back( os[parentid]->calcer() ); + }; + + ObjectCalcer* o = 0; + switch (objID) + { + case ID_point: + { + // fetch the coordinates... + QString strX = c->readEntry("QPointX"); + QString strY = c->readEntry("QPointY"); + double x = strX.toDouble(&ok); + if (!ok) KIG_FILTER_PARSE_ERROR; + double y = strY.toDouble(&ok); + if (!ok) KIG_FILTER_PARSE_ERROR; + Coordinate m( x, y ); + uint nparents = parents.size(); + if ( nparents == 0 ) + o = factory->fixedPointCalcer( m ); + else if ( nparents == 1 ) + o = factory->constrainedPointCalcer( parents[0], m, *ret ); + else + KIG_FILTER_PARSE_ERROR; + break; + } + case ID_segment: + { + o = new ObjectTypeCalcer( SegmentABType::instance(), parents ); + break; + } + case ID_circle: + { + o = new ObjectTypeCalcer( CircleBCPType::instance(), parents ); + break; + } + case ID_line: + { + o = new ObjectTypeCalcer( LineABType::instance(), parents ); + break; + } + case ID_bisection: + { + // if this is the bisection of two points, first build a segment + // between them.. + if ( parents.size() == 2 ) + { + ObjectTypeCalcer* seg = new ObjectTypeCalcer( SegmentABType::instance(), parents ); + parents.clear(); + parents.push_back( seg ); + } + if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR; + o = factory->propertyObjectCalcer( parents[0], "mid-point" ); + break; + }; + case ID_perpendicular: + { + o = new ObjectTypeCalcer( LinePerpendLPType::instance(), parents ); + break; + } + case ID_parallel: + { + o = new ObjectTypeCalcer( LineParallelLPType::instance(), parents ); + break; + } + case ID_vector: + { + o = new ObjectTypeCalcer( VectorType::instance(), parents ); + break; + } + case ID_ray: + { + o = new ObjectTypeCalcer( RayABType::instance(), parents ); + break; + } + case ID_move: + { + o = new ObjectTypeCalcer( TranslatedType::instance(), parents ); + break; + } + case ID_mirrorPoint: + { + o = new ObjectTypeCalcer( PointReflectionType::instance(), parents ); + break; + } + case ID_pointOfConc: + { + o = new ObjectTypeCalcer( LineLineIntersectionType::instance(), parents ); + break; + } + case ID_text: + { + bool frame = c->readBoolEntry( "Frame" ); + double x = c->readDoubleNumEntry( "TextRectCenterX" ); + double y = c->readDoubleNumEntry( "TextRectCenterY" ); + QString text = c->readEntry( "TextRectEntry" ); + double height = c->readNumEntry( "TextRectHeight" ); + double width = c->readNumEntry( "TextRectWidth" ); + // we don't want the center, but the top left.. + x -= width / 80; + y -= height / 80; + o = factory->labelCalcer( + text, Coordinate( x, y ), frame, std::vector<ObjectCalcer*>(), *ret ); + break; + } + case ID_fixedCircle: + { + double r = c->readDoubleNumEntry( "Radius" ); + parents.push_back( new ObjectConstCalcer( new DoubleImp( r ) ) ); + o = new ObjectTypeCalcer( CircleBPRType::instance(), parents ); + break; + } + case ID_angle: + { + if ( parents.size() == 3 ) + { + ObjectTypeCalcer* ao = new ObjectTypeCalcer( AngleType::instance(), parents ); + ao->calc( *ret ); + parents.clear(); + parents.push_back( ao ); + }; + if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR; + ObjectCalcer* angle = parents[0]; + parents.clear(); + const Coordinate c = + static_cast<const PointImp*>( angle->parents()[1]->imp() )->coordinate(); + o = filtersConstructTextObject( c, angle, "angle-degrees", *ret, true ); + break; + } + case ID_distance: + { + if ( parents.size() != 2 ) KIG_FILTER_PARSE_ERROR; + ObjectTypeCalcer* segment = new ObjectTypeCalcer( SegmentABType::instance(), parents ); + segment->calc( *ret ); + Coordinate m = ( static_cast<const PointImp*>( parents[0]->imp() )->coordinate() + + static_cast<const PointImp*>( parents[1]->imp() )->coordinate() ) / 2; + o = filtersConstructTextObject( m, segment, "length", *ret, true ); + break; + } + case ID_arc: + { + o = new ObjectTypeCalcer( AngleType::instance(), parents ); + break; + } + case ID_area: + { + if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR; + const CircleImp* circle = static_cast<const CircleImp*>( parents[0]->imp() ); + const Coordinate c = circle->center() + Coordinate( circle->radius(), 0 ); + o = filtersConstructTextObject( c, parents[0], "surface", *ret, true ); + break; + } + case ID_slope: + { + // if parents contains a segment, line, vector or whatever, we + // take its parents cause we want points.. + if ( parents.size() == 1 ) parents = parents[0]->parents(); + if ( parents.size() != 2 ) KIG_FILTER_PARSE_ERROR; + const Coordinate c = ( + static_cast<const PointImp*>( parents[0]->imp() )->coordinate() + + static_cast<const PointImp*>( parents[1]->imp() )->coordinate() ) / 2; + ObjectTypeCalcer* line = new ObjectTypeCalcer( LineABType::instance(), parents ); + line->calc( *ret ); + o = filtersConstructTextObject( c, line, "slope", *ret, true ); + break; + } + case ID_circumference: + { + if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR; + const CircleImp* c = static_cast<const CircleImp*>( parents[0]->imp() ); + const Coordinate m = c->center() + Coordinate( c->radius(), 0 ); + o = filtersConstructTextObject( m, parents[0], "circumference", *ret, true ); + break; + } + case ID_rotation: + { + // in kig, the rotated object should be last.. + ObjectCalcer* t = parents[2]; + parents[2] = parents[0]; + parents[0] = t; + o = new ObjectTypeCalcer( RotationType::instance(), parents ); + break; + } + default: + KIG_FILTER_PARSE_ERROR; + }; + + // set the color... + QColor co = c->readColorEntry( "Color" ); + if( !co.isValid() ) + co = Qt::blue; + ObjectDrawer* d = new ObjectDrawer( co ); + + os[i] = new ObjectHolder( o, d ); + os[i]->calc( *ret ); + }; // for loop (creating KGeoHierarchyElements.. + + ret->addObjects( os ); + ret->setGrid( grid ); + ret->setAxes( axes ); + return ret; +} + +KigFilterKGeo::KigFilterKGeo() +{ +} + +KigFilterKGeo::~KigFilterKGeo() +{ +} + +KigFilterKGeo* KigFilterKGeo::instance() +{ + static KigFilterKGeo f; + return &f; +} diff --git a/kig/filters/kgeo-filter.h b/kig/filters/kgeo-filter.h new file mode 100644 index 00000000..1a2ff83d --- /dev/null +++ b/kig/filters/kgeo-filter.h @@ -0,0 +1,55 @@ +// This file is part of Kig, a KDE program for Interactive Geometry... +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_FILTERS_KGEO_FILTER_H +#define KIG_FILTERS_KGEO_FILTER_H + +#include "filter.h" + +class KSimpleConfig; + +/** + * This is an import filter for files generated by the program KGeo, + * which was an interactive geometry program in kdeedu. Kig is + * supposed to be its successor, and this import filter is part of my + * attempt to achieve that :) + * + * Status: a significant part of KGeo's format is supported, not all + * yet, though.. + */ +class KigFilterKGeo + : public KigFilter +{ +public: + static KigFilterKGeo* instance(); + bool supportMime ( const QString& mime ); + KigDocument* load ( const QString& from ); +protected: + KigFilterKGeo(); + ~KigFilterKGeo(); + + void loadMetrics ( KSimpleConfig* ); + KigDocument* loadObjects ( const QString& file, KSimpleConfig* ); + + int xMax; + int yMax; + bool grid; + bool axes; +}; + +#endif diff --git a/kig/filters/kgeo-resource.h b/kig/filters/kgeo-resource.h new file mode 100644 index 00000000..2a272ed0 --- /dev/null +++ b/kig/filters/kgeo-resource.h @@ -0,0 +1,204 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + + +/// note: this code comes from KGeo by Marc Bartsch.. + + +/*************************************************************************** + resource.h - description + ------------------- + begin : Die Okt 3 09:00:59 CEST 2000 + copyright : (C) 2000 by Marc Bartsch + email : marc.bartsch@topmail.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef RESOURCE_H +#define RESOURCE_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <iostream> +#include <qstring.h> +#include <klocale.h> + +/////////////////////////////////////////////////////////////////// +// resource.h -- contains macros used for commands + + +/////////////////////////////////////////////////////////////////// +// COMMAND VALUES FOR MENUBAR AND TOOLBAR ENTRIES + + +/////////////////////////////////////////////////////////////////// +// File-menu entries +#define ID_FILE_NEW_WINDOW 10010 +#define ID_FILE_NEW 10020 +#define ID_FILE_OPEN 10030 +#define ID_FILE_OPEN_RECENT 10040 +#define ID_FILE_CLOSE 10050 + +#define ID_FILE_SAVE 10060 +#define ID_FILE_SAVE_AS 10070 + +#define ID_FILE_PRINT 10080 + +#define ID_FILE_QUIT 10090 + +/////////////////////////////////////////////////////////////////// +// Edit-menu entries +#define ID_EDIT_COPY 11010 +#define ID_EDIT_CUT 11020 +#define ID_EDIT_PASTE 11030 +// domi: disabled, breaks --enable-final, and is not used anyway. +//#define ID_EDIT_PREFERENCES 11040 +#define ID_EDIT_FULLSCREEN 11050 + +/////////////////////////////////////////////////////////////////// +// View-menu entries +#define ID_VIEW_TOOLBAR 12010 +#define ID_VIEW_STATUSBAR 12020 +#define ID_VIEW_FULLSCREEN 12030 + +/////////////////////////////////////////////////////////////////// +// Help-menu entries +#define ID_HELP_CONTENTS 1002 + +/////////////////////////////////////////////////////////////////// +// General application values +#define ID_STATUS_MSG 1001 + +#define IDS_STATUS_DEFAULT "Ready." + +#define ID_infinite -1 + +#define ID_point 1 +#define ID_pointxy 14 +#define ID_pointOnLine 15 +#define ID_pointOfConc 7 +#define ID_bisection 5 +#define ID_mirrorPoint 9 + +#define ID_segment 2 +#define ID_circle 3 +#define ID_line 4 +#define ID_fixedCircle 6 +#define ID_arc 8 +#define ID_eraser 10 +#define ID_attacher 11 +#define ID_tracer 12 +#define ID_triangle 13 +#define ID_colorizer 16 +#define ID_thicker 17 +#define ID_geoPoint 18 +#define ID_geoTool 19 +#define ID_geoObject 20 +#define ID_geoMeasure 21 +#define ID_distance 22 +#define ID_angle 23 +#define ID_area 24 +#define ID_slope 25 +#define ID_circumference 26 +#define ID_vector 27 +#define ID_geoLine 28 +#define ID_ray 29 +#define ID_parallel 30 +#define ID_perpendicular 31 +#define ID_move 32 +#define ID_rotation 33 +#define ID_text 34 + +#define ID_buttonFileNew 100 +#define ID_buttonKiosk 101 + +#define ID_buttonPoint 110 +#define ID_buttonPointxy 111 +#define ID_buttonPointOnLine 112 +#define ID_buttonPointOfConc 113 +#define ID_buttonBisection 114 +#define ID_buttonMirrorPoint 115 +#define ID_buttonMove 116 +#define ID_buttonRotation 117 + +#define ID_buttonSegment 120 +#define ID_buttonLine 121 +#define ID_buttonVector 122 +#define ID_buttonRay 123 +#define ID_buttonParallel 124 +#define ID_buttonPerpendicular 125 +#define ID_buttonTriangle 126 + +#define ID_buttonBaseCircle 130 +#define ID_buttonCircle 131 +#define ID_buttonArc 132 + +#define ID_buttonDistance 140 +#define ID_buttonAngle 141 +#define ID_buttonArea 142 +#define ID_buttonSlope 143 +#define ID_buttonCircumference 144 + +#define ID_buttonBlack 150 +#define ID_buttonDarkGray 151 +#define ID_buttonLightGray 152 +#define ID_buttonWhite 153 +#define ID_buttonBlue 154 +#define ID_buttonRed 155 +#define ID_buttonGreen 156 + +#define ID_buttonThinLine 160 +#define ID_buttonMiddleLine 161 +#define ID_buttonThickLine 162 + +#define ID_buttonEraser 170 +#define ID_buttonAttacher 171 +#define ID_buttonTracer 172 +#define ID_buttonText 173 +#define ID_buttonMoveGrid 174 +#define ID_buttonPointer 175 +#define ID_buttonDrawColor 176 +#define ID_buttonSizer 177 + +#define ID_drawingModeNoMode 0 +#define ID_drawingModeMovingGrid 1 +#define ID_drawingModeMovingObjects 2 +#define ID_drawingModeConstructing 3 + +#define MinimumPointSize 3 + +#define Str_AppName "KGeo" + + +#define ID_overlayRectSize 24 + +#define PI 3.1415926535 + +#endif // RESOURCE_H diff --git a/kig/filters/kseg-defs.h b/kig/filters/kseg-defs.h new file mode 100644 index 00000000..ad90fa1a --- /dev/null +++ b/kig/filters/kseg-defs.h @@ -0,0 +1,301 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. + +// this is a collection of definitions we need from KSeg. It includes +// code from defs.H and G_drawstyle.H. Thanks to Ilya Baran for +// making KSeg GPL, so there are no license probs or whatever.. + +/* + * KSeg + * Copyright (C) 1999-2003 Ilya Baran + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. + * + * Send comments and/or bug reports to: + * ibaran@mit.edu + */ + + +#ifndef DEFS_H +#define DEFS_H + +#include <stdio.h> +#include <stdlib.h> +#include <qglobal.h> + +using namespace std; + +#define DRAW_MAX 5000 // maximum coordinate. assumes you have a screen resolution less than this. + +#define BIG (1e+37) +#define SMALL (1e-10) + +inline int ROUND(double x) { return ((int)(x + 0.5)); } +inline int SIGN(double x) { return (x < 0) ? -1 : 1; } +inline int INTRAND(int a, int b) { return QMIN(a, b) + rand() % abs(a - b); } +#define SQR(x) ((x) * (x)) +#define CUBE(x) ((x) * (x) * (x)) +#define QUAD(x) (((x) * (x)) * ((x) * (x))) + +enum G_Type +{ + G_POINT = 1, + G_SEGMENT = 2, + G_RAY = 4, + G_LINE = 8, + G_CIRCLE = 16, + G_ARC = 32, + G_POLYGON = 64, + G_CIRCLEINTERIOR = 128, + G_ARCSECTOR = 256, + G_ARCSEGMENT = 512, + //non-primitive geometric types now: + G_LOCUS = 1024, + G_MEASURE = 2048, + G_CALCULATE = 4096, + G_ANNOTATION = 8192, + //fake type for scripting: + G_LOOP = 16384, + //compound types now: + G_STRAIGHT = G_SEGMENT | G_LINE | G_RAY, + G_CURVE = G_STRAIGHT | G_ARC | G_CIRCLE, + G_FILLED = G_POLYGON | G_CIRCLEINTERIOR | G_ARCSECTOR | G_ARCSEGMENT, + G_GEOMETRIC = G_POINT | G_CURVE | G_FILLED | G_LOCUS, + G_VALUE = G_MEASURE | G_CALCULATE, + G_TEXT = G_VALUE | G_ANNOTATION, + G_ANY = G_GEOMETRIC | G_TEXT | G_LOOP +}; + +enum G_AnyType +{ + G_TRANSLATED, + G_ROTATED, + G_SCALED, + G_REFLECTED +}; + +#define IS_TRANSFORM(x) ((x) == G_TRANSLATED || (x) == G_ROTATED || (x) == G_SCALED || (x) == G_REFLECTED) + +enum G_PointType +{ + G_FREE_POINT = G_REFLECTED + 1, + G_CONSTRAINED_POINT, + G_INTERSECTION_POINT, + G_INTERSECTION2_POINT, + G_MID_POINT +}; + +enum G_SegmentType +{ + G_ENDPOINTS_SEGMENT = G_REFLECTED + 1 +}; + +enum G_RayType +{ + G_TWOPOINTS_RAY = G_REFLECTED + 1, + G_BISECTOR_RAY +}; + +enum G_LineType +{ + G_TWOPOINTS_LINE = G_REFLECTED + 1, + G_PARALLEL_LINE, + G_PERPENDICULAR_LINE +}; + +enum G_CircleType +{ + G_CENTERPOINT_CIRCLE = G_REFLECTED + 1, + G_CENTERRADIUS_CIRCLE +}; + +enum G_ArcType +{ + G_THREEPOINTS_ARC = G_REFLECTED + 1 +}; + +enum G_FilledType +{ + G_DEFAULT_FILLED = G_REFLECTED + 1 +}; + +enum G_LocusType +{ + G_OBJECT_LOCUS = G_REFLECTED + 1 +}; + +enum G_MeasureType +{ + G_DISTANCE_MEASURE, + G_LENGTH_MEASURE, + G_RADIUS_MEASURE, + G_ANGLE_MEASURE, + G_RATIO_MEASURE, + G_SLOPE_MEASURE, + G_AREA_MEASURE +}; + +enum G_CalculateType +{ + G_REGULAR_CALCULATE +}; + + +enum MenuIDs +{ + ID_NEW_SEGMENT = 1, + ID_NEW_MIDPOINT, + ID_NEW_LINE, + ID_NEW_PERPENDICULAR, + ID_NEW_RAY, + ID_NEW_BISECTOR, + ID_NEW_CIRCLE, + ID_NEW_INTERSECTION, + ID_NEW_ARC, + ID_NEW_LOCUS, + ID_NEW_ARCSECTOR, + ID_NEW_ARCSEGMENT, + ID_NEW_CIRCLEINTERIOR, + ID_NEW_POLYGON, + + ID_EDIT_UNDO, + ID_EDIT_REDO, + ID_EDIT_DELETE, + ID_EDIT_TOGGLELABELS, + ID_EDIT_SHOWLABELS, + ID_EDIT_HIDELABELS, + ID_EDIT_CHANGELABEL, + ID_EDIT_HIDE, + ID_EDIT_SHOWHIDDEN, + ID_EDIT_COLOR, + ID_EDIT_POINTSTYLE, + ID_EDIT_LINESTYLE, + ID_EDIT_FONT, + ID_EDIT_CHANGE_NUMBER_OF_SAMPLES, + ID_EDIT_PREFERENCES, + + ID_EDIT_COLOR_BLACK, + ID_EDIT_COLOR_GRAY, + ID_EDIT_COLOR_RED, + ID_EDIT_COLOR_GREEN, + ID_EDIT_COLOR_BLUE, + ID_EDIT_COLOR_YELLOW, + ID_EDIT_COLOR_PURPLE, + ID_EDIT_COLOR_CYAN, + ID_EDIT_COLOR_OTHER, + + ID_EDIT_POINTSTYLE_LARGECIRCLE, + ID_EDIT_POINTSTYLE_MEDIUMCIRCLE, + ID_EDIT_POINTSTYLE_SMALLCIRCLE, + + ID_EDIT_LINESTYLE_SOLID, + ID_EDIT_LINESTYLE_DASHED, + ID_EDIT_LINESTYLE_DOTTED, + ID_EDIT_LINESTYLE_THIN, + ID_EDIT_LINESTYLE_NORMAL, + ID_EDIT_LINESTYLE_THICK, + + ID_EDIT_FONT_10, + ID_EDIT_FONT_12, + ID_EDIT_FONT_14, + ID_EDIT_FONT_20, + ID_EDIT_FONT_30, + ID_EDIT_FONT_FONT, + + ID_MEASURE_DISTANCE, + ID_MEASURE_LENGTH, + ID_MEASURE_RADIUS, + ID_MEASURE_ANGLE, + ID_MEASURE_RATIO, + ID_MEASURE_SLOPE, + ID_MEASURE_AREA, + ID_MEASURE_CALCULATE, + + ID_TRANSFORM_CHOOSE_VECTOR, + ID_TRANSFORM_CHOOSE_MIRROR, + ID_TRANSFORM_CHOOSE_CENTER, + ID_TRANSFORM_CHOOSE_RATIO, + ID_TRANSFORM_CHOOSE_ANGLE, + ID_TRANSFORM_CLEAR_CHOSEN, + ID_TRANSFORM_TRANSLATE, + ID_TRANSFORM_ROTATE, + ID_TRANSFORM_REFLECT, + ID_TRANSFORM_SCALE, + + ID_CONSTRUCTION_MAKE_NORMAL, + ID_CONSTRUCTION_MAKE_GIVEN, + ID_CONSTRUCTION_MAKE_FINAL, + ID_CONSTRUCTION_MAKE_INITIAL, + ID_CONSTRUCTION_RECURSE, + + ID_PLAY_QUICKPLAY, + + ID_QUICKPLAY_SET_DIRECTORY, + + ID_FILE_RECENTLIST_START //should be the last entry +}; + +#endif //DEFS_H + + +/* + * KSeg + * Copyright (C) 1999-2003 Ilya Baran + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. + * + * Send comments and/or bug reports to: + * ibaran@mit.edu + */ + + +#ifndef G_DRAWSTYLE_H +#define G_DRAWSTYLE_H + +enum PointStyle +{ + ANY = 0, + SMALL_CIRCLE, + MEDIUM_CIRCLE, + LARGE_CIRCLE +}; + +#endif //G_DRAWSTYLE_H diff --git a/kig/filters/kseg-filter-status.txt b/kig/filters/kseg-filter-status.txt new file mode 100644 index 00000000..eb896726 --- /dev/null +++ b/kig/filters/kseg-filter-status.txt @@ -0,0 +1,41 @@ +KSeg filter status +================== + +Objects imported +---------------- + +Points ok +Rays ok +Vectors ok +Lines ok +Circles ok +Angles ok +Arcs ok +Locuses some +Transformations most +Intersections most +Polygons ok +Labels ok + +Colors ok +Visibility ok +Styles ok +Grid ok + +Objects not imported +-------------------- + +Transformations scaling (with ratio as formula) (*) +Intersections arc-circle(*) +Filled circles (*) +Arc sectors (*) +Arc segments (*) +Formulas (*) + +(*) objects which currently are not implemented in Kig + +Known problems +-------------- + +* Some locuses may crash the filter. +* Label text should be correctly encoded. diff --git a/kig/filters/kseg-filter.cc b/kig/filters/kseg-filter.cc new file mode 100644 index 00000000..f2ba901b --- /dev/null +++ b/kig/filters/kseg-filter.cc @@ -0,0 +1,679 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "kseg-filter.h" + +#include "kseg-defs.h" + +#include "../kig/kig_document.h" +#include "../kig/kig_part.h" +#include "../misc/coordinate.h" +#include "../objects/angle_type.h" +#include "../objects/arc_type.h" +#include "../objects/bogus_imp.h" +#include "../objects/circle_type.h" +#include "../objects/conic_imp.h" +#include "../objects/conic_types.h" +#include "../objects/intersection_types.h" +#include "../objects/line_imp.h" +#include "../objects/line_type.h" +#include "../objects/object_calcer.h" +#include "../objects/object_drawer.h" +#include "../objects/object_factory.h" +#include "../objects/object_holder.h" +#include "../objects/other_imp.h" +#include "../objects/other_type.h" +#include "../objects/point_imp.h" +#include "../objects/point_type.h" +#include "../objects/polygon_type.h" +#include "../objects/transform_types.h" +#include "../objects/vector_type.h" + +#include <qfont.h> +#include <qpen.h> +#include <qbrush.h> +#include <qfile.h> +#include <qdatastream.h> +#include <qbuffer.h> + +#include <klocale.h> + +KigFilterKSeg::KigFilterKSeg() +{ +} + +KigFilterKSeg::~KigFilterKSeg() +{ +} + +bool KigFilterKSeg::supportMime( const QString& mime ) +{ + return mime == "application/x-kseg"; +} + +struct drawstyle +{ + Q_INT8 pointstyle; + QFont font; + QPen pen; + QBrush brush; +}; + +static Coordinate readKSegCoordinate( QDataStream& stream ) +{ + // read the coord.. + float inx, iny; + stream >> inx >> iny; + // KSeg uses a coordinate system, where the topleft is (0,0), and + // the bottom right is the widget coordinate in the window: if the + // KSeg window had a width of 600 pixels and a height of 600, then + // the bottom right will be at (600,600). We assume a window of + // such a height here, and transform it into Kig Coordinates. This + // function is quite similar to ScreenInfo::fromScreen, and it's + // basically a simple modification of that code to floats.. + + // invert the y-axis: 0 is at the bottom ! + Coordinate t( inx, 600 - iny ); + t *= 14; + t /= 600; + return t + Coordinate( -7, -7 ); +} + +static ObjectTypeCalcer* intersectionPoint( const std::vector<ObjectCalcer*>& parents, int which ) +{ + if ( parents.size() != 2 ) return 0; + int nlines = 0; + int nconics = 0; + int narcs = 0; + for ( int i = 0; i < 2; ++i ) + { + if ( parents[i]->imp()->inherits( AbstractLineImp::stype() ) ) ++nlines; + else if ( parents[i]->imp()->inherits( ConicImp::stype() ) ) ++nconics; + else if ( parents[i]->imp()->inherits( ArcImp::stype() ) ) ++narcs; + else return 0; + }; + if ( nlines == 2 ) + return which == -1 ? new ObjectTypeCalcer( LineLineIntersectionType::instance(), parents ) : 0; + else if ( nlines == 1 && nconics == 1 ) + { + std::vector<ObjectCalcer*> intparents( parents ); + intparents.push_back( new ObjectConstCalcer( new IntImp( which ) ) ); + return new ObjectTypeCalcer( ConicLineIntersectionType::instance(), intparents ); + } + else if ( nlines == 0 && nconics == 2 ) + { + std::vector<ObjectCalcer*> rparents( parents ); + rparents.push_back( new ObjectConstCalcer( new IntImp( 1 ) ) ); + rparents.push_back( new ObjectConstCalcer( new IntImp( 1 ) ) ); + rparents.push_back( new ObjectTypeCalcer( ConicRadicalType::instance(), rparents ) ); + std::vector<ObjectCalcer*> iparents; + iparents.push_back( parents[0] ); + iparents.push_back( rparents.back() ); + iparents.push_back( new ObjectConstCalcer( new IntImp( which ) ) ); + return new ObjectTypeCalcer( ConicLineIntersectionType::instance(), iparents ); + } + else if ( nlines == 1 && narcs == 1 ) + { + std::vector<ObjectCalcer*> intparents( parents ); + intparents.push_back( new ObjectConstCalcer( new IntImp( which ) ) ); + return new ObjectTypeCalcer( ArcLineIntersectionType::instance(), intparents ); + } + else return 0; +} + +ObjectCalcer* KigFilterKSeg::transformObject( const QString& file, KigDocument& kigdoc, + std::vector<ObjectCalcer*>& parents, + int subtype, bool& ok ) +{ + ok = true; + ObjectCalcer* retobj = 0; + switch( subtype ) + { + case G_TRANSLATED: + { + std::vector<ObjectCalcer*> vectorparents( parents.begin() + 1, parents.end() ); + ObjectTypeCalcer* vector = new ObjectTypeCalcer( VectorType::instance(), vectorparents ); + vector->calc( kigdoc ); + + std::vector<ObjectCalcer*> transparents; + transparents.push_back( parents[0] ); + transparents.push_back( vector ); + retobj = new ObjectTypeCalcer( TranslatedType::instance(), transparents ); + break; + } + case G_ROTATED: + { + std::vector<ObjectCalcer*> angleparents( parents.begin() + 2, parents.end() ); + ObjectTypeCalcer* angle = new ObjectTypeCalcer( AngleType::instance(), angleparents ); + angle->calc( kigdoc ); + + std::vector<ObjectCalcer*> rotparents; + rotparents.push_back( parents[0] ); + rotparents.push_back( parents[1] ); + rotparents.push_back( angle ); + retobj = new ObjectTypeCalcer( RotationType::instance(), rotparents ); + break; + } + case G_SCALED: + { + if ( parents.size() == 4 ) + { + retobj = new ObjectTypeCalcer( ScalingOverCenter2Type::instance(), parents ); + } + else + { + // TODO + notSupported( file, i18n( "This KSeg document uses a scaling " + "transformation, which Kig currently " + "cannot import." ) ); + ok = false; + return 0; + } + break; + } + case G_REFLECTED: + { + std::vector<ObjectCalcer*> mirparents( parents.begin(), parents.end() ); + retobj = new ObjectTypeCalcer( LineReflectionType::instance(), mirparents ); + break; + } + } + + return retobj; +} + +KigDocument* KigFilterKSeg::load( const QString& file ) +{ + QFile ffile( file ); + if ( ! ffile.open( IO_ReadOnly ) ) + { + fileNotFound( file ); + return false; + }; + + KigDocument* retdoc = new KigDocument(); + + QDataStream fstream( &ffile ); + + QString versionstring; + fstream >> versionstring; + if ( !versionstring.startsWith( "KSeg Document Version " ) ) + KIG_FILTER_PARSE_ERROR; + + QByteArray array; + fstream >> array; + QBuffer buf( array ); + buf.open( IO_ReadOnly ); + QDataStream stream( &buf ); + + stream.setVersion( 3 ); + + // G_drawstyles: + short numstyles; + stream >> numstyles; + std::vector<drawstyle> drawstyles( numstyles ); + for ( short i = 0; i < numstyles; ++i ) + { + stream >> drawstyles[i].pointstyle; + stream >> drawstyles[i].font; + stream >> drawstyles[i].pen; + stream >> drawstyles[i].brush; + }; + + std::vector<ObjectHolder*> ret; + std::vector<ObjectHolder*> ret2; + + // G_refs + unsigned int count; + stream >> count; + + ret.resize( count, 0 ); + const ObjectFactory* fact = ObjectFactory::instance(); + + // KSeg topologically sorts the objects before saving, that means we + // can read the entire file in one iteration.. + for ( uint i = 0; i < count; ++i ) + { + short styleid; + stream >> styleid; + short nparents; + stream >> nparents; + std::vector<ObjectCalcer*> parents( nparents, 0 ); + for ( short j = 0; j < nparents; ++j ) + { + int parent; + stream >> parent; + parents[j] = ret[parent]->calcer(); + }; + + // read the object.. + short info; + stream >> info; + int type = 1 << (info & 31); + info >>= 5; + int descendtype = (info & 15); + info >>= 4; + bool visible = info & 1; + bool labelVisible = info & 2; + bool given = info & 4; + bool final = info & 8; + + // avoid g++ warnings about unused vars.. + // this doesn't really do anything.. + (void) given; + (void) final; + + drawstyle style = drawstyles[styleid]; + + if ( type == G_LOOP ) continue; + // read the label.. + QString labeltext; + stream >> labeltext; + Coordinate relcoord = readKSegCoordinate( stream ); + // shut up gcc + (void) relcoord; + if ( type & G_CURVE ) + { + Coordinate relcurvecoord = readKSegCoordinate( stream ); + // shut up gcc + (void) relcurvecoord; + }; + + // now load the object data.. + ObjectHolder* object = 0; + ObjectCalcer* o = 0; + bool ok = true; + + QColor color = style.pen.color(); + int width = style.pen.width(); + +/* + kdDebug() << "type: " << type << endl + << "descendtype: " << descendtype << endl + << "label: " << labeltext << endl; +//*/ + + switch ( type ) + { + case G_POINT: + { + switch( descendtype ) + { + case G_TRANSLATED: + case G_ROTATED: + case G_SCALED: + case G_REFLECTED: + { + o = transformObject( file, *retdoc, parents, descendtype, ok ); + break; + } + case G_FREE_POINT: + { + // fixed point + if ( nparents != 0 ) KIG_FILTER_PARSE_ERROR; + Coordinate c = readKSegCoordinate( stream ); + o = fact->fixedPointCalcer( c ); + break; + } + case G_CONSTRAINED_POINT: + { + // constrained point + double p; + stream >> p; + if ( nparents != 1 ) KIG_FILTER_PARSE_ERROR; + ObjectCalcer* parent = parents[0]; + assert( parent ); + o = fact->constrainedPointCalcer( parent, p ); + break; + } + case G_INTERSECTION_POINT: + { + // KSeg has somewhat weird intersection objects.. + // for all objects G_INTERSECTION_POINT gets the + // first intersection of its parents, G_INTERSECTION2_POINT + // represents the second, if present. + o = intersectionPoint( parents, -1 ); + if ( ! o ) KIG_FILTER_PARSE_ERROR; + break; + } + case G_INTERSECTION2_POINT: + { + o = intersectionPoint( parents, 1 ); + if ( ! o ) KIG_FILTER_PARSE_ERROR; + break; + } + case G_MID_POINT: + { + // midpoint of a segment.. + if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR; + if ( !parents[0]->imp()->inherits( SegmentImp::stype() ) ) + KIG_FILTER_PARSE_ERROR; + int index = parents[0]->imp()->propertiesInternalNames().findIndex( "mid-point" ); + assert( index != -1 ); + o = new ObjectPropertyCalcer( parents[0], index ); + break; + } + default: + KIG_FILTER_PARSE_ERROR; + }; + width = style.pointstyle == SMALL_CIRCLE ? 2 : style.pointstyle == MEDIUM_CIRCLE ? 3 : 5; + color = style.brush.color(); + break; + }; + case G_SEGMENT: + { + switch( descendtype ) + { + case G_TRANSLATED: + case G_ROTATED: + case G_SCALED: + case G_REFLECTED: + { + o = transformObject( file, *retdoc, parents, descendtype, ok ); + break; + } + case G_ENDPOINTS_SEGMENT: + { + if ( nparents != 2 ) KIG_FILTER_PARSE_ERROR; + o = new ObjectTypeCalcer( SegmentABType::instance(), parents ); + break; + } + default: + KIG_FILTER_PARSE_ERROR; + } + break; + }; + case G_RAY: + { + switch( descendtype ) + { + case G_TRANSLATED: + case G_ROTATED: + case G_SCALED: + case G_REFLECTED: + { + o = transformObject( file, *retdoc, parents, descendtype, ok ); + break; + } + case G_TWOPOINTS_RAY: + { + if ( nparents != 2 ) KIG_FILTER_PARSE_ERROR; + o = new ObjectTypeCalcer( RayABType::instance(), parents ); + break; + } + case G_BISECTOR_RAY: + { + ObjectTypeCalcer* angle = new ObjectTypeCalcer( HalfAngleType::instance(), parents ); + angle->calc( *retdoc ); + o = fact->propertyObjectCalcer( angle, "angle-bisector" ); + break; + } + default: + KIG_FILTER_PARSE_ERROR; + }; + break; + }; + case G_LINE: + { + switch( descendtype ) + { + case G_TRANSLATED: + case G_ROTATED: + case G_SCALED: + case G_REFLECTED: + { + o = transformObject( file, *retdoc, parents, descendtype, ok ); + break; + } + case G_TWOPOINTS_LINE: + { + if ( nparents != 2 ) KIG_FILTER_PARSE_ERROR; + o = new ObjectTypeCalcer( LineABType::instance(), parents ); + break; + } + case G_PARALLEL_LINE: + { + if ( nparents != 2 ) KIG_FILTER_PARSE_ERROR; + o = new ObjectTypeCalcer( LineParallelLPType::instance(), parents ); + break; + } + case G_PERPENDICULAR_LINE: + { + if ( nparents != 2 ) KIG_FILTER_PARSE_ERROR; + o = new ObjectTypeCalcer( LinePerpendLPType::instance(), parents ); + break; + } + default: + KIG_FILTER_PARSE_ERROR; + }; + break; + }; + case G_CIRCLE: + { + switch( descendtype ) + { + case G_TRANSLATED: + case G_ROTATED: + case G_SCALED: + case G_REFLECTED: + { + o = transformObject( file, *retdoc, parents, descendtype, ok ); + break; + } + case G_CENTERPOINT_CIRCLE: + { + if ( nparents != 2 ) KIG_FILTER_PARSE_ERROR; + o = new ObjectTypeCalcer( CircleBCPType::instance(), parents ); + break; + } + case G_CENTERRADIUS_CIRCLE: + { + ObjectCalcer* point; + ObjectCalcer* segment; + if ( parents[0]->imp()->inherits( PointImp::stype() ) ) + { + point = parents[0]; + segment = parents[1]; + } + else + { + point = parents[1]; + segment = parents[0]; + }; + int index = segment->imp()->propertiesInternalNames().findIndex( "length" ); + if ( index == -1 ) KIG_FILTER_PARSE_ERROR; + ObjectPropertyCalcer* length = new ObjectPropertyCalcer( segment, index ); + length->calc( *retdoc ); + std::vector<ObjectCalcer*> cparents; + cparents.push_back( point ); + cparents.push_back( length ); + o = new ObjectTypeCalcer( CircleBPRType::instance(), cparents ); + break; + } + default: + KIG_FILTER_PARSE_ERROR; + }; + break; + }; + case G_ARC: + { + switch( descendtype ) + { + case G_TRANSLATED: + case G_ROTATED: + case G_SCALED: + case G_REFLECTED: + { + o = transformObject( file, *retdoc, parents, descendtype, ok ); + break; + } + case G_THREEPOINTS_ARC: + { + if ( nparents != 3 ) KIG_FILTER_PARSE_ERROR; + o = new ObjectTypeCalcer( ArcBTPType::instance(), parents ); + break; + } + default: + KIG_FILTER_PARSE_ERROR; + } + break; + }; + case G_POLYGON: + { + switch( descendtype ) + { + case G_TRANSLATED: + case G_ROTATED: + case G_SCALED: + case G_REFLECTED: + { + o = transformObject( file, *retdoc, parents, descendtype, ok ); + break; + } + default: + { + if ( nparents < 3 ) KIG_FILTER_PARSE_ERROR; + o = new ObjectTypeCalcer( PolygonBNPType::instance(), parents ); + } + } +// default: +// KIG_FILTER_PARSE_ERROR; + break; + }; + case G_CIRCLEINTERIOR: + { + notSupported( file, i18n( "This KSeg file contains a filled circle, " + "which Kig does not currently support." ) ); + return false; + }; + case G_ARCSECTOR: + { + notSupported( file, i18n( "This KSeg file contains an arc sector, " + "which Kig does not currently support." ) ); + return false; + }; + case G_ARCSEGMENT: + { + notSupported( file, i18n( "This KSeg file contains an arc segment, " + "which Kig does not currently support." ) ); + return false; + }; + case G_LOCUS: + { + switch( descendtype ) + { + case G_TRANSLATED: + case G_ROTATED: + case G_SCALED: + case G_REFLECTED: + { + o = transformObject( file, *retdoc, parents, descendtype, ok ); + break; + } + case G_OBJECT_LOCUS: + { + if ( nparents != 2 ) KIG_FILTER_PARSE_ERROR; + o = fact->locusCalcer( parents[0], parents[1] ); + break; + } + default: + KIG_FILTER_PARSE_ERROR; + } + break; + }; + case G_MEASURE: + KIG_FILTER_PARSE_ERROR; + case G_CALCULATE: + KIG_FILTER_PARSE_ERROR; + case G_ANNOTATION: + KIG_FILTER_PARSE_ERROR; + case G_LOOP: + KIG_FILTER_PARSE_ERROR; + default: + KIG_FILTER_PARSE_ERROR; + + } + + // checking if the object was correctly created + if ( ! o ) + { + if ( ok ) + KIG_FILTER_PARSE_ERROR + else + return 0; + } + + ObjectDrawer* d = new ObjectDrawer( color, width, visible, style.pen.style() ); + if ( !labeltext.isEmpty() ) + { + ObjectConstCalcer* name = new ObjectConstCalcer( new StringImp( labeltext ) ); + object = new ObjectHolder( o, d, name ); + } + else + { + object = new ObjectHolder( o, d ); + } + + assert( object ); + ret[i] = object; + object->calc( *retdoc ); + if ( !labeltext.isEmpty() && labelVisible ) + { + std::vector<ObjectCalcer*> args2; + args2.push_back( object->nameCalcer() ); + ObjectCalcer* oc2 = fact->attachedLabelCalcer( + QString::fromLatin1( "%1" ), object->calcer(), + static_cast<const PointImp*>( object->imp() )->coordinate(), + false, args2, *retdoc ); + oc2->calc( *retdoc ); + ObjectDrawer* d2 = new ObjectDrawer( style.pen.color() ); + ObjectHolder* o2 = new ObjectHolder( oc2, d2 ); + ret2.push_back( o2 ); + } + }; + + // selection groups ( we ignore them, but we pretend to read them + // out anyway, so we can find what comes after them.. ) + int selgroupcount; + stream >> selgroupcount; + for ( int i = 0; i < selgroupcount; ++i ) + { + QString name; + stream >> name; + int size; + stream >> size; + for ( int i = 0; i < size; ++i ) + { + short object; + stream >> object; + (void) object; + }; + }; + + // no more data in the file.. + retdoc->addObjects( ret ); + retdoc->addObjects( ret2 ); + retdoc->setAxes( false ); + retdoc->setGrid( false ); + return retdoc; +} + +KigFilterKSeg* KigFilterKSeg::instance() +{ + static KigFilterKSeg f; + return &f; +} diff --git a/kig/filters/kseg-filter.h b/kig/filters/kseg-filter.h new file mode 100644 index 00000000..af475a7a --- /dev/null +++ b/kig/filters/kseg-filter.h @@ -0,0 +1,42 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_FILTERS_KSEG_FILTER_H +#define KIG_FILTERS_KSEG_FILTER_H + +#include "filter.h" + +class ObjectCalcer; + +class KigFilterKSeg + : public KigFilter +{ + KigFilterKSeg(); + ~KigFilterKSeg(); + + ObjectCalcer* transformObject( const QString& file, KigDocument& kigdoc, + std::vector<ObjectCalcer*>& parents, + int subtype, bool& ok ); + +public: + static KigFilterKSeg* instance(); + + bool supportMime ( const QString& mime ); + KigDocument* load ( const QString& fromfile ); +}; + +#endif diff --git a/kig/filters/latexexporter.cc b/kig/filters/latexexporter.cc new file mode 100644 index 00000000..955ac148 --- /dev/null +++ b/kig/filters/latexexporter.cc @@ -0,0 +1,608 @@ +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include <config.h> + +#include "latexexporter.h" + +#include "latexexporteroptions.h" + +#include "../kig/kig_document.h" +#include "../kig/kig_part.h" +#include "../kig/kig_view.h" +#include "../misc/common.h" +#include "../misc/goniometry.h" +#include "../misc/kigfiledialog.h" +#include "../misc/rect.h" +#include "../objects/circle_imp.h" +#include "../objects/cubic_imp.h" +#include "../objects/curve_imp.h" +#include "../objects/line_imp.h" +#include "../objects/locus_imp.h" +#include "../objects/object_drawer.h" +#include "../objects/object_holder.h" +#include "../objects/object_imp.h" +#include "../objects/other_imp.h" +#include "../objects/point_imp.h" +#include "../objects/polygon_imp.h" +#include "../objects/text_imp.h" + +#include <math.h> +#include <algorithm> + +#include <qcheckbox.h> +#include <qcolor.h> +#include <qfile.h> +#include <qtextstream.h> + +#include <klocale.h> +#include <kmessagebox.h> + +#ifdef HAVE_TRUNC +#define KDE_TRUNC(a) trunc(a) +#else +#define KDE_TRUNC(a) rint(a) +#endif + +struct ColorMap { + QColor color; + QString name; +}; + +LatexExporter::~LatexExporter() +{ +} + +QString LatexExporter::exportToStatement() const +{ + return i18n( "Export to &Latex..." ); +} + +QString LatexExporter::menuEntryName() const +{ + return i18n( "&Latex..." ); +} + +QString LatexExporter::menuIcon() const +{ + // TODO + return "tex"; +} + +class LatexExportImpVisitor + : public ObjectImpVisitor +{ + QTextStream& mstream; + ObjectHolder* mcurobj; + const KigWidget& mw; + Rect msr; + std::vector<ColorMap> mcolors; + QString mcurcolorid; +public: + void visit( ObjectHolder* obj ); + void mapColor( QColor color ); + + LatexExportImpVisitor( QTextStream& s, const KigWidget& w ) + : mstream( s ), mw( w ), msr( mw.showingRect() ) + { + } + void visit( const LineImp* imp ); + void visit( const PointImp* imp ); + void visit( const TextImp* imp ); + void visit( const AngleImp* imp ); + void visit( const VectorImp* imp ); + void visit( const LocusImp* imp ); + void visit( const CircleImp* imp ); + void visit( const ConicImp* imp ); + void visit( const CubicImp* imp ); + void visit( const SegmentImp* imp ); + void visit( const RayImp* imp ); + void visit( const ArcImp* imp ); + void visit( const PolygonImp* imp ); + + double unit; + +private: + /** + * Converts Kig coords to pstrick coord system and sends them to stream + * using the format: (xcoord,ycoord) + */ + void emitCoord( const Coordinate& c ); + /** + * Draws a line (segment) or a vector if vector is true. + */ + void emitLine( const Coordinate& a, const Coordinate& b, const int width, + const Qt::PenStyle s, bool vector = false ); + /** + * Sends a new line character ( \n ) to stream. + */ + void newLine(); + /** + * Searches if a color is already mapped into mcolors, and returns its + * index or -1 if not found. + */ + int findColor( QColor c ); + /** + * Use to convert a dimension "on the screen" to a dimension wrt. + * Kig coordinate system. + */ + double dimRealToCoord( int dim ); + /** + * Converts a pen style into latex style string. + */ + QString writeStyle( Qt::PenStyle style ); + /** + * Plots a generic curve though its points calc'ed with getPoint. + */ + void plotGenericCurve( const CurveImp* imp ); +}; + +void LatexExportImpVisitor::emitCoord( const Coordinate& c ) +{ + mstream << "(" << c.x - msr.left() << "," << c.y - msr.bottom() << ")"; +} + +void LatexExportImpVisitor::emitLine( const Coordinate& a, const Coordinate& b, + const int width, const Qt::PenStyle s, + bool vector ) +{ + mstream << "\\psline[linecolor=" << mcurcolorid << ",linewidth=" << width / 100.0 + << "," << writeStyle( s ); + if ( vector ) + mstream << ",arrowscale=3,arrowinset=1.3"; + mstream << "]"; + if ( vector ) + mstream << "{->}"; + emitCoord( a ); + emitCoord( b ); + newLine(); +} + +void LatexExportImpVisitor::newLine() +{ + mstream << "\n"; +} + +int LatexExportImpVisitor::findColor( QColor c ) +{ + for ( uint i = 0; i < mcolors.size(); ++i ) + { + if ( c == mcolors[i].color ) + return i; + } + return -1; +} + +void LatexExportImpVisitor::mapColor( QColor color ) +{ + if ( findColor( color ) == -1 ) + { + ColorMap newcolor; + newcolor.color = color; + QString tmpname = color.name(); + tmpname.replace( "#", "" ); + newcolor.name = tmpname; + mcolors.push_back( newcolor ); + mstream << "\\newrgbcolor{" << tmpname << "}{" + << color.red() / 255.0 << " " << color.green() / 255.0 << " " + << color.blue() / 255.0 << "}\n"; + } +} + +double LatexExportImpVisitor::dimRealToCoord( int dim ) +{ + QRect qr( 0, 0, dim, dim ); + Rect r = mw.screenInfo().fromScreen( qr ); + return fabs( r.width() ); +} + +QString LatexExportImpVisitor::writeStyle( Qt::PenStyle style ) +{ + QString ret( "linestyle=" ); + if ( style == Qt::DashLine ) + ret += "dashed"; + else if ( style == Qt::DotLine ) + ret += "dotted,dotsep=2pt"; + else + ret += "solid"; + return ret; +} + +void LatexExportImpVisitor::plotGenericCurve( const CurveImp* imp ) +{ + int width = mcurobj->drawer()->width(); + if ( width == -1 ) width = 1; + + QString prefix = QString( "\\pscurve[linecolor=%1,linewidth=%2,%3]" ) + .arg( mcurcolorid ) + .arg( width / 100.0 ) + .arg( writeStyle( mcurobj->drawer()->style() ) ); + + std::vector< std::vector< Coordinate > > coordlist; + coordlist.push_back( std::vector< Coordinate >() ); + uint curid = 0; + + Coordinate c; + Coordinate prev = Coordinate::invalidCoord(); + for ( double i = 0.0; i <= 1.0; i += 0.005 ) + { + c = imp->getPoint( i, mw.document() ); + if ( !c.valid() ) + { + if ( coordlist[curid].size() > 0 ) + { + coordlist.push_back( std::vector< Coordinate >() ); + ++curid; + prev = Coordinate::invalidCoord(); + } + continue; + } + if ( ! ( ( fabs( c.x ) <= 1000 ) && ( fabs( c.y ) <= 1000 ) ) ) + continue; + // if there's too much distance between this coordinate and the previous + // one, then it's another piece of curve not joined with the rest + if ( prev.valid() && ( c.distance( prev ) > 4.0 ) ) + { + coordlist.push_back( std::vector< Coordinate >() ); + ++curid; + } + coordlist[curid].push_back( c ); + prev = c; + } + // special case for ellipse + if ( const ConicImp* conic = dynamic_cast< const ConicImp* >( imp ) ) + { + // if ellipse, close its path + if ( conic->conicType() == 1 && coordlist.size() == 1 && coordlist[0].size() > 1 ) + { + coordlist[0].push_back( coordlist[0][0] ); + } + } + for ( uint i = 0; i < coordlist.size(); ++i ) + { + uint s = coordlist[i].size(); + // there's no point in draw curves empty or with only one point + if ( s <= 1 ) + continue; + + mstream << prefix; + for ( uint j = 0; j < s; ++j ) + emitCoord( coordlist[i][j] ); + newLine(); + } +} + +void LatexExportImpVisitor::visit( ObjectHolder* obj ) +{ + if ( ! obj->drawer()->shown() ) + return; + const int id = findColor( obj->drawer()->color() ); + if ( id == -1 ) + return; + mcurcolorid = mcolors[id].name; + mcurobj = obj; + obj->imp()->visit( this ); +} + +void LatexExportImpVisitor::visit( const LineImp* imp ) +{ + Coordinate a = imp->data().a; + Coordinate b = imp->data().b; + calcBorderPoints( a, b, msr ); + + int width = mcurobj->drawer()->width(); + if ( width == -1 ) width = 1; + + emitLine( a, b, width, mcurobj->drawer()->style() ); +} + +void LatexExportImpVisitor::visit( const PointImp* imp ) +{ + int width = mcurobj->drawer()->width(); + if ( width == -1 ) width = 5; + width /= 5; + + mstream << "\\psdots[linecolor=" << mcurcolorid + << ",dotscale=" << width << ",dotstyle="; + const int ps = mcurobj->drawer()->pointStyle(); + QString pss( "*,fillstyle=solid,fillcolor=" + mcurcolorid ); + if ( ps == 1 ) + pss = "o,fillstyle=none"; + else if ( ps == 2 ) + pss = "square*,fillstyle=solid,fillcolor=" + mcurcolorid; + else if ( ps == 3 ) + pss = "square,fillstyle=none"; + else if ( ps == 4 ) + pss = "+,dotangle=45"; + mstream << pss << "]"; + emitCoord( imp->coordinate() ); + newLine(); +} + +void LatexExportImpVisitor::visit( const TextImp* imp ) +{ + // FIXME: support multiline texts... + mstream << "\\rput[tl]"; + emitCoord( imp->coordinate() ); + newLine(); + mstream << "{"; + newLine(); + if ( imp->hasFrame() ) + { + mstream << " \\psframebox[linecolor=c5c2c5,linewidth=0.01" + << ",fillstyle=solid,fillcolor=ffffde]" + << "{" << imp->text() << "}"; + } + else + { + mstream << imp->text(); + } + newLine(); + mstream << "}"; + newLine(); +} + +void LatexExportImpVisitor::visit( const AngleImp* imp ) +{ + const Coordinate center = imp->point(); + const double radius = dimRealToCoord( 50 ) * unit; + double startangle = imp->startAngle(); + double endangle = startangle + imp->angle(); +// if ( startangle > M_PI ) +// startangle -= 2 * M_PI; + startangle = Goniometry::convert( startangle, Goniometry::Rad, Goniometry::Deg ); +// if ( endangle > 2 * M_PI ) +// endangle -= 2 * M_PI; + endangle = Goniometry::convert( endangle, Goniometry::Rad, Goniometry::Deg ); + int width = mcurobj->drawer()->width(); + if ( width == -1 ) width = 1; + + mstream << "\\psarc[linecolor=" << mcurcolorid << ",linewidth=" << width / 100.0 + << "," << writeStyle( mcurobj->drawer()->style() ) << ",arrowscale=3,arrowinset=0]{->}"; + emitCoord( center ); + mstream << "{" << radius << "}{" << startangle << "}{" << endangle << "}"; + newLine(); +} + +void LatexExportImpVisitor::visit( const VectorImp* imp ) +{ + Coordinate a = imp->data().a; + Coordinate b = imp->data().b; + + int width = mcurobj->drawer()->width(); + if ( width == -1 ) width = 1; + + emitLine( a, b, width, mcurobj->drawer()->style(), true ); +} + +void LatexExportImpVisitor::visit( const LocusImp* imp ) +{ + plotGenericCurve( imp ); +} + +void LatexExportImpVisitor::visit( const CircleImp* imp ) +{ + int width = mcurobj->drawer()->width(); + if ( width == -1 ) width = 1; + + mstream << "\\pscircle[linecolor=" << mcurcolorid << ",linewidth=" << width / 100.0 + << "," << writeStyle( mcurobj->drawer()->style() ) << "]"; + emitCoord( imp->center() ); + mstream << "{" << imp->radius() * unit << "}"; + newLine(); +} + +void LatexExportImpVisitor::visit( const ConicImp* imp ) +{ + plotGenericCurve( imp ); +} + +void LatexExportImpVisitor::visit( const CubicImp* ) +{ + // FIXME: cubic are not drawn correctly with plotGenericCurve +// plotGenericCurve( imp ); +} + +void LatexExportImpVisitor::visit( const SegmentImp* imp ) +{ + Coordinate a = imp->data().a; + Coordinate b = imp->data().b; + + int width = mcurobj->drawer()->width(); + if ( width == -1 ) width = 1; + + emitLine( a, b, width, mcurobj->drawer()->style() ); +} + +void LatexExportImpVisitor::visit( const RayImp* imp ) +{ + Coordinate a = imp->data().a; + Coordinate b = imp->data().b; + calcRayBorderPoints( a, b, msr ); + + int width = mcurobj->drawer()->width(); + if ( width == -1 ) width = 1; + + emitLine( a, b, width, mcurobj->drawer()->style() ); +} + +void LatexExportImpVisitor::visit( const ArcImp* imp ) +{ + const Coordinate center = imp->center(); + const double radius = imp->radius() * unit; + double startangle = imp->startAngle(); + double endangle = startangle + imp->angle(); +// if ( startangle > M_PI ) +// startangle -= 2 * M_PI; + startangle = Goniometry::convert( startangle, Goniometry::Rad, Goniometry::Deg ); +// if ( endangle > M_PI ) +// endangle -= 2 * M_PI; + endangle = Goniometry::convert( endangle, Goniometry::Rad, Goniometry::Deg ); + int width = mcurobj->drawer()->width(); + if ( width == -1 ) width = 1; + + mstream << "\\psarc[linecolor=" << mcurcolorid << ",linewidth=" << width / 100.0 + << "," << writeStyle( mcurobj->drawer()->style() ) << "]"; + emitCoord( center ); + mstream << "{" << radius << "}{" << startangle << "}{" << endangle << "}"; + newLine(); +} + +void LatexExportImpVisitor::visit( const PolygonImp* imp ) +{ + int width = mcurobj->drawer()->width(); + if ( width == -1 ) width = 1; + + mstream << "\\pspolygon[linecolor=" << mcurcolorid << ",linewidth=0" + << "," << writeStyle( mcurobj->drawer()->style() ) + << ",hatchcolor=" << mcurcolorid << ",hatchwidth=0.5pt,hatchsep=0.5pt" + << ",fillcolor=" << mcurcolorid << ",fillstyle=crosshatch]"; + + std::vector<Coordinate> pts = imp->points(); + for ( uint i = 0; i < pts.size(); i++ ) + { + emitCoord( pts[i] ); + } + newLine(); +} + +void LatexExporter::run( const KigPart& doc, KigWidget& w ) +{ + KigFileDialog* kfd = new KigFileDialog( + QString::null, i18n( "*.tex|Latex Documents (*.tex)" ), + i18n( "Export as Latex" ), &w ); + kfd->setOptionCaption( i18n( "Latex Options" ) ); + LatexExporterOptions* opts = new LatexExporterOptions( 0L ); + kfd->setOptionsWidget( opts ); + opts->showGridCheckBox->setChecked( doc.document().grid() ); + opts->showAxesCheckBox->setChecked( doc.document().axes() ); + opts->showExtraFrameCheckBox->setChecked( false ); + if ( !kfd->exec() ) + return; + + QString file_name = kfd->selectedFile(); + bool showgrid = opts->showGridCheckBox->isOn(); + bool showaxes = opts->showAxesCheckBox->isOn(); + bool showframe = opts->showExtraFrameCheckBox->isOn(); + + delete opts; + delete kfd; + + QFile file( file_name ); + if ( ! file.open( IO_WriteOnly ) ) + { + KMessageBox::sorry( &w, i18n( "The file \"%1\" could not be opened. Please " + "check if the file permissions are set correctly." ) + .arg( file_name ) ); + return; + }; + + QTextStream stream( &file ); + stream << "\\documentclass[a4paper]{minimal}\n"; +// stream << "\\usepackage[latin1]{inputenc}\n"; + stream << "\\usepackage{pstricks}\n"; + stream << "\\usepackage{pst-plot}\n"; + stream << "\\author{Kig " << KIGVERSION << "}\n"; + stream << "\\begin{document}\n"; + + const double bottom = w.showingRect().bottom(); + const double left = w.showingRect().left(); + const double height = w.showingRect().height(); + const double width = w.showingRect().width(); + +/* + // TODO: calculating aspect ratio... + if ( 297 / 210 >= height / width ) + { + + } +*/ + const double tmpwidth = 15.0; + const double xunit = tmpwidth / width; + const double yunit = xunit; + + stream << "\\begin{pspicture*}(0,0)(" << tmpwidth << "," << yunit * height << ")\n"; + stream << "\\psset{xunit=" << xunit << "}\n"; + stream << "\\psset{yunit=" << yunit << "}\n"; + + std::vector<ObjectHolder*> os = doc.document().objects(); + LatexExportImpVisitor visitor( stream, w ); + visitor.unit = xunit; + + for ( std::vector<ObjectHolder*>::const_iterator i = os.begin(); + i != os.end(); ++i ) + { + if ( ! ( *i )->shown() ) continue; + visitor.mapColor( ( *i )->drawer()->color() ); + }; + visitor.mapColor( QColor( 255, 255, 222 ) ); // ffffde - text label background + visitor.mapColor( QColor( 197, 194, 197 ) ); // c5c2c5 - text label border line + visitor.mapColor( QColor( 160, 160, 164 ) ); // a0a0a4 - axes color + visitor.mapColor( QColor( 192, 192, 192 ) ); // c0c0c0 - grid color + + // extra frame + if ( showframe ) + { + stream << "\\psframe[linecolor=black,linewidth=0.02]" + << "(0,0)" + << "(" << width << "," << height << ")" + << "\n"; + } + + // grid + if ( showgrid ) + { + // vertical lines... + double startingpoint = - left - 1 + static_cast<int>( KDE_TRUNC( left ) ); + for ( double i = startingpoint; i < width; ++i ) + { + stream << "\\psline[linecolor=c0c0c0,linewidth=0.01,linestyle=dashed]" + << "(" << i << ",0)" + << "(" << i << "," << height << ")" + << "\n"; + } + + // horizontal lines... + startingpoint = - bottom - 1 + static_cast<int>( KDE_TRUNC( bottom ) ); + for ( double i = startingpoint; i < height; ++i ) + { + stream << "\\psline[linecolor=c0c0c0,linewidth=0.01,linestyle=dashed]" + << "(0," << i << ")" + << "(" << width << "," << i << ")" + << "\n"; + } + } + + // axes + if ( showaxes ) + { + stream << "\\psaxes[linecolor=a0a0a4,linewidth=0.03,ticks=none,arrowinset=0]{->}" + << "(" << -left << "," << -bottom << ")" + << "(0,0)" + << "(" << width << "," << height << ")" + << "\n"; + } + + for ( std::vector<ObjectHolder*>::const_iterator i = os.begin(); + i != os.end(); ++i ) + { + visitor.visit( *i ); + }; + + stream << "\\end{pspicture*}\n"; + stream << "\\end{document}\n"; +} diff --git a/kig/filters/latexexporter.h b/kig/filters/latexexporter.h new file mode 100644 index 00000000..32638de1 --- /dev/null +++ b/kig/filters/latexexporter.h @@ -0,0 +1,41 @@ +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_FILTERS_LATEXEXPORTER_H +#define KIG_FILTERS_LATEXEXPORTER_H + +#include "exporter.h" + +class QString; +class KigPart; +class KigWidget; + +/** + * Export to LaTex. + */ +class LatexExporter + : public KigExporter +{ +public: + ~LatexExporter(); + QString exportToStatement() const; + QString menuEntryName() const; + QString menuIcon() const; + void run( const KigPart& doc, KigWidget& w ); +}; + +#endif diff --git a/kig/filters/latexexporteroptions.ui b/kig/filters/latexexporteroptions.ui new file mode 100644 index 00000000..d2e60c5b --- /dev/null +++ b/kig/filters/latexexporteroptions.ui @@ -0,0 +1,69 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>LatexExporterOptions</class> +<widget class="QWidget"> + <property name="name"> + <cstring>LatexExporterOptions</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>450</width> + <height>150</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="title"> + <string>Options</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>showGridCheckBox</cstring> + </property> + <property name="text"> + <string>Show grid</string> + </property> + </widget> + <widget class="QCheckBox" row="0" column="1"> + <property name="name"> + <cstring>showAxesCheckBox</cstring> + </property> + <property name="text"> + <string>Show axes</string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="0"> + <property name="name"> + <cstring>showExtraFrameCheckBox</cstring> + </property> + <property name="text"> + <string>Show extra frame</string> + </property> + </widget> + </grid> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<layoutdefaults spacing="6" margin="11"/> +<includehints> +</includehints> +</UI> diff --git a/kig/filters/native-filter.cc b/kig/filters/native-filter.cc new file mode 100644 index 00000000..6cecf09b --- /dev/null +++ b/kig/filters/native-filter.cc @@ -0,0 +1,747 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "native-filter.h" + +#include "../kig/kig_part.h" +#include "../kig/kig_document.h" +#include "../objects/bogus_imp.h" +#include "../objects/object_type.h" +#include "../objects/object_imp.h" +#include "../objects/object_calcer.h" +#include "../objects/object_drawer.h" +#include "../objects/object_holder.h" +#include "../objects/object_type_factory.h" +#include "../objects/object_imp_factory.h" +#include "../misc/calcpaths.h" +#include "../misc/coordinate_system.h" + +#include "config.h" + +#include <qdom.h> +#include <qfile.h> +#include <qregexp.h> + +#include <karchive.h> +#include <kdebug.h> +#include <kglobal.h> +#include <kstandarddirs.h> +#include <ktar.h> + +#include <stdio.h> + +#include <vector> +#include <algorithm> +#include <map> + +struct HierElem +{ + int id; + std::vector<int> parents; + QDomElement el; +}; + +static void extendVect( std::vector<HierElem>& vect, uint size ) +{ + if ( size > vect.size() ) + { + int osize = vect.size(); + vect.resize( size ); + for ( uint i = osize; i < size; ++i ) + vect[i].id = i+1; + }; +} + +static void visitElem( std::vector<HierElem>& ret, + const std::vector<HierElem>& elems, + std::vector<bool>& seen, + int i ) +{ + if ( !seen[i] ) + { + for ( uint j = 0; j < elems[i].parents.size(); ++j ) + visitElem( ret, elems, seen, elems[i].parents[j] - 1); + ret.push_back( elems[i] ); + seen[i] = true; + }; +} + +static std::vector<HierElem> sortElems( const std::vector<HierElem> elems ) +{ + std::vector<HierElem> ret; + std::vector<bool> seenElems( elems.size(), false ); + for ( uint i = 0; i < elems.size(); ++i ) + visitElem( ret, elems, seenElems, i ); + return ret; +} + +KigFilterNative::KigFilterNative() +{ +} + +KigFilterNative::~KigFilterNative() +{ +} + +bool KigFilterNative::supportMime( const QString& mime ) +{ + return mime == "application/x-kig"; +} + +KigDocument* KigFilterNative::load( const QString& file ) +{ + QFile ffile( file ); + if ( ! ffile.open( IO_ReadOnly ) ) + { + fileNotFound( file ); + return 0; + }; + + QFile kigdoc( file ); +#ifndef KIG_NO_COMPRESSED_FILES + bool iscompressed = false; + if ( !file.endsWith( ".kig", false ) ) + { + // the file is compressed, so we have to decompress it and fetch the + // kig file inside it... + iscompressed = true; + + QString tempdir = KGlobal::dirs()->saveLocation( "tmp" ); + if ( tempdir.isEmpty() ) + KIG_FILTER_PARSE_ERROR; + + QString tempname = file.section( '/', -1 ); + if ( file.endsWith( ".kigz", false ) ) + { + tempname.remove( QRegExp( "\\.[Kk][Ii][Gg][Zz]$" ) ); + } + else + KIG_FILTER_PARSE_ERROR; + // reading compressed file + KTar* ark = new KTar( file, "application/x-gzip" ); + ark->open( IO_ReadOnly ); + const KArchiveDirectory* dir = ark->directory(); +// assert( dir ); + QStringList entries = dir->entries(); + QStringList kigfiles = entries.grep( QRegExp( "\\.kig$" ) ); + if ( kigfiles.count() != 1 ) + // I throw a generic parse error here, but I should warn the user that + // this kig archive file doesn't contain one kig file (it contains no + // kig files or more than one). + KIG_FILTER_PARSE_ERROR; + const KArchiveEntry* kigz = dir->entry( kigfiles[0] ); + if ( !kigz->isFile() ) + KIG_FILTER_PARSE_ERROR; + dynamic_cast<const KArchiveFile*>( kigz )->copyTo( tempdir ); + kdDebug() << "extracted file: " << tempdir + kigz->name() << endl + << "exists: " << QFile::exists( tempdir + kigz->name() ) << endl; + + kigdoc.setName( tempdir + kigz->name() ); + } +#endif + + if ( !kigdoc.open( IO_ReadOnly ) ) + KIG_FILTER_PARSE_ERROR; + + QDomDocument doc( "KigDocument" ); + if ( !doc.setContent( &kigdoc ) ) + KIG_FILTER_PARSE_ERROR; + kigdoc.close(); + +#ifndef KIG_NO_COMPRESSED_FILES + // removing temp file + if ( iscompressed ) + kigdoc.remove(); +#endif + + QDomElement main = doc.documentElement(); + + QString version = main.attribute( "CompatibilityVersion" ); + if ( version.isEmpty() ) version = main.attribute( "Version" ); + if ( version.isEmpty() ) version = main.attribute( "version" ); + if ( version.isEmpty() ) + KIG_FILTER_PARSE_ERROR; + + // matches 0.1, 0.2.0, 153.128.99 etc. + QRegExp versionre( "(\\d+)\\.(\\d+)(\\.(\\d+))?" ); + if ( ! versionre.exactMatch( version ) ) + KIG_FILTER_PARSE_ERROR; + bool ok = true; + int major = versionre.cap( 1 ).toInt( &ok ); + bool ok2 = true; + int minor = versionre.cap( 2 ).toInt( &ok2 ); + if ( ! ok || ! ok2 ) + KIG_FILTER_PARSE_ERROR; + + // int minorminor = versionre.cap( 4 ).toInt( &ok ); + + // we only support 0.[0-7] and 1.0.* + if ( major > 0 || minor > 9 ) + { + notSupported( file, i18n( "This file was created by Kig version \"%1\", " + "which this version cannot open." ).arg( version ) ); + return false; + } + else if ( major == 0 && minor <= 3 ) + { + notSupported( file, i18n( "This file was created by Kig version \"%1\".\n" + "Support for older Kig formats (pre-0.4) has been " + "removed from Kig.\n" + "You can try to open this file with an older Kig " + "version (0.4 to 0.6),\n" + "and then save it again, which will save it in the " + "new format." ).arg( version ) ); + return false; + } + else if ( major == 0 && minor <= 6 ) + return load04( file, main ); + else + return load07( file, main ); +} + +KigDocument* KigFilterNative::load04( const QString& file, const QDomElement& docelem ) +{ + bool ok = true; + + KigDocument* ret = new KigDocument(); + + for ( QDomNode n = docelem.firstChild(); ! n.isNull(); n = n.nextSibling() ) + { + QDomElement e = n.toElement(); + if ( e.isNull() ) continue; + if ( e.tagName() == "CoordinateSystem" ) + { + const QCString type = e.text().latin1(); + CoordinateSystem* s = CoordinateSystemFactory::build( type ); + if ( ! s ) + { + warning( i18n( "This Kig file has a coordinate system " + "that this Kig version does not support.\n" + "A standard coordinate system will be used " + "instead." ) ); + } + else ret->setCoordinateSystem( s ); + } + else if ( e.tagName() == "Objects" ) + { + std::vector<ObjectCalcer*> retcalcers; + std::vector<ObjectHolder*> retholders; + + // first pass: do a topological sort of the objects, to support + // randomly ordered files... + std::vector<HierElem> elems; + QDomElement objectselem = e; + for ( QDomNode o = objectselem.firstChild(); ! o.isNull(); o = o.nextSibling() ) + { + e = o.toElement(); + if ( e.isNull() ) continue; + uint id; + if ( e.tagName() == "Data" || e.tagName() == "Property" || e.tagName() == "Object" ) + { + // fetch the id + QString tmp = e.attribute("id"); + id = tmp.toInt(&ok); + if ( !ok ) KIG_FILTER_PARSE_ERROR; + + extendVect( elems, id ); + elems[id-1].el = e; + } + else continue; + + for ( QDomNode p = e.firstChild(); !p.isNull(); p = p.nextSibling() ) + { + QDomElement f = p.toElement(); + if ( f.isNull() ) continue; + if ( f.tagName() == "Parent" ) + { + QString tmp = f.attribute( "id" ); + uint pid = tmp.toInt( &ok ); + if ( ! ok ) KIG_FILTER_PARSE_ERROR; + + extendVect( elems, id ); + elems[id-1].parents.push_back( pid ); + } + } + }; + + for ( uint i = 0; i < elems.size(); ++i ) + if ( elems[i].el.isNull() ) + KIG_FILTER_PARSE_ERROR; + elems = sortElems( elems ); + + retcalcers.resize( elems.size(), 0 ); + + for ( std::vector<HierElem>::iterator i = elems.begin(); + i != elems.end(); ++i ) + { + QDomElement e = i->el; + bool internal = e.attribute( "internal" ) == "true" ? true : false; + ObjectCalcer* o = 0; + if ( e.tagName() == "Data" ) + { + QString tmp = e.attribute( "type" ); + if ( tmp.isNull() ) + KIG_FILTER_PARSE_ERROR; + QString error; + ObjectImp* imp = ObjectImpFactory::instance()->deserialize( tmp, e, error ); + if ( ( !imp ) && !error.isEmpty() ) + { + parseError( file, error ); + return false; + } + o = new ObjectConstCalcer( imp ); + } + else if ( e.tagName() == "Property" ) + { + QCString propname; + for ( QDomElement ec = e.firstChild().toElement(); !ec.isNull(); + ec = ec.nextSibling().toElement() ) + { + if ( ec.tagName() == "Property" ) + propname = ec.text().latin1(); + }; + + if ( i->parents.size() != 1 ) KIG_FILTER_PARSE_ERROR; + ObjectCalcer* parent = retcalcers[i->parents[0] -1]; + QCStringList propnames = parent->imp()->propertiesInternalNames(); + int propid = propnames.findIndex( propname ); + if ( propid == -1 ) + KIG_FILTER_PARSE_ERROR; + + o = new ObjectPropertyCalcer( parent, propid ); + } + else if ( e.tagName() == "Object" ) + { + QString tmp = e.attribute( "type" ); + if ( tmp.isNull() ) + KIG_FILTER_PARSE_ERROR; + + const ObjectType* type = + ObjectTypeFactory::instance()->find( tmp.latin1() ); + if ( !type ) + { + notSupported( file, i18n( "This Kig file uses an object of type \"%1\", " + "which this Kig version does not support." + "Perhaps you have compiled Kig without support " + "for this object type," + "or perhaps you are using an older Kig version." ).arg( tmp ) ); + return false; + }; + + std::vector<ObjectCalcer*> parents; + for ( std::vector<int>::iterator j = i->parents.begin(); + j != i->parents.end(); ++j ) + parents.push_back( retcalcers[*j - 1] ); + + o = new ObjectTypeCalcer( type, parents ); + } + else continue; + + o->calc( *ret ); + retcalcers[i->id - 1] = o; + + if ( ! internal ) + { + QString tmp = e.attribute( "color" ); + QColor color( tmp ); + if ( !color.isValid() ) + KIG_FILTER_PARSE_ERROR; + + tmp = e.attribute( "shown" ); + bool shown = !( tmp == "false" || tmp == "no" ); + + tmp = e.attribute( "width" ); + int width = tmp.toInt( &ok ); + if ( ! ok ) width = -1; + + ObjectDrawer* d = new ObjectDrawer( color, width, shown ); + retholders.push_back( new ObjectHolder( o, d ) ); + } + } + ret->addObjects( retholders ); + } + else continue; // be forward-compatible.. + }; + + return ret; +} + +KigFilterNative* KigFilterNative::instance() +{ + static KigFilterNative f; + return &f; +} + +KigDocument* KigFilterNative::load07( const QString& file, const QDomElement& docelem ) +{ + KigDocument* ret = new KigDocument(); + + bool ok = true; + std::vector<ObjectCalcer::shared_ptr> calcers; + std::vector<ObjectHolder*> holders; + + QString t = docelem.attribute( "grid" ); + bool tmphide = ( t == "false" ) || ( t == "no" ) || ( t == "0" ); + ret->setGrid( !tmphide ); + t = docelem.attribute( "axes" ); + tmphide = ( t == "false" ) || ( t == "no" ) || ( t == "0" ); + ret->setAxes( !tmphide ); + + for ( QDomElement subsectionelement = docelem.firstChild().toElement(); ! subsectionelement.isNull(); + subsectionelement = subsectionelement.nextSibling().toElement() ) + { + if ( subsectionelement.tagName() == "CoordinateSystem" ) + { + QString tmptype = subsectionelement.text(); + // compatibility code - to support Invisible coord system... + if ( tmptype == "Invisible" ) + { + tmptype = "Euclidean"; + ret->setGrid( false ); + ret->setAxes( false ); + } + const QCString type = tmptype.latin1(); + CoordinateSystem* s = CoordinateSystemFactory::build( type ); + if ( ! s ) + { + warning( i18n( "This Kig file has a coordinate system " + "that this Kig version does not support.\n" + "A standard coordinate system will be used " + "instead." ) ); + } + else ret->setCoordinateSystem( s ); + } + else if ( subsectionelement.tagName() == "Hierarchy" ) + { + for ( QDomElement e = subsectionelement.firstChild().toElement(); ! e.isNull(); + e = e.nextSibling().toElement() ) + { + QString tmp = e.attribute( "id" ); + uint id = tmp.toInt( &ok ); + if ( id <= 0 ) KIG_FILTER_PARSE_ERROR; + + std::vector<ObjectCalcer*> parents; + for ( QDomElement parentel = e.firstChild().toElement(); ! parentel.isNull(); + parentel = parentel.nextSibling().toElement() ) + { + if ( parentel.tagName() != "Parent" ) continue; + QString tmp = parentel.attribute( "id" ); + uint parentid = tmp.toInt( &ok ); + if ( ! ok ) KIG_FILTER_PARSE_ERROR; + if ( parentid == 0 || parentid > calcers.size() ) KIG_FILTER_PARSE_ERROR; + ObjectCalcer* parent = calcers[parentid - 1].get(); + if ( ! parent ) KIG_FILTER_PARSE_ERROR; + parents.push_back( parent ); + } + + ObjectCalcer* o = 0; + + if ( e.tagName() == "Data" ) + { + if ( !parents.empty() ) KIG_FILTER_PARSE_ERROR; + QString tmp = e.attribute( "type" ); + QString error; + ObjectImp* imp = ObjectImpFactory::instance()->deserialize( tmp, e, error ); + if ( ( !imp ) && !error.isEmpty() ) + { + parseError( file, error ); + return false; + } + o = new ObjectConstCalcer( imp ); + } + else if ( e.tagName() == "Property" ) + { + if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR; + QCString propname = e.attribute( "which" ).latin1(); + + ObjectCalcer* parent = parents[0]; + int propid = parent->imp()->propertiesInternalNames().findIndex( propname ); + if ( propid == -1 ) KIG_FILTER_PARSE_ERROR; + + o = new ObjectPropertyCalcer( parent, propid ); + } + else if ( e.tagName() == "Object" ) + { + QString tmp = e.attribute( "type" ); + const ObjectType* type = + ObjectTypeFactory::instance()->find( tmp.latin1() ); + if ( ! type ) + { + notSupported( file, i18n( "This Kig file uses an object of type \"%1\", " + "which this Kig version does not support." + "Perhaps you have compiled Kig without support " + "for this object type," + "or perhaps you are using an older Kig version." ).arg( tmp ) ); + return false; + } + + // mp: (I take the responsibility for this!) explanation: the usual ObjectTypeCalcer + // constructor also "sortArgs" the parents. I believe that this *must not* be done + // when loading from a saved kig file for the following reasons: + // 1. the arguments should already be in their intended order, since the file was + // saved from a working hierarchy; furthermore we actually want to restore the original + // hierarchy, not really to also fix possible problems with the original hierarchy; + // 2. calling sortArgs could have undesirable side effects in particular situations, + // since kig actually allow an ObjectType to produce different type of ObjectImp's + // it may happen that the parents of an object do not satisfy the requirements + // enforced by sortArgs (while moving around the free objects) but still be + // perfectly valid + o = new ObjectTypeCalcer( type, parents, false ); + } + else KIG_FILTER_PARSE_ERROR; + + o->calc( *ret ); + calcers.resize( id, 0 ); + calcers[id-1] = o; + } + } + else if ( subsectionelement.tagName() == "View" ) + { + for ( QDomElement e = subsectionelement.firstChild().toElement(); ! e.isNull(); + e = e.nextSibling().toElement() ) + { + if ( e.tagName() != "Draw" ) KIG_FILTER_PARSE_ERROR; + + QString tmp = e.attribute( "object" ); + uint id = tmp.toInt( &ok ); + if ( !ok ) KIG_FILTER_PARSE_ERROR; + if ( id <= 0 || id > calcers.size() ) + KIG_FILTER_PARSE_ERROR; + ObjectCalcer* calcer = calcers[id-1].get(); + + tmp = e.attribute( "color" ); + QColor color( tmp ); + if ( !color.isValid() ) + KIG_FILTER_PARSE_ERROR; + + tmp = e.attribute( "shown" ); + bool shown = !( tmp == "false" || tmp == "no" ); + + tmp = e.attribute( "width" ); + int width = tmp.toInt( &ok ); + if ( ! ok ) width = -1; + + tmp = e.attribute( "style" ); + Qt::PenStyle style = ObjectDrawer::styleFromString( tmp ); + + tmp = e.attribute( "point-style" ); + int pointstyle = ObjectDrawer::pointStyleFromString( tmp ); + + ObjectConstCalcer* namecalcer = 0; + tmp = e.attribute( "namecalcer" ); + if ( tmp != "none" && !tmp.isEmpty() ) + { + int ncid = tmp.toInt( &ok ); + if ( !ok ) KIG_FILTER_PARSE_ERROR; + if ( ncid <= 0 || ncid > calcers.size() ) + KIG_FILTER_PARSE_ERROR; + if ( ! dynamic_cast<ObjectConstCalcer*>( calcers[ncid-1].get() ) ) + KIG_FILTER_PARSE_ERROR; + namecalcer = static_cast<ObjectConstCalcer*>( calcers[ncid-1].get() ); + } + + ObjectDrawer* drawer = new ObjectDrawer( color, width, shown, style, pointstyle ); + holders.push_back( new ObjectHolder( calcer, drawer, namecalcer ) ); + } + } + } + + ret->addObjects( holders ); + return ret; +} + +bool KigFilterNative::save07( const KigDocument& kdoc, QTextStream& stream ) +{ + QDomDocument doc( "KigDocument" ); + + QDomElement docelem = doc.createElement( "KigDocument" ); + docelem.setAttribute( "Version", KIGVERSION ); + docelem.setAttribute( "CompatibilityVersion", "0.7.0" ); + docelem.setAttribute( "grid", kdoc.grid() ); + docelem.setAttribute( "axes", kdoc.axes() ); + + QDomElement cselem = doc.createElement( "CoordinateSystem" ); + cselem.appendChild( doc.createTextNode( kdoc.coordinateSystem().type() ) ); + docelem.appendChild( cselem ); + + std::vector<ObjectHolder*> holders = kdoc.objects(); + std::vector<ObjectCalcer*> calcers = getAllParents( getAllCalcers( holders ) ); + calcers = calcPath( calcers ); + + QDomElement hierelem = doc.createElement( "Hierarchy" ); + std::map<const ObjectCalcer*, int> idmap; + for ( std::vector<ObjectCalcer*>::const_iterator i = calcers.begin(); + i != calcers.end(); ++i ) + idmap[*i] = ( i - calcers.begin() ) + 1; + int id = 1; + + for ( std::vector<ObjectCalcer*>::const_iterator i = calcers.begin(); i != calcers.end(); ++i ) + { + QDomElement objectelem; + if ( dynamic_cast<ObjectConstCalcer*>( *i ) ) + { + objectelem = doc.createElement( "Data" ); + QString ser = + ObjectImpFactory::instance()->serialize( *(*i)->imp(), objectelem, doc ); + objectelem.setAttribute( "type", ser ); + } + else if ( dynamic_cast<const ObjectPropertyCalcer*>( *i ) ) + { + const ObjectPropertyCalcer* o = static_cast<const ObjectPropertyCalcer*>( *i ); + objectelem = doc.createElement( "Property" ); + + QCString propname = o->parent()->imp()->propertiesInternalNames()[o->propId()]; + objectelem.setAttribute( "which", propname ); + } + else if ( dynamic_cast<const ObjectTypeCalcer*>( *i ) ) + { + const ObjectTypeCalcer* o = static_cast<const ObjectTypeCalcer*>( *i ); + objectelem = doc.createElement( "Object" ); + objectelem.setAttribute( "type", o->type()->fullName() ); + } + else assert( false ); + + const std::vector<ObjectCalcer*> parents = ( *i )->parents(); + for ( std::vector<ObjectCalcer*>::const_iterator i = parents.begin(); i != parents.end(); ++i ) + { + std::map<const ObjectCalcer*,int>::const_iterator idp = idmap.find( *i ); + assert( idp != idmap.end() ); + int pid = idp->second; + QDomElement pel = doc.createElement( "Parent" ); + pel.setAttribute( "id", pid ); + objectelem.appendChild( pel ); + } + + objectelem.setAttribute( "id", id++ ); + hierelem.appendChild( objectelem ); + } + docelem.appendChild( hierelem ); + + QDomElement windowelem = doc.createElement( "View" ); + for ( std::vector<ObjectHolder*>::iterator i = holders.begin(); i != holders.end(); ++i ) + { + std::map<const ObjectCalcer*,int>::const_iterator idp = idmap.find( ( *i )->calcer() ); + assert( idp != idmap.end() ); + int id = idp->second; + + const ObjectDrawer* d = ( *i )->drawer(); + QDomElement drawelem = doc.createElement( "Draw" ); + drawelem.setAttribute( "object", id ); + drawelem.setAttribute( "color", d->color().name() ); + drawelem.setAttribute( "shown", QString::fromLatin1( d->shown() ? "true" : "false" ) ); + drawelem.setAttribute( "width", QString::number( d->width() ) ); + drawelem.setAttribute( "style", d->styleToString() ); + drawelem.setAttribute( "point-style", d->pointStyleToString() ); + + ObjectCalcer* namecalcer = ( *i )->nameCalcer(); + if ( namecalcer ) + { + std::map<const ObjectCalcer*,int>::const_iterator ncp = idmap.find( namecalcer ); + assert( ncp != idmap.end() ); + int ncid = ncp->second; + drawelem.setAttribute( "namecalcer", ncid ); + } + else + { + drawelem.setAttribute( "namecalcer", "none" ); + } + + windowelem.appendChild( drawelem ); + }; + docelem.appendChild( windowelem ); + + doc.appendChild( docelem ); + stream << doc.toString(); + return true; +} + +bool KigFilterNative::save( const KigDocument& data, const QString& file ) +{ + return save07( data, file ); +} + +bool KigFilterNative::save07( const KigDocument& data, const QString& outfile ) +{ + // we have an empty outfile, so we have to print all to stdout + if ( outfile.isEmpty() ) + { + QTextStream stdoutstream( stdout, IO_WriteOnly ); + return save07( data, stdoutstream ); + } +#ifndef KIG_NO_COMPRESSED_FILES + if ( !outfile.endsWith( ".kig", false ) ) + { + // the user wants to save a compressed file, so we have to save our kig + // file to a temp file and then compress it... + + QString tempdir = KGlobal::dirs()->saveLocation( "tmp" ); + if ( tempdir.isEmpty() ) + return false; + + QString tempname = outfile.section( '/', -1 ); + if ( outfile.endsWith( ".kigz", false ) ) + tempname.remove( QRegExp( "\\.[Kk][Ii][Gg][Zz]$" ) ); + else + return false; + + QString tmpfile = tempdir + tempname + ".kig"; + QFile ftmpfile( tmpfile ); + if ( !ftmpfile.open( IO_WriteOnly ) ) + return false; + QTextStream stream( &ftmpfile ); + if ( !save07( data, stream ) ) + return false; + ftmpfile.close(); + + kdDebug() << "tmp saved file: " << tmpfile << endl; + + // creating the archive and adding our file + KTar* ark = new KTar( outfile, "application/x-gzip" ); + ark->open( IO_WriteOnly ); + ark->addLocalFile( tmpfile, tempname + ".kig" ); + ark->close(); + + // finally, removing temp file + QFile::remove( tmpfile ); + + return true; + } + else + { +#endif + QFile file( outfile ); + if ( ! file.open( IO_WriteOnly ) ) + { + fileNotFound( outfile ); + return false; + } + QTextStream stream( &file ); + return save07( data, stream ); +#ifndef KIG_NO_COMPRESSED_FILES + } + + // we should never reach this point... + return false; +#endif +} + +/* +bool KigFilterNative::save( const KigDocument& data, QTextStream& stream ) +{ + return save07( data, stream ); +} +*/ diff --git a/kig/filters/native-filter.h b/kig/filters/native-filter.h new file mode 100644 index 00000000..12778f35 --- /dev/null +++ b/kig/filters/native-filter.h @@ -0,0 +1,66 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_FILTERS_NATIVE_FILTER_H +#define KIG_FILTERS_NATIVE_FILTER_H + +#include "filter.h" + +class QDomElement; +class KigDocument; +class QTextStream; +class QString; + +/** + * Kig's native format. Between versions 0.3.1 and 0.4, there was a + * change in the file format. This filter no longer supports pre-0.4 + * formats, it did up until Kig 0.6. + */ +class KigFilterNative + : public KigFilter +{ +private: + /** + * this is the load function for the Kig format that is used, + * starting at Kig 0.4 + */ + KigDocument* load04( const QString& file, const QDomElement& doc ); + /** + * this is the load function for the Kig format that is used + * starting at Kig 0.7 + */ + KigDocument* load07( const QString& file, const QDomElement& doc ); + + /** + * save in the Kig format that is used starting at Kig 0.7 + */ + bool save07( const KigDocument& data, const QString& outfile ); + bool save07( const KigDocument& data, QTextStream& file ); + + KigFilterNative(); + ~KigFilterNative(); +public: + static KigFilterNative* instance(); + + bool supportMime( const QString& mime ); + KigDocument* load( const QString& file ); + + bool save( const KigDocument& data, const QString& file ); +// bool save( const KigDocument& data, QTextStream& stream ); +}; + +#endif diff --git a/kig/filters/svgexporter.cc b/kig/filters/svgexporter.cc new file mode 100644 index 00000000..6e945de2 --- /dev/null +++ b/kig/filters/svgexporter.cc @@ -0,0 +1,111 @@ +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "svgexporter.h" + +#include "svgexporteroptions.h" + +#include "../kig/kig_document.h" +#include "../kig/kig_part.h" +#include "../kig/kig_view.h" +#include "../misc/common.h" +#include "../misc/kigfiledialog.h" +#include "../misc/kigpainter.h" + +#include <qcheckbox.h> +#include <qfile.h> +#include <qpicture.h> +#include <qrect.h> + +#include <klocale.h> +#include <kmessagebox.h> + +#include <map> + +SVGExporter::~SVGExporter() +{ +} + +QString SVGExporter::exportToStatement() const +{ + return i18n( "&Export to SVG..." ); +} + +QString SVGExporter::menuEntryName() const +{ + return i18n( "&SVG..." ); +} + +QString SVGExporter::menuIcon() const +{ + // TODO + return "vectorgfx"; +} + +void SVGExporter::run( const KigPart& part, KigWidget& w ) +{ + KigFileDialog* kfd = new KigFileDialog( + QString::null, i18n( "*.svg|Scalable Vector Graphics (*.svg)" ), + i18n( "Export as SVG" ), &w ); + kfd->setOptionCaption( i18n( "SVG Options" ) ); + SVGExporterOptions* opts = new SVGExporterOptions( 0L ); + kfd->setOptionsWidget( opts ); + opts->showGridCheckBox->setChecked( part.document().grid() ); + opts->showAxesCheckBox->setChecked( part.document().axes() ); + if ( !kfd->exec() ) + return; + + QString file_name = kfd->selectedFile(); + bool showgrid = opts->showGridCheckBox->isOn(); + bool showaxes = opts->showAxesCheckBox->isOn(); + + delete opts; + delete kfd; + + QFile file( file_name ); + if ( ! file.open( IO_WriteOnly ) ) + { + KMessageBox::sorry( &w, i18n( "The file \"%1\" could not be opened. Please " + "check if the file permissions are set correctly." ) + .arg( file_name ) ); + return; + }; + + QRect viewrect( w.screenInfo().viewRect() ); + QRect r( 0, 0, viewrect.width(), viewrect.height() ); + + QPicture pic; + pic.setBoundingRect( r ); + KigPainter* p = new KigPainter( ScreenInfo( w.screenInfo().shownRect(), viewrect ), + &pic, part.document() ); +// p->setWholeWinOverlay(); +// p->setBrushColor( Qt::white ); +// p->setBrushStyle( Qt::SolidPattern ); +// p->drawRect( r ); +// p->setBrushStyle( Qt::NoBrush ); +// p->setWholeWinOverlay(); + p->drawGrid( part.document().coordinateSystem(), showgrid, showaxes ); + p->drawObjects( part.document().objects(), false ); + + delete p; + + if ( !pic.save( file_name, "SVG" ) ) + { + KMessageBox::error( &w, i18n( "Sorry, something went wrong while saving to SVG file \"%1\"" ).arg( file_name ) ); + } + +} diff --git a/kig/filters/svgexporter.h b/kig/filters/svgexporter.h new file mode 100644 index 00000000..17193673 --- /dev/null +++ b/kig/filters/svgexporter.h @@ -0,0 +1,41 @@ +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_FILTERS_SVGEXPORTER_H +#define KIG_FILTERS_SVGEXPORTER_H + +#include "exporter.h" + +class QString; +class KigPart; +class KigWidget; + +/** + * Export to Scalable Vector Graphics (SVG) + */ +class SVGExporter + : public KigExporter +{ +public: + ~SVGExporter(); + QString exportToStatement() const; + QString menuEntryName() const; + QString menuIcon() const; + void run( const KigPart& part, KigWidget& w ); +}; + +#endif diff --git a/kig/filters/svgexporteroptions.ui b/kig/filters/svgexporteroptions.ui new file mode 100644 index 00000000..2de679cb --- /dev/null +++ b/kig/filters/svgexporteroptions.ui @@ -0,0 +1,61 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>SVGExporterOptions</class> +<widget class="QWidget"> + <property name="name"> + <cstring>SVGExporterOptions</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>440</width> + <height>200</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="title"> + <string>Options</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>showGridCheckBox</cstring> + </property> + <property name="text"> + <string>Show grid</string> + </property> + </widget> + <widget class="QCheckBox" row="0" column="1"> + <property name="name"> + <cstring>showAxesCheckBox</cstring> + </property> + <property name="text"> + <string>Show axes</string> + </property> + </widget> + </grid> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<layoutdefaults spacing="6" margin="11"/> +<includehints> +</includehints> +</UI> diff --git a/kig/filters/tests/circleBCP.FIG b/kig/filters/tests/circleBCP.FIG new file mode 100644 index 00000000..aeaa9689 --- /dev/null +++ b/kig/filters/tests/circleBCP.FIG @@ -0,0 +1,24 @@ +FIGURE CabriII vers. MS-Windows 1.0
+
+Window center x: 0 y: 0 Window size x: 16.2983333333333 y: 9.68375
+
+1: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, I, nSt
+Val: 0 0
+
+2: Axes, 1, CN:1, VN:3
+Gr, W, t, DS:1 1, GT:0, I, nSt
+Const: 1, Val: 1 0, 0 1
+
+3: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: -4.89479166666667 0.291041666666667
+
+4: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: -1.61395833333333 2.43416666666667
+
+5: Cir, 0, CN:2, VN:2
+Bl, W, t, DS:1 1, GT:0, V, nSt
+Const: 3 4
+
diff --git a/kig/filters/tests/constrainedPoint.FIG b/kig/filters/tests/constrainedPoint.FIG new file mode 100644 index 00000000..ac086421 --- /dev/null +++ b/kig/filters/tests/constrainedPoint.FIG @@ -0,0 +1,24 @@ +FIGURE CabriII vers. MS-Windows 1.0
+
+Window center x: 0 y: 0 Window size x: 16.2983333333333 y: 9.68375
+
+1: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, I, nSt
+Val: 0 0
+
+2: Axes, 1, CN:1, VN:3
+Gr, W, t, DS:1 1, GT:0, I, nSt
+Const: 1, Val: 1 0, 0 1
+
+3: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: -5.18583333333333 1.11125
+
+4: Line, 0, CN:1, VN:2
+V, W, t, DS:1 1, GT:0, V, nSt
+Const: 3, Val: 0.958665525176453 0.284535429846892
+
+5: Pt/, 0, CN:1, VN:3
+R, W, t, DS:1 1, GT:1, V, nSt
+Const: 4, Val: -2.80458333333333 1.81801369863014
+
diff --git a/kig/filters/tests/cubiclineintersect.kig b/kig/filters/tests/cubiclineintersect.kig new file mode 100644 index 00000000..a157f611 --- /dev/null +++ b/kig/filters/tests/cubiclineintersect.kig @@ -0,0 +1,105 @@ +<!DOCTYPE KigDocument> +<KigDocument Version="0.6.0" > + <CoordinateSystem>Euclidean</CoordinateSystem> + <Objects> + <Data internal="true" type="double" id="1" >-5.63522</Data> + <Data internal="true" type="double" id="2" >1.0566</Data> + <Data internal="true" type="double" id="3" >-1.49686</Data> + <Data internal="true" type="double" id="4" >2.33333</Data> + <Data internal="true" type="double" id="5" >-1.14465</Data> + <Data internal="true" type="double" id="6" >-0.220126</Data> + <Data internal="true" type="double" id="7" >-4.66667</Data> + <Data internal="true" type="double" id="8" >-1.14465</Data> + <Data internal="true" type="double" id="9" >-8.1927</Data> + <Data internal="true" type="double" id="10" >-2.01515</Data> + <Data internal="true" type="double" id="11" >-8.36478</Data> + <Data internal="true" type="double" id="12" >1.36478</Data> + <Data internal="true" type="double" id="13" >-7.48428</Data> + <Data internal="true" type="double" id="14" >2.64151</Data> + <Data internal="true" type="double" id="15" >-4.88679</Data> + <Data internal="true" type="double" id="16" >3.12579</Data> + <Data internal="true" type="double" id="17" >-3.03774</Data> + <Data internal="true" type="double" id="18" >1.49686</Data> + <Data internal="true" type="double" id="19" >-6.25157</Data> + <Data internal="true" type="double" id="20" >3.91824</Data> + <Data internal="true" type="double" id="21" >-2.06918</Data> + <Data internal="true" type="double" id="22" >-3.47799</Data> + <Data internal="true" type="int" id="23" >1</Data> + <Data internal="true" type="int" id="24" >2</Data> + <Data internal="true" type="int" id="25" >3</Data> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="26" color="#0000ff" > + <Parent id="1" /> + <Parent id="2" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="27" color="#0000ff" > + <Parent id="3" /> + <Parent id="4" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="28" color="#0000ff" > + <Parent id="5" /> + <Parent id="6" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="29" color="#0000ff" > + <Parent id="7" /> + <Parent id="8" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="30" color="#0000ff" > + <Parent id="9" /> + <Parent id="10" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="31" color="#0000ff" > + <Parent id="11" /> + <Parent id="12" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="32" color="#0000ff" > + <Parent id="13" /> + <Parent id="14" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="33" color="#0000ff" > + <Parent id="15" /> + <Parent id="16" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="34" color="#0000ff" > + <Parent id="17" /> + <Parent id="18" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="35" color="#0000ff" > + <Parent id="19" /> + <Parent id="20" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="36" color="#0000ff" > + <Parent id="21" /> + <Parent id="22" /> + </Object> + <Object width="-1" internal="false" shown="true" type="CubicB9P" id="37" color="#0000ff" > + <Parent id="26" /> + <Parent id="27" /> + <Parent id="28" /> + <Parent id="29" /> + <Parent id="30" /> + <Parent id="31" /> + <Parent id="32" /> + <Parent id="33" /> + <Parent id="34" /> + </Object> + <Object width="-1" internal="false" shown="true" type="LineAB" id="38" color="#0000ff" > + <Parent id="35" /> + <Parent id="36" /> + </Object> + <Object width="-1" internal="false" shown="true" type="LineCubicIntersection" id="39" color="#0000ff" > + <Parent id="37" /> + <Parent id="38" /> + <Parent id="23" /> + </Object> + <Object width="-1" internal="false" shown="true" type="LineCubicIntersection" id="40" color="#0000ff" > + <Parent id="37" /> + <Parent id="38" /> + <Parent id="24" /> + </Object> + <Object width="-1" internal="false" shown="true" type="LineCubicIntersection" id="41" color="#0000ff" > + <Parent id="37" /> + <Parent id="38" /> + <Parent id="25" /> + </Object> + </Objects> +</KigDocument> diff --git a/kig/filters/tests/intersectandasymptotestest.kig b/kig/filters/tests/intersectandasymptotestest.kig new file mode 100644 index 00000000..d521e241 --- /dev/null +++ b/kig/filters/tests/intersectandasymptotestest.kig @@ -0,0 +1,111 @@ +<!DOCTYPE KigDocument> +<KigDocument Version="0.6.0" > + <CoordinateSystem>Euclidean</CoordinateSystem> + <Objects> + <Data internal="true" type="double" id="1" >-6.73585</Data> + <Data internal="true" type="double" id="2" >-1.0566</Data> + <Data internal="true" type="double" id="3" >-2.92721</Data> + <Data internal="true" type="double" id="4" >2.22373</Data> + <Data internal="true" type="double" id="5" >-5.45912</Data> + <Data internal="true" type="double" id="6" >4.40252</Data> + <Data internal="true" type="double" id="7" >-6.16352</Data> + <Data internal="true" type="double" id="8" >2.11321</Data> + <Data internal="true" type="int" id="9" >-1</Data> + <Data internal="true" type="int" id="10" >1</Data> + <Data internal="true" type="double" id="11" >-12.1069</Data> + <Data internal="true" type="double" id="12" >1.0566</Data> + <Data internal="true" type="double" id="13" >2.50943</Data> + <Data internal="true" type="double" id="14" >-1.10063</Data> + <Data internal="true" type="int" id="15" >1</Data> + <Data internal="true" type="int" id="16" >-1</Data> + <Data internal="true" type="double" id="17" >1.84906</Data> + <Data internal="true" type="double" id="18" >1.45283</Data> + <Data internal="true" type="double" id="19" >7.04403</Data> + <Data internal="true" type="double" id="20" >1.49686</Data> + <Data internal="true" type="double" id="21" >6.42767</Data> + <Data internal="true" type="double" id="22" >-0.440252</Data> + <Data internal="true" type="int" id="23" >1</Data> + <Data internal="true" type="int" id="24" >-1</Data> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="25" color="#0000ff" > + <Parent id="1" /> + <Parent id="2" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="26" color="#0000ff" > + <Parent id="3" /> + <Parent id="4" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="27" color="#0000ff" > + <Parent id="5" /> + <Parent id="6" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="28" color="#0000ff" > + <Parent id="7" /> + <Parent id="8" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="29" color="#0000ff" > + <Parent id="11" /> + <Parent id="12" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="30" color="#0000ff" > + <Parent id="13" /> + <Parent id="14" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="31" color="#0000ff" > + <Parent id="17" /> + <Parent id="18" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="32" color="#0000ff" > + <Parent id="19" /> + <Parent id="20" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="33" color="#0000ff" > + <Parent id="21" /> + <Parent id="22" /> + </Object> + <Object width="-1" internal="false" shown="true" type="CircleBCP" id="34" color="#0000ff" > + <Parent id="25" /> + <Parent id="26" /> + </Object> + <Object width="-1" internal="false" shown="true" type="SegmentAB" id="35" color="#0000ff" > + <Parent id="27" /> + <Parent id="28" /> + </Object> + <Object width="-1" internal="false" shown="true" type="LineAB" id="36" color="#0000ff" > + <Parent id="29" /> + <Parent id="30" /> + </Object> + <Object width="-1" internal="false" shown="true" type="HyperbolaBFFP" id="37" color="#0000ff" > + <Parent id="31" /> + <Parent id="32" /> + <Parent id="33" /> + </Object> + <Object width="-1" internal="false" shown="true" type="ConicLineIntersection" id="38" color="#0000ff" > + <Parent id="34" /> + <Parent id="35" /> + <Parent id="9" /> + </Object> + <Object width="-1" internal="false" shown="true" type="ConicLineIntersection" id="39" color="#0000ff" > + <Parent id="34" /> + <Parent id="35" /> + <Parent id="10" /> + </Object> + <Object width="-1" internal="false" shown="true" type="ConicLineIntersection" id="40" color="#0000ff" > + <Parent id="34" /> + <Parent id="36" /> + <Parent id="15" /> + </Object> + <Object width="-1" internal="false" shown="true" type="ConicLineIntersection" id="41" color="#0000ff" > + <Parent id="34" /> + <Parent id="36" /> + <Parent id="16" /> + </Object> + <Object width="-1" internal="false" shown="true" type="ConicAsymptote" id="42" color="#0000ff" > + <Parent id="37" /> + <Parent id="23" /> + </Object> + <Object width="-1" internal="false" shown="true" type="ConicAsymptote" id="43" color="#0000ff" > + <Parent id="37" /> + <Parent id="24" /> + </Object> + </Objects> +</KigDocument> diff --git a/kig/filters/tests/intersection-test.seg b/kig/filters/tests/intersection-test.seg Binary files differnew file mode 100644 index 00000000..44d69b70 --- /dev/null +++ b/kig/filters/tests/intersection-test.seg diff --git a/kig/filters/tests/invisibleLine.FIG b/kig/filters/tests/invisibleLine.FIG new file mode 100644 index 00000000..5eeb6ae3 --- /dev/null +++ b/kig/filters/tests/invisibleLine.FIG @@ -0,0 +1,32 @@ +FIGURE CabriII vers. MS-Windows 1.0
+
+Window center x: 0 y: 0 Window size x: 16.2983333333333 y: 9.68375
+
+1: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, I, nSt
+Val: 0 0
+
+2: Axes, 1, CN:1, VN:3
+Gr, W, t, DS:1 1, GT:0, I, nSt
+Const: 1, Val: 1 0, 0 1
+
+3: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: -5.159375 -0.608541666666667
+
+4: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: -2.91041666666667 3.54541666666667
+
+5: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: 2.19604166666667 0.79375
+
+6: Line, 0, CN:2, VN:2
+V, W, t, DS:1 1, GT:0, I, nSt
+Const: 5 3
+
+7: Perp, 0, CN:2, VN:2
+V, W, t, DS:1 1, GT:0, V, nSt
+Const: 4 6
+
diff --git a/kig/filters/tests/lineBTP.FIG b/kig/filters/tests/lineBTP.FIG new file mode 100644 index 00000000..7926b787 --- /dev/null +++ b/kig/filters/tests/lineBTP.FIG @@ -0,0 +1,24 @@ +FIGURE CabriII vers. MS-Windows 1.0
+
+Window center x: 0 y: 0 Window size x: 16.2983333333333 y: 9.68375
+
+1: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, I, nSt
+Val: 0 0
+
+2: Axes, 1, CN:1, VN:3
+Gr, W, t, DS:1 1, GT:0, I, nSt
+Const: 1, Val: 1 0, 0 1
+
+3: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: -5.318125 2.16958333333333
+
+4: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: -1.29645833333333 3.01625
+
+5: Line, 0, CN:2, VN:2
+V, W, t, DS:1 1, GT:0, V, nSt
+Const: 3 4
+
diff --git a/kig/filters/tests/linebyonepoint.FIG b/kig/filters/tests/linebyonepoint.FIG new file mode 100644 index 00000000..4cdac84e --- /dev/null +++ b/kig/filters/tests/linebyonepoint.FIG @@ -0,0 +1,20 @@ +FIGURE CabriII vers. MS-Windows 1.0
+
+Window center x: 0 y: 0 Window size x: 16.2983333333333 y: 9.68375
+
+1: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, I, nSt
+Val: 0 0
+
+2: Axes, 1, CN:1, VN:3
+Gr, W, t, DS:1 1, GT:0, I, nSt
+Const: 1, Val: 1 0, 0 1
+
+3: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: -4.683125 1.93145833333333
+
+4: Line, 0, CN:1, VN:2
+V, W, t, DS:1 1, GT:0, V, nSt
+Const: 3, Val: 0.972745500001038 0.231875380814201
+
diff --git a/kig/filters/tests/locus.fgeo b/kig/filters/tests/locus.fgeo new file mode 100644 index 00000000..a914068c --- /dev/null +++ b/kig/filters/tests/locus.fgeo @@ -0,0 +1,2 @@ +<?xml version="1.0"?> +<drgenius><drgeo name="Figuur 1" scale="30.000000" origin_x="0.000000" origin_y="0.000000" grid="False"><point id="8293DE0" type="Free" color="Red" thickness="Dashed" style="Cross" filled="False" masked="False" name=""><x>-1.666666</x><y>3.216667</y></point><point id="8306C70" type="Free" color="Red" thickness="Dashed" style="Cross" filled="False" masked="False" name=""><x>-2.766667</x><y>0.550000</y></point><circle id="8307268" type="2pts" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""><parent ref="8293DE0"/><parent ref="8306C70"/></circle><point id="8309098" type="On_curve" color="Red" thickness="Dashed" style="Cross" filled="False" masked="False" name=""><value>0.080691</value><parent ref="8307268"/></point><point id="830A920" type="Free" color="Red" thickness="Dashed" style="Cross" filled="False" masked="False" name=""><x>3.000000</x><y>3.016667</y></point><line id="8172970" type="2pts" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""><parent ref="8309098"/><parent ref="830A920"/></line><point id="8339730" type="Free" color="Red" thickness="Dashed" style="Cross" filled="False" masked="False" name=""><x>2.866667</x><y>-0.750000</y></point><line id="833AD18" type="2pts" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""><parent ref="8309098"/><parent ref="8339730"/></line><point id="8338A50" type="Free" color="Red" thickness="Dashed" style="Cross" filled="False" masked="False" name=""><x>2.600000</x><y>3.816667</y></point><line id="833BBB8" type="perpendicular" color="Black" thickness="Normal" style="Cross" filled="False" masked="False" name=""><parent ref="8338A50"/><parent ref="833AD18"/></line><point id="8342478" type="Intersection" color="Red" thickness="Dashed" style="Cross" filled="False" masked="False" name="" extra="0"><parent ref="8172970"/><parent ref="833BBB8"/></point><locus id="8345D60" type="None" color="Black" thickness="Dashed" style="Cross" filled="False" masked="False" name=""><parent ref="8309098"/><parent ref="8342478"/></locus></drgeo></drgenius> diff --git a/kig/filters/tests/lotsOfObjects.FIG b/kig/filters/tests/lotsOfObjects.FIG new file mode 100644 index 00000000..67b24164 --- /dev/null +++ b/kig/filters/tests/lotsOfObjects.FIG @@ -0,0 +1,148 @@ +FIGURE CabriII vers. MS-Windows 1.0
+
+Window center x: 0 y: 0 Window size x: 16.2983333333333 y: 9.68375
+
+1: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, I, nSt
+Val: 0 0
+
+2: Axes, 1, CN:1, VN:3
+Gr, W, t, DS:1 1, GT:0, I, nSt
+Const: 1, Val: 1 0, 0 1
+
+3: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: -6.72041666666667 3.94229166666667
+
+4: Line, 0, CN:1, VN:2
+V, W, t, DS:1 1, GT:0, V, nSt
+Const: 3, Val: -0.0202661071897195 -0.999794621359494
+
+5: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: 0.582083333333333 2.35479166666667
+
+6: Line, 0, CN:2, VN:2
+V, W, t, DS:1 1, GT:0, V, nSt
+Const: 5 3
+
+7: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: -4.78895833333333 -0.661458333333333
+
+8: Seg, 0, CN:2, VN:0
+V, W, t, DS:1 1, GT:0, V, nSt
+Const: 5 7
+
+9: Ray, 0, CN:1, VN:2
+V, W, t, DS:1 1, GT:0, V, nSt
+Const: 7, Val: 0.965226371919943 -0.261415475728318
+
+10: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: -2.19604166666667 -2.56645833333333
+
+11: Ray, 0, CN:2, VN:2
+V, W, t, DS:1 1, GT:0, V, nSt
+Const: 7 10
+
+12: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:0, V, nSt
+Val: -0.687916666666667 -0.820208333333333
+
+13: Vec, 0, CN:2, VN:0
+P, W, t, DS:1 1, GT:0, V, nSt
+Const: 10 12
+
+14: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: 3.75708333333333 -0.661458333333333
+
+15: Cir, 0, CN:1, VN:2
+Bl, W, t, DS:1 1, GT:0, V, nSt
+Const: 14, Val: 2.32351774734393
+
+16: Cir, 0, CN:2, VN:2
+Bl, W, t, DS:1 1, GT:0, V, nSt
+Const: 14 5
+
+17: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: -4.70958333333333 2.19604166666667
+
+18: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: -6.19125 1.00541666666667
+
+19: Arc, 0, CN:3, VN:4
+R, W, t, DS:1 1, GT:0, V, nSt
+Const: 7 17 18
+
+20: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: -2.96333333333333 3.78354166666667
+
+21: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: -0.0264583333333333 4.1275
+
+22: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: 1.95791666666667 2.69875
+
+23: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: 0.0264583333333333 0.582083333333333
+
+24: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: -4.25979166666667 1.08479166666667
+
+25: Con, 1, CN:5, VN:6
+Bl, W, t, DS:1 1, GT:0, V, nSt
+Const: 20 21 22 23 24
+
+26: Perp, 0, CN:2, VN:2
+V, W, t, DS:1 1, GT:0, V, nSt
+Const: 14 11
+
+27: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: -5.37104166666667 -2.56645833333333
+
+28: Par, 0, CN:2, VN:2
+V, W, t, DS:1 1, GT:0, V, nSt
+Const: 27 11
+
+29: Mid, 0, CN:1, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Const: 13
+
+30: Mid, 0, CN:1, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Const: 8
+
+31: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: -5.97958333333333 3.254375
+
+32: Mid, 0, CN:2, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Const: 31 18
+
+33: PBiss, 0, CN:1, VN:2
+V, W, t, DS:1 1, GT:0, V, nSt
+Const: 8
+
+34: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: -4.55083333333333 -2.460625
+
+35: Pt, 0, CN:0, VN:1
+R, W, t, DS:1 1, GT:1, V, nSt
+Val: -3.889375 -1.71979166666667
+
+36: PBiss, 0, CN:2, VN:2
+V, W, t, DS:1 1, GT:0, V, nSt
+Const: 34 35
+
diff --git a/kig/filters/tests/python-script.kig b/kig/filters/tests/python-script.kig new file mode 100644 index 00000000..edac8b48 --- /dev/null +++ b/kig/filters/tests/python-script.kig @@ -0,0 +1,29 @@ +<!DOCTYPE KigDocument> +<KigDocument Version="0.5.1" > + <CoordinateSystem>Euclidean</CoordinateSystem> + <Objects> + <Data internal="true" type="double" id="1" >-3.93514</Data> + <Data internal="true" type="double" id="2" >2.97568</Data> + <Data internal="true" type="double" id="3" >-2.03636</Data> + <Data internal="true" type="double" id="4" >1.57224</Data> + <Data internal="true" type="string" id="5" >def calc( arg1, arg2 ): + return Point( ( arg1.coordinate() + arg2.coordinate() ) / 2 ) +</Data> + <Object width="-1" internal="false" shown="true" type="FixedPoint" id="6" color="#0000ff" > + <Parent id="1" /> + <Parent id="2" /> + </Object> + <Object width="-1" internal="false" shown="true" type="FixedPoint" id="7" color="#0000ff" > + <Parent id="3" /> + <Parent id="4" /> + </Object> + <Object width="-1" internal="true" shown="true" type="PythonCompileType" id="8" color="#0000ff" > + <Parent id="5" /> + </Object> + <Object width="-1" internal="false" shown="true" type="PythonExecuteType" id="9" color="#0000ff" > + <Parent id="8" /> + <Parent id="6" /> + <Parent id="7" /> + </Object> + </Objects> +</KigDocument> diff --git a/kig/filters/tests/radicallinestest.kig b/kig/filters/tests/radicallinestest.kig new file mode 100644 index 00000000..82b1e564 --- /dev/null +++ b/kig/filters/tests/radicallinestest.kig @@ -0,0 +1,85 @@ +<!DOCTYPE KigDocument> +<KigDocument Version="0.6.0" > + <CoordinateSystem>Euclidean</CoordinateSystem> + <Objects> + <Data internal="true" type="double" id="1" >-3.43396</Data> + <Data internal="true" type="double" id="2" >1.54088</Data> + <Data internal="true" type="double" id="3" >-1.10063</Data> + <Data internal="true" type="double" id="4" >2.01216</Data> + <Data internal="true" type="double" id="5" >-0.0880503</Data> + <Data internal="true" type="double" id="6" >-0.748428</Data> + <Data internal="true" type="double" id="7" >-2.28931</Data> + <Data internal="true" type="double" id="8" >0.968553</Data> + <Data internal="true" type="double" id="9" >-0.836478</Data> + <Data internal="true" type="double" id="10" >4.35849</Data> + <Data internal="true" type="int" id="11" >1</Data> + <Data internal="true" type="int" id="12" >1</Data> + <Data internal="true" type="int" id="13" >-1</Data> + <Data internal="true" type="int" id="14" >1</Data> + <Data internal="true" type="double" id="15" >5.50314</Data> + <Data internal="true" type="double" id="16" >-0.0440252</Data> + <Data internal="true" type="double" id="17" >1.01258</Data> + <Data internal="true" type="double" id="18" >1.80503</Data> + <Data internal="true" type="int" id="19" >1</Data> + <Data internal="true" type="int" id="20" >1</Data> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="21" color="#0000ff" > + <Parent id="1" /> + <Parent id="2" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="22" color="#0000ff" > + <Parent id="3" /> + <Parent id="4" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="23" color="#0000ff" > + <Parent id="5" /> + <Parent id="6" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="24" color="#0000ff" > + <Parent id="7" /> + <Parent id="8" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="25" color="#0000ff" > + <Parent id="9" /> + <Parent id="10" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="26" color="#0000ff" > + <Parent id="15" /> + <Parent id="16" /> + </Object> + <Object width="5" internal="false" shown="true" type="FixedPoint" id="27" color="#0000ff" > + <Parent id="17" /> + <Parent id="18" /> + </Object> + <Object width="-1" internal="false" shown="true" type="CircleBCP" id="28" color="#0000ff" > + <Parent id="21" /> + <Parent id="22" /> + </Object> + <Object width="-1" internal="false" shown="true" type="CircleBTP" id="29" color="#0000ff" > + <Parent id="23" /> + <Parent id="24" /> + <Parent id="25" /> + </Object> + <Object width="-1" internal="false" shown="true" type="CircleBCP" id="30" color="#0000ff" > + <Parent id="26" /> + <Parent id="27" /> + </Object> + <Object width="-1" internal="false" shown="true" type="ConicRadical" id="31" color="#0000ff" > + <Parent id="28" /> + <Parent id="29" /> + <Parent id="11" /> + <Parent id="12" /> + </Object> + <Object width="-1" internal="false" shown="true" type="ConicRadical" id="32" color="#0000ff" > + <Parent id="28" /> + <Parent id="29" /> + <Parent id="13" /> + <Parent id="14" /> + </Object> + <Object width="-1" internal="false" shown="true" type="ConicRadical" id="33" color="#0000ff" > + <Parent id="29" /> + <Parent id="30" /> + <Parent id="19" /> + <Parent id="20" /> + </Object> + </Objects> +</KigDocument> diff --git a/kig/filters/tests/stylestest.kig b/kig/filters/tests/stylestest.kig new file mode 100644 index 00000000..f9a91951 --- /dev/null +++ b/kig/filters/tests/stylestest.kig @@ -0,0 +1,27 @@ +<!DOCTYPE KigDocument> +<KigDocument Version="0.7.1" > + <CoordinateSystem>Euclidean</CoordinateSystem> + <Hierarchy> + <Data type="double" id="1" >-3.41511</Data> + <Data type="double" id="2" >-0.798177</Data> + <Data type="double" id="3" >2.53813</Data> + <Data type="double" id="4" >3.67781</Data> + <Object type="FixedPoint" id="5" > + <Parent id="1" /> + <Parent id="3" /> + </Object> + <Object type="FixedPoint" id="6" > + <Parent id="4" /> + <Parent id="2" /> + </Object> + <Object type="LineAB" id="7" > + <Parent id="5" /> + <Parent id="6" /> + </Object> + </Hierarchy> + <View> + <Draw width="-1" point-style="Round" style="DashLine" shown="true" color="#0000ff" object="7" /> + <Draw width="-1" point-style="Cross" style="SolidLine" shown="true" color="#0000ff" object="5" /> + <Draw width="-1" point-style="RectangularEmpty" style="SolidLine" shown="true" color="#0000ff" object="6" /> + </View> +</KigDocument> diff --git a/kig/filters/tests/test.kgeo b/kig/filters/tests/test.kgeo new file mode 100644 index 00000000..e0edaa9d --- /dev/null +++ b/kig/filters/tests/test.kgeo @@ -0,0 +1,145 @@ +[Colors] +Background=255,255,255 +Draw=0,0,0 + +[Main] +Axes=true +Grid=true +Number=17 +Numbers=true +Scaling=false +Version=KGeo 1.0.2 +XMax=11 +YMax=7 + +[Object 1] +Color=0,0,0 +Geo=1 +Parents= +Position=1 +QPointX=-3.4 +QPointY=1.3 +Size=3 + +[Object 10] +Color=0,0,0 +Geo=31 +Parents=8,9 +Size=1 + +[Object 11] +Color=0,0,0 +Geo=27 +Parents=5,7 +Size=1 + +[Object 12] +Color=0,0,0 +Geo=29 +Parents=9,5 +Size=1 + +[Object 13] +Color=0,0,0 +Geo=1 +Parents= +Position=1 +QPointX=3.8 +QPointY=-1.7 +Size=3 + +[Object 14] +Color=0,0,0 +Geo=32 +Parents=13,11 +Position=1 +QPointX=-0.2 +QPointY=-0.3 +Size=3 + +[Object 15] +Color=0,0,0 +Geo=1 +Parents= +Position=1 +QPointX=-7.4 +QPointY=7.8 +Size=3 + +[Object 16] +Color=0,0,0 +Geo=1 +Parents= +Position=1 +QPointX=-7.4 +QPointY=6.4 +Size=3 + +[Object 17] +Color=0,0,0 +Geo=9 +Parents=15,16 +Position=1 +QPointX=-7.4 +QPointY=5 +Size=3 + +[Object 2] +Color=0,0,0 +Geo=1 +Parents= +Position=1 +QPointX=-3.4 +QPointY=2.4 +Size=3 + +[Object 3] +Color=0,0,0 +Geo=3 +Parents=1,2 +Size=1 + +[Object 4] +Color=0,0,0 +Geo=4 +Parents=1,2 +Size=1 + +[Object 5] +Color=0,0,0 +Geo=1 +Parents= +Position=1 +QPointX=1.5 +QPointY=3.6 +Size=3 + +[Object 6] +Color=0,0,0 +Geo=2 +Parents=1,5 +Size=1 + +[Object 7] +Color=0,0,0 +Geo=1 +Parents= +Position=1 +QPointX=-2.5 +QPointY=5 +Size=3 + +[Object 8] +Color=0,0,0 +Geo=30 +Parents=6,7 +Size=1 + +[Object 9] +Color=0,0,0 +Geo=1 +Parents= +Position=1 +QPointX=-5.1 +QPointY=-4.1 +Size=3 diff --git a/kig/filters/tests/test.seg b/kig/filters/tests/test.seg Binary files differnew file mode 100644 index 00000000..d51c281d --- /dev/null +++ b/kig/filters/tests/test.seg diff --git a/kig/filters/tests/testalotofeverything.kig b/kig/filters/tests/testalotofeverything.kig new file mode 100644 index 00000000..1e7a5fcb --- /dev/null +++ b/kig/filters/tests/testalotofeverything.kig @@ -0,0 +1,174 @@ +<!DOCTYPE KigDocument> +<KigDocument Version="0.7.1" > + <CoordinateSystem>Euclidean</CoordinateSystem> + <Hierarchy> + <Data type="double" id="1" >4.20683</Data> + <Data type="double" id="2" >-1.93144</Data> + <Data type="double" id="3" >0.661451</Data> + <Data type="double" id="4" >-2.91039</Data> + <Data type="double" id="5" >0.694042</Data> + <Data type="double" id="6" >-2.38123</Data> + <Data type="double" id="7" >-2.27539</Data> + <Data type="double" id="8" >3.2808</Data> + <Data type="double" id="9" >-0.8202</Data> + <Data type="double" id="10" >0.24161</Data> + <Data type="double" id="11" >2.0671</Data> + <Data type="double" id="12" >5.39744</Data> + <Data type="double" id="13" >2.17293</Data> + <Data type="double" id="14" >5.39744</Data> + <Data type="double" id="15" >-1.66686</Data> + <Data type="double" id="16" >-0.0760034</Data> + <Data type="double" id="17" >3.09559</Data> + <Data type="double" id="18" >0.434012</Data> + <Data type="int" id="19" >1</Data> + <Data type="double" id="20" >-3.91579</Data> + <Data type="double" id="21" >6.42931</Data> + <Data type="double" id="22" >4.73936</Data> + <Data type="string" id="23" >%1</Data> + <Data type="double" id="24" >-1.34936</Data> + <Data type="point" id="25" > + <x>4.55079</x> + <y>3.67767</y> + </Data> + <Data type="double" id="26" >-1.76794</Data> + <Data type="hierarchy" id="27" > + <input requirement="point" id="1" /> + <input requirement="point" id="2" /> + <input requirement="double" id="3" /> + <intermediate action="calc" type="LineAB" id="4" > + <arg>1</arg> + <arg>2</arg> + </intermediate> + <result action="calc" type="ConstrainedPoint" id="5" > + <arg>3</arg> + <arg>4</arg> + </result> + </Data> + <Data type="double" id="28" >1.93481</Data> + <Data type="string" id="29" >def calc( arg1 ): + return Point( arg1.coordinate() + Coordinate( 1,.8))</Data> + <Object type="FixedPoint" id="30" > + <Parent id="3" /> + <Parent id="2" /> + </Object> + <Object type="FixedPoint" id="31" > + <Parent id="7" /> + <Parent id="9" /> + </Object> + <Object type="FixedPoint" id="32" > + <Parent id="15" /> + <Parent id="6" /> + </Object> + <Object type="FixedPoint" id="33" > + <Parent id="12" /> + <Parent id="16" /> + </Object> + <Object type="FixedPoint" id="34" > + <Parent id="17" /> + <Parent id="8" /> + </Object> + <Object type="FixedPoint" id="35" > + <Parent id="20" /> + <Parent id="11" /> + </Object> + <Object type="FixedPoint" id="36" > + <Parent id="1" /> + <Parent id="21" /> + </Object> + <Object type="FixedPoint" id="37" > + <Parent id="4" /> + <Parent id="22" /> + </Object> + <Object type="FixedPoint" id="38" > + <Parent id="24" /> + <Parent id="13" /> + </Object> + <Object type="FixedPoint" id="39" > + <Parent id="26" /> + <Parent id="18" /> + </Object> + <Object type="FixedPoint" id="40" > + <Parent id="14" /> + <Parent id="28" /> + </Object> + <Object type="PythonCompileType" id="41" > + <Parent id="29" /> + </Object> + <Object type="Vector" id="42" > + <Parent id="36" /> + <Parent id="34" /> + </Object> + <Object type="Vector" id="43" > + <Parent id="35" /> + <Parent id="38" /> + </Object> + <Object type="CircleBCP" id="44" > + <Parent id="33" /> + <Parent id="40" /> + </Object> + <Object type="PythonExecuteType" id="45" > + <Parent id="41" /> + <Parent id="37" /> + </Object> + <Object type="LineByVector" id="46" > + <Parent id="43" /> + <Parent id="37" /> + </Object> + <Object type="VectorEquality" id="47" > + <Parent id="43" /> + <Parent id="42" /> + </Object> + <Object type="ConstrainedPoint" id="48" > + <Parent id="10" /> + <Parent id="44" /> + </Object> + <Object type="Locus" id="49" > + <Parent id="27" /> + <Parent id="44" /> + <Parent id="39" /> + <Parent id="5" /> + </Object> + <Property which="test-result" id="50" > + <Parent id="47" /> + </Property> + <Object type="LineAB" id="51" > + <Parent id="48" /> + <Parent id="39" /> + </Object> + <Object type="Similitude" id="52" > + <Parent id="49" /> + <Parent id="31" /> + <Parent id="32" /> + <Parent id="30" /> + </Object> + <Object type="Label" id="53" > + <Parent id="19" /> + <Parent id="25" /> + <Parent id="23" /> + <Parent id="50" /> + </Object> + </Hierarchy> + <View> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="35" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="39" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="48" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="51" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="53" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="30" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="43" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="31" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="38" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="40" /> + <Draw width="5" point-style="Round" style="SolidLine" shown="true" color="#ffff00" object="52" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="42" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="34" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="32" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="33" /> + <Draw width="7" point-style="Round" style="DotLine" shown="true" color="#a0a0a4" object="49" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="36" /> + <Draw width="7" point-style="Round" style="DashDotDotLine" shown="true" color="#00ff00" object="46" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="44" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="37" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="45" /> + </View> +</KigDocument> diff --git a/kig/filters/tests/testlocuswithinvalidpoints.kig b/kig/filters/tests/testlocuswithinvalidpoints.kig new file mode 100644 index 00000000..6bb3f57d --- /dev/null +++ b/kig/filters/tests/testlocuswithinvalidpoints.kig @@ -0,0 +1,177 @@ +<!DOCTYPE KigDocument> +<KigDocument Version="0.7.1" > + <CoordinateSystem>Euclidean</CoordinateSystem> + <Hierarchy> + <Data type="int" id="1" >0</Data> + <Data type="hierarchy" id="2" > + <input requirement="point" id="1" /> + <input requirement="double" id="2" /> + <input requirement="circle" id="3" /> + <input requirement="int" id="4" /> + <intermediate action="calc" type="CircleBPR" id="5" > + <arg>1</arg> + <arg>2</arg> + </intermediate> + <intermediate action="calc" type="CircleCircleIntersection" id="6" > + <arg>3</arg> + <arg>5</arg> + <arg>4</arg> + </intermediate> + <intermediate action="calc" type="SegmentAB" id="7" > + <arg>6</arg> + <arg>1</arg> + </intermediate> + <result action="fetch-property" property="mid-point" id="8" > + <arg>7</arg> + </result> + </Data> + <Data type="double" id="3" >-1.40273</Data> + <Data type="double" id="4" >-0.608162</Data> + <Data type="int" id="5" >1</Data> + <Data type="int" id="6" >0</Data> + <Data type="double" id="7" >1.74813</Data> + <Data type="double" id="8" >-8.26962</Data> + <Data type="double" id="9" >-2.18736</Data> + <Data type="double" id="10" >-9.17811</Data> + <Data type="double" id="11" >-8.44019</Data> + <Data type="double" id="12" >-9.66304</Data> + <Data type="string" id="13" >Q</Data> + <Data type="double" id="14" >3.24779</Data> + <Data type="double" id="15" >-0.401053</Data> + <Data type="double" id="16" >3.41264</Data> + <Data type="double" id="17" >-0.327406</Data> + <Data type="double" id="18" >2.46458</Data> + <Data type="double" id="19" >-9.94648</Data> + <Data type="string" id="20" >P</Data> + <Data type="double" id="21" >-0.816147</Data> + <Data type="int" id="22" >-1</Data> + <Data type="double" id="23" >-5.54805</Data> + <Data type="double" id="24" >0.177996</Data> + <Object type="FixedPoint" id="25" > + <Parent id="8" /> + <Parent id="7" /> + </Object> + <Object type="FixedPoint" id="26" > + <Parent id="12" /> + <Parent id="14" /> + </Object> + <Object type="FixedPoint" id="27" > + <Parent id="10" /> + <Parent id="15" /> + </Object> + <Object type="FixedPoint" id="28" > + <Parent id="11" /> + <Parent id="16" /> + </Object> + <Object type="FixedPoint" id="29" > + <Parent id="4" /> + <Parent id="18" /> + </Object> + <Object type="FixedPoint" id="30" > + <Parent id="19" /> + <Parent id="9" /> + </Object> + <Object type="FixedPoint" id="31" > + <Parent id="17" /> + <Parent id="21" /> + </Object> + <Object type="FixedPoint" id="32" > + <Parent id="23" /> + <Parent id="3" /> + </Object> + <Object type="SegmentAB" id="33" > + <Parent id="27" /> + <Parent id="25" /> + </Object> + <Object type="SegmentAB" id="34" > + <Parent id="26" /> + <Parent id="28" /> + </Object> + <Object type="SegmentAB" id="35" > + <Parent id="30" /> + <Parent id="32" /> + </Object> + <Property which="length" id="36" > + <Parent id="33" /> + </Property> + <Property which="length" id="37" > + <Parent id="34" /> + </Property> + <Property which="length" id="38" > + <Parent id="35" /> + </Property> + <Object type="CircleBPR" id="39" > + <Parent id="29" /> + <Parent id="36" /> + </Object> + <Object type="CircleBPR" id="40" > + <Parent id="31" /> + <Parent id="37" /> + </Object> + <Object type="ConstrainedPoint" id="41" > + <Parent id="24" /> + <Parent id="40" /> + </Object> + <Object type="Locus" id="42" > + <Parent id="2" /> + <Parent id="40" /> + <Parent id="38" /> + <Parent id="39" /> + <Parent id="5" /> + </Object> + <Object type="CircleBPR" id="43" > + <Parent id="41" /> + <Parent id="38" /> + </Object> + <Object type="Label" id="44" > + <Parent id="1" /> + <Parent id="41" /> + <Parent id="20" /> + </Object> + <Object type="CircleCircleIntersection" id="45" > + <Parent id="39" /> + <Parent id="43" /> + <Parent id="22" /> + </Object> + <Object type="CircleCircleIntersection" id="46" > + <Parent id="39" /> + <Parent id="43" /> + <Parent id="5" /> + </Object> + <Object type="SegmentAB" id="47" > + <Parent id="46" /> + <Parent id="41" /> + </Object> + <Property which="mid-point" id="48" > + <Parent id="47" /> + </Property> + <Object type="Label" id="49" > + <Parent id="6" /> + <Parent id="48" /> + <Parent id="13" /> + </Object> + </Hierarchy> + <View> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="44" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="26" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="42" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="43" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="33" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="30" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="46" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="49" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="41" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="31" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="27" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="35" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="28" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="40" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="32" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="39" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="45" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="25" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="29" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="48" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="34" /> + </View> +</KigDocument> diff --git a/kig/filters/tests/testnames.kig b/kig/filters/tests/testnames.kig new file mode 100644 index 00000000..3fd10730 --- /dev/null +++ b/kig/filters/tests/testnames.kig @@ -0,0 +1,62 @@ +<!DOCTYPE KigDocument> +<KigDocument Version="0.7.1" > + <CoordinateSystem>Euclidean</CoordinateSystem> + <Hierarchy> + <Data type="int" id="1" >0</Data> + <Data type="double" id="2" >1.3131</Data> + <Data type="string" id="3" >%1</Data> + <Data type="double" id="4" >-4.04756</Data> + <Data type="double" id="5" >4.22311</Data> + <Data type="double" id="6" >0.661366</Data> + <Data type="int" id="7" >0</Data> + <Data type="double" id="8" >0.55381</Data> + <Data type="string" id="9" >%1</Data> + <Data type="string" id="10" >ab</Data> + <Data type="int" id="11" >0</Data> + <Data type="string" id="12" >%1</Data> + <Data type="string" id="13" >a</Data> + <Data type="string" id="14" >b</Data> + <Object type="FixedPoint" id="15" > + <Parent id="4" /> + <Parent id="2" /> + </Object> + <Object type="FixedPoint" id="16" > + <Parent id="6" /> + <Parent id="5" /> + </Object> + <Object type="Label" id="17" > + <Parent id="11" /> + <Parent id="15" /> + <Parent id="12" /> + <Parent id="14" /> + </Object> + <Object type="SegmentAB" id="18" > + <Parent id="15" /> + <Parent id="16" /> + </Object> + <Object type="Label" id="19" > + <Parent id="1" /> + <Parent id="16" /> + <Parent id="3" /> + <Parent id="13" /> + </Object> + <Object type="ConstrainedPoint" id="20" > + <Parent id="8" /> + <Parent id="18" /> + </Object> + <Object type="Label" id="21" > + <Parent id="7" /> + <Parent id="20" /> + <Parent id="9" /> + <Parent id="10" /> + </Object> + </Hierarchy> + <View> + <Draw width="-1" point-style="Round" namecalcer="none" style="SolidLine" shown="true" color="#0000ff" object="19" /> + <Draw width="-1" point-style="Round" namecalcer="none" style="SolidLine" shown="true" color="#0000ff" object="21" /> + <Draw width="-1" point-style="Round" namecalcer="10" style="SolidLine" shown="true" color="#0000ff" object="18" /> + <Draw width="-1" point-style="Round" namecalcer="13" style="SolidLine" shown="true" color="#0000ff" object="16" /> + <Draw width="-1" point-style="Round" namecalcer="14" style="SolidLine" shown="true" color="#0000ff" object="15" /> + <Draw width="-1" point-style="Round" namecalcer="none" style="SolidLine" shown="true" color="#0000ff" object="17" /> + </View> +</KigDocument> diff --git a/kig/filters/tests/testtest.kig b/kig/filters/tests/testtest.kig new file mode 100644 index 00000000..b232d227 --- /dev/null +++ b/kig/filters/tests/testtest.kig @@ -0,0 +1,66 @@ +<!DOCTYPE KigDocument> +<KigDocument Version="0.7.1" > + <CoordinateSystem>Euclidean</CoordinateSystem> + <Hierarchy> + <Data type="point" id="1" > + <x>0.446592</x> + <y>0.0161959</y> + </Data> + <Data type="double" id="2" >0.278897</Data> + <Data type="double" id="3" >-0.288971</Data> + <Data type="double" id="4" >0.278897</Data> + <Data type="double" id="5" >-2.2855</Data> + <Data type="double" id="6" >-2.92606</Data> + <Data type="double" id="7" >-3.91425</Data> + <Data type="double" id="8" >2.27542</Data> + <Data type="int" id="9" >1</Data> + <Data type="double" id="10" >0.157621</Data> + <Data type="string" id="11" >%1</Data> + <Object type="FixedPoint" id="12" > + <Parent id="3" /> + <Parent id="4" /> + </Object> + <Object type="FixedPoint" id="13" > + <Parent id="5" /> + <Parent id="6" /> + </Object> + <Object type="FixedPoint" id="14" > + <Parent id="7" /> + <Parent id="2" /> + </Object> + <Object type="FixedPoint" id="15" > + <Parent id="10" /> + <Parent id="8" /> + </Object> + <Object type="LineAB" id="16" > + <Parent id="12" /> + <Parent id="13" /> + </Object> + <Object type="SegmentAB" id="17" > + <Parent id="15" /> + <Parent id="14" /> + </Object> + <Object type="AreOrthogonal" id="18" > + <Parent id="16" /> + <Parent id="17" /> + </Object> + <Property which="test-result" id="19" > + <Parent id="18" /> + </Property> + <Object type="Label" id="20" > + <Parent id="9" /> + <Parent id="1" /> + <Parent id="11" /> + <Parent id="19" /> + </Object> + </Hierarchy> + <View> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="14" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="12" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="16" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="13" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="20" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="15" /> + <Draw width="-1" point-style="Round" style="SolidLine" shown="true" color="#0000ff" object="17" /> + </View> +</KigDocument> diff --git a/kig/filters/tests/transform-test.seg b/kig/filters/tests/transform-test.seg Binary files differnew file mode 100644 index 00000000..e2dc7219 --- /dev/null +++ b/kig/filters/tests/transform-test.seg diff --git a/kig/filters/tests/transformlocustest.kig b/kig/filters/tests/transformlocustest.kig new file mode 100644 index 00000000..f9c8500c --- /dev/null +++ b/kig/filters/tests/transformlocustest.kig @@ -0,0 +1,82 @@ +<!DOCTYPE KigDocument> +<KigDocument Version="0.4.0" > + <CoordinateSystem>Euclidean</CoordinateSystem> + <Objects> + <Data type="double" id="1" >-4.86</Data> + <Data type="double" id="2" >0</Data> + <Data type="double" id="3" >-6.93</Data> + <Data type="double" id="4" >0.42</Data> + <Data type="double" id="5" >0.346818</Data> + <Data type="double" id="6" >0.12</Data> + <Data type="double" id="7" >4.14</Data> + <Data type="double" id="8" >0.698969</Data> + <Data type="hierarchy" id="9" > + <input requirement="point" id="1" /> + <input requirement="point" id="2" /> + <input requirement="double" id="3" /> + <intermediate action="calc" type="LineAB" id="4" > + <arg>1</arg> + <arg>2</arg> + </intermediate> + <result action="calc" type="ConstrainedPoint" id="5" > + <arg>3</arg> + <arg>4</arg> + </result> + </Data> + <Data type="double" id="10" >0.15</Data> + <Data type="double" id="11" >0</Data> + <Data type="double" id="12" >3.63</Data> + <Data type="double" id="13" >-0.12</Data> + <Object width="-1" shown="true" type="FixedPoint" id="14" color="#0000ff" > + <Parent id="1" /> + <Parent id="2" /> + </Object> + <Object width="-1" shown="true" type="FixedPoint" id="15" color="#0000ff" > + <Parent id="3" /> + <Parent id="4" /> + </Object> + <Object width="-1" shown="true" type="FixedPoint" id="16" color="#0000ff" > + <Parent id="6" /> + <Parent id="7" /> + </Object> + <Object width="-1" shown="true" type="FixedPoint" id="17" color="#0000ff" > + <Parent id="10" /> + <Parent id="11" /> + </Object> + <Object width="-1" shown="true" type="FixedPoint" id="18" color="#0000ff" > + <Parent id="12" /> + <Parent id="13" /> + </Object> + <Object width="-1" shown="true" type="CircleBCP" id="19" color="#0000ff" > + <Parent id="14" /> + <Parent id="15" /> + </Object> + <Object width="-1" shown="true" type="SegmentAB" id="20" color="#0000ff" > + <Parent id="17" /> + <Parent id="18" /> + </Object> + <Object width="-1" shown="true" type="ConstrainedPoint" id="21" color="#0000ff" > + <Parent id="5" /> + <Parent id="19" /> + </Object> + <Object width="-1" shown="true" type="Locus" id="22" color="#0000ff" > + <Parent id="19" /> + <Parent id="9" /> + <Parent id="16" /> + <Parent id="8" /> + </Object> + <Object width="-1" shown="true" type="LineAB" id="23" color="#0000ff" > + <Parent id="21" /> + <Parent id="16" /> + </Object> + <Object width="-1" shown="true" type="ConstrainedPoint" id="24" color="#0000ff" > + <Parent id="8" /> + <Parent id="23" /> + </Object> + <Object width="-1" shown="true" type="ScalingOverLine" id="25" color="#0000ff" > + <Parent id="22" /> + <Parent id="23" /> + <Parent id="20" /> + </Object> + </Objects> +</KigDocument> diff --git a/kig/gencontributors.sh b/kig/gencontributors.sh new file mode 100644 index 00000000..f73ba22c --- /dev/null +++ b/kig/gencontributors.sh @@ -0,0 +1,9 @@ +tempfile=`tempfile` +cat kig/aboutdata.h | sed -ne '/tmp->addAuthor/,/setTranslator/p' | sed -e '$d' > $tempfile +tempfile2=`tempfile` +cat $tempfile | tr $'\n' " " | sed -e 's/"[[:blank:]]*"//g' | sed -e 's/I18N_NOOP([[:blank:]]*\("[^"]*"\)[[:blank:]]*)/\1/g' | sed -e 's/,[[:blank:]]*"/, "/g' | sed -e 's/^[[:blank:]]*//g' | sed -e 's/;[[:blank:]]*/\n/g' > $tempfile2 +cat $tempfile2 | grep addAuthor | sed -e 's/tmp->addAuthor(\([^,]*\), "[^"]*",\([^)]*\))/ $appinfo->addAuthor(\1,\2);/g' | sed -e 's/@/ AT /g' | sed -e 's/\./ DOT /g' +cat $tempfile2 | sed -e 's/[^(]*(\([^,]*\), \("[^"]*"\), \([^)]*\))/ $appinfo->addContributor( \1, \3, \2 );/g' + +rm $tempfile +rm $tempfile2 diff --git a/kig/generate_todo_inc.pl b/kig/generate_todo_inc.pl new file mode 100644 index 00000000..6999f217 --- /dev/null +++ b/kig/generate_todo_inc.pl @@ -0,0 +1,26 @@ +#! /usr/bin/env perl + +use warnings; + +print "<ul>\n"; +open( TODO, "<TODO" ); +while( <TODO> ) + { + if( /^\* (.*)$/ ) + { + print "</li></ul></li>\n\n" if( $inlist ); + print "<li><span style=\"font-weight:bold\">$1</span><br/>\n"; + $inlist = 0; + $initem = 0; + } + elsif( /^- (.*)$/ ) + { + if( $initem ) { print "</li>"; }; + print "<ul>\n" if( ! $inlist ); + $inlist = 1; + print "<li>$1\n"; + $initem = 1; + } + else { print unless /^$/; } + }; +print "</li></ul></li></ul>\n"; diff --git a/kig/icons/Makefile.am b/kig/icons/Makefile.am new file mode 100644 index 00000000..c98325ed --- /dev/null +++ b/kig/icons/Makefile.am @@ -0,0 +1,2 @@ +appicondir = $(kde_datadir)/kig/icons +appicon_ICON = AUTO diff --git a/kig/icons/hi16-action-kig_xfig.png b/kig/icons/hi16-action-kig_xfig.png Binary files differnew file mode 100644 index 00000000..48f00f63 --- /dev/null +++ b/kig/icons/hi16-action-kig_xfig.png diff --git a/kig/icons/hi22-action-angle.png b/kig/icons/hi22-action-angle.png Binary files differnew file mode 100644 index 00000000..ec31b4fb --- /dev/null +++ b/kig/icons/hi22-action-angle.png diff --git a/kig/icons/hi22-action-angle_bisector.png b/kig/icons/hi22-action-angle_bisector.png Binary files differnew file mode 100644 index 00000000..790d2985 --- /dev/null +++ b/kig/icons/hi22-action-angle_bisector.png diff --git a/kig/icons/hi22-action-angle_size.png b/kig/icons/hi22-action-angle_size.png Binary files differnew file mode 100644 index 00000000..4ee03ab5 --- /dev/null +++ b/kig/icons/hi22-action-angle_size.png diff --git a/kig/icons/hi22-action-arc.png b/kig/icons/hi22-action-arc.png Binary files differnew file mode 100644 index 00000000..61c161e9 --- /dev/null +++ b/kig/icons/hi22-action-arc.png diff --git a/kig/icons/hi22-action-arc_center.png b/kig/icons/hi22-action-arc_center.png Binary files differnew file mode 100644 index 00000000..f16a4b18 --- /dev/null +++ b/kig/icons/hi22-action-arc_center.png diff --git a/kig/icons/hi22-action-areaCircle.png b/kig/icons/hi22-action-areaCircle.png Binary files differnew file mode 100644 index 00000000..07a54ed2 --- /dev/null +++ b/kig/icons/hi22-action-areaCircle.png diff --git a/kig/icons/hi22-action-attacher.png b/kig/icons/hi22-action-attacher.png Binary files differnew file mode 100644 index 00000000..ef9b1814 --- /dev/null +++ b/kig/icons/hi22-action-attacher.png diff --git a/kig/icons/hi22-action-baseCircle.png b/kig/icons/hi22-action-baseCircle.png Binary files differnew file mode 100644 index 00000000..20692aae --- /dev/null +++ b/kig/icons/hi22-action-baseCircle.png diff --git a/kig/icons/hi22-action-bisection.png b/kig/icons/hi22-action-bisection.png Binary files differnew file mode 100644 index 00000000..406dc2d8 --- /dev/null +++ b/kig/icons/hi22-action-bisection.png diff --git a/kig/icons/hi22-action-centerofcurvature.png b/kig/icons/hi22-action-centerofcurvature.png Binary files differnew file mode 100644 index 00000000..1cf73f96 --- /dev/null +++ b/kig/icons/hi22-action-centerofcurvature.png diff --git a/kig/icons/hi22-action-centralsymmetry.png b/kig/icons/hi22-action-centralsymmetry.png Binary files differnew file mode 100644 index 00000000..e2d4f635 --- /dev/null +++ b/kig/icons/hi22-action-centralsymmetry.png diff --git a/kig/icons/hi22-action-circlebcl.png b/kig/icons/hi22-action-circlebcl.png Binary files differnew file mode 100644 index 00000000..71b2e663 --- /dev/null +++ b/kig/icons/hi22-action-circlebcl.png diff --git a/kig/icons/hi22-action-circlebcp.png b/kig/icons/hi22-action-circlebcp.png Binary files differnew file mode 100644 index 00000000..7697c2bc --- /dev/null +++ b/kig/icons/hi22-action-circlebcp.png diff --git a/kig/icons/hi22-action-circlebpd.png b/kig/icons/hi22-action-circlebpd.png Binary files differnew file mode 100644 index 00000000..d36fadb6 --- /dev/null +++ b/kig/icons/hi22-action-circlebpd.png diff --git a/kig/icons/hi22-action-circlebps.png b/kig/icons/hi22-action-circlebps.png Binary files differnew file mode 100644 index 00000000..0b05d686 --- /dev/null +++ b/kig/icons/hi22-action-circlebps.png diff --git a/kig/icons/hi22-action-circlebtp.png b/kig/icons/hi22-action-circlebtp.png Binary files differnew file mode 100644 index 00000000..a0f124f0 --- /dev/null +++ b/kig/icons/hi22-action-circlebtp.png diff --git a/kig/icons/hi22-action-circlelineintersection.png b/kig/icons/hi22-action-circlelineintersection.png Binary files differnew file mode 100644 index 00000000..158d1115 --- /dev/null +++ b/kig/icons/hi22-action-circlelineintersection.png diff --git a/kig/icons/hi22-action-circumference.png b/kig/icons/hi22-action-circumference.png Binary files differnew file mode 100644 index 00000000..6b043838 --- /dev/null +++ b/kig/icons/hi22-action-circumference.png diff --git a/kig/icons/hi22-action-conicasymptotes.png b/kig/icons/hi22-action-conicasymptotes.png Binary files differnew file mode 100644 index 00000000..c86d3590 --- /dev/null +++ b/kig/icons/hi22-action-conicasymptotes.png diff --git a/kig/icons/hi22-action-conicb5p.png b/kig/icons/hi22-action-conicb5p.png Binary files differnew file mode 100644 index 00000000..90c9dd2f --- /dev/null +++ b/kig/icons/hi22-action-conicb5p.png diff --git a/kig/icons/hi22-action-coniclineintersection.png b/kig/icons/hi22-action-coniclineintersection.png Binary files differnew file mode 100644 index 00000000..94f9b5fd --- /dev/null +++ b/kig/icons/hi22-action-coniclineintersection.png diff --git a/kig/icons/hi22-action-conicsradicalline.png b/kig/icons/hi22-action-conicsradicalline.png Binary files differnew file mode 100644 index 00000000..7b145143 --- /dev/null +++ b/kig/icons/hi22-action-conicsradicalline.png diff --git a/kig/icons/hi22-action-convexhull.png b/kig/icons/hi22-action-convexhull.png Binary files differnew file mode 100644 index 00000000..f75978f7 --- /dev/null +++ b/kig/icons/hi22-action-convexhull.png diff --git a/kig/icons/hi22-action-curvelineintersection.png b/kig/icons/hi22-action-curvelineintersection.png Binary files differnew file mode 100644 index 00000000..259f8c0c --- /dev/null +++ b/kig/icons/hi22-action-curvelineintersection.png diff --git a/kig/icons/hi22-action-directrix.png b/kig/icons/hi22-action-directrix.png Binary files differnew file mode 100644 index 00000000..f944ba62 --- /dev/null +++ b/kig/icons/hi22-action-directrix.png diff --git a/kig/icons/hi22-action-distance.png b/kig/icons/hi22-action-distance.png Binary files differnew file mode 100644 index 00000000..f6b7cd6e --- /dev/null +++ b/kig/icons/hi22-action-distance.png diff --git a/kig/icons/hi22-action-ellipsebffp.png b/kig/icons/hi22-action-ellipsebffp.png Binary files differnew file mode 100644 index 00000000..93d38018 --- /dev/null +++ b/kig/icons/hi22-action-ellipsebffp.png diff --git a/kig/icons/hi22-action-en.png b/kig/icons/hi22-action-en.png Binary files differnew file mode 100644 index 00000000..435e3386 --- /dev/null +++ b/kig/icons/hi22-action-en.png diff --git a/kig/icons/hi22-action-equilateralhyperbolab4p.png b/kig/icons/hi22-action-equilateralhyperbolab4p.png Binary files differnew file mode 100644 index 00000000..1a30b464 --- /dev/null +++ b/kig/icons/hi22-action-equilateralhyperbolab4p.png diff --git a/kig/icons/hi22-action-equitriangle.png b/kig/icons/hi22-action-equitriangle.png Binary files differnew file mode 100644 index 00000000..76bd910a --- /dev/null +++ b/kig/icons/hi22-action-equitriangle.png diff --git a/kig/icons/hi22-action-genericaffinity.png b/kig/icons/hi22-action-genericaffinity.png Binary files differnew file mode 100644 index 00000000..41fb2ae2 --- /dev/null +++ b/kig/icons/hi22-action-genericaffinity.png diff --git a/kig/icons/hi22-action-genericprojectivity.png b/kig/icons/hi22-action-genericprojectivity.png Binary files differnew file mode 100644 index 00000000..f43a1815 --- /dev/null +++ b/kig/icons/hi22-action-genericprojectivity.png diff --git a/kig/icons/hi22-action-halflinebyvector.png b/kig/icons/hi22-action-halflinebyvector.png Binary files differnew file mode 100644 index 00000000..a1046e48 --- /dev/null +++ b/kig/icons/hi22-action-halflinebyvector.png diff --git a/kig/icons/hi22-action-harmonichomology.png b/kig/icons/hi22-action-harmonichomology.png Binary files differnew file mode 100644 index 00000000..25b15086 --- /dev/null +++ b/kig/icons/hi22-action-harmonichomology.png diff --git a/kig/icons/hi22-action-hexagonbcv.png b/kig/icons/hi22-action-hexagonbcv.png Binary files differnew file mode 100644 index 00000000..3f87bfa9 --- /dev/null +++ b/kig/icons/hi22-action-hexagonbcv.png diff --git a/kig/icons/hi22-action-hyperbolabffp.png b/kig/icons/hi22-action-hyperbolabffp.png Binary files differnew file mode 100644 index 00000000..5dfcb7e3 --- /dev/null +++ b/kig/icons/hi22-action-hyperbolabffp.png diff --git a/kig/icons/hi22-action-intersection.png b/kig/icons/hi22-action-intersection.png Binary files differnew file mode 100644 index 00000000..1e460abc --- /dev/null +++ b/kig/icons/hi22-action-intersection.png diff --git a/kig/icons/hi22-action-inversion.png b/kig/icons/hi22-action-inversion.png Binary files differnew file mode 100644 index 00000000..ab8e61d7 --- /dev/null +++ b/kig/icons/hi22-action-inversion.png diff --git a/kig/icons/hi22-action-kig_polygon.png b/kig/icons/hi22-action-kig_polygon.png Binary files differnew file mode 100644 index 00000000..67c9f102 --- /dev/null +++ b/kig/icons/hi22-action-kig_polygon.png diff --git a/kig/icons/hi22-action-kig_text.png b/kig/icons/hi22-action-kig_text.png Binary files differnew file mode 100644 index 00000000..ff32a431 --- /dev/null +++ b/kig/icons/hi22-action-kig_text.png diff --git a/kig/icons/hi22-action-line.png b/kig/icons/hi22-action-line.png Binary files differnew file mode 100644 index 00000000..867cba2f --- /dev/null +++ b/kig/icons/hi22-action-line.png diff --git a/kig/icons/hi22-action-linebyvector.png b/kig/icons/hi22-action-linebyvector.png Binary files differnew file mode 100644 index 00000000..34671d5f --- /dev/null +++ b/kig/icons/hi22-action-linebyvector.png diff --git a/kig/icons/hi22-action-locus.png b/kig/icons/hi22-action-locus.png Binary files differnew file mode 100644 index 00000000..f5759d4a --- /dev/null +++ b/kig/icons/hi22-action-locus.png diff --git a/kig/icons/hi22-action-mirrorpoint.png b/kig/icons/hi22-action-mirrorpoint.png Binary files differnew file mode 100644 index 00000000..80572a73 --- /dev/null +++ b/kig/icons/hi22-action-mirrorpoint.png diff --git a/kig/icons/hi22-action-move.png b/kig/icons/hi22-action-move.png Binary files differnew file mode 100644 index 00000000..24820ff8 --- /dev/null +++ b/kig/icons/hi22-action-move.png diff --git a/kig/icons/hi22-action-paint.png b/kig/icons/hi22-action-paint.png Binary files differnew file mode 100644 index 00000000..3114b729 --- /dev/null +++ b/kig/icons/hi22-action-paint.png diff --git a/kig/icons/hi22-action-parabolabtp.png b/kig/icons/hi22-action-parabolabtp.png Binary files differnew file mode 100644 index 00000000..502ef8fa --- /dev/null +++ b/kig/icons/hi22-action-parabolabtp.png diff --git a/kig/icons/hi22-action-parallel.png b/kig/icons/hi22-action-parallel.png Binary files differnew file mode 100644 index 00000000..81ee7bf9 --- /dev/null +++ b/kig/icons/hi22-action-parallel.png diff --git a/kig/icons/hi22-action-perpendicular.png b/kig/icons/hi22-action-perpendicular.png Binary files differnew file mode 100644 index 00000000..ad8643dd --- /dev/null +++ b/kig/icons/hi22-action-perpendicular.png diff --git a/kig/icons/hi22-action-point.png b/kig/icons/hi22-action-point.png Binary files differnew file mode 100644 index 00000000..20429af9 --- /dev/null +++ b/kig/icons/hi22-action-point.png diff --git a/kig/icons/hi22-action-pointOnLine.png b/kig/icons/hi22-action-pointOnLine.png Binary files differnew file mode 100644 index 00000000..c7928747 --- /dev/null +++ b/kig/icons/hi22-action-pointOnLine.png diff --git a/kig/icons/hi22-action-pointxy.png b/kig/icons/hi22-action-pointxy.png Binary files differnew file mode 100644 index 00000000..779e00f1 --- /dev/null +++ b/kig/icons/hi22-action-pointxy.png diff --git a/kig/icons/hi22-action-polygonsides.png b/kig/icons/hi22-action-polygonsides.png Binary files differnew file mode 100644 index 00000000..279ba34a --- /dev/null +++ b/kig/icons/hi22-action-polygonsides.png diff --git a/kig/icons/hi22-action-polygonvertices.png b/kig/icons/hi22-action-polygonvertices.png Binary files differnew file mode 100644 index 00000000..f13d1b52 --- /dev/null +++ b/kig/icons/hi22-action-polygonvertices.png diff --git a/kig/icons/hi22-action-python.png b/kig/icons/hi22-action-python.png Binary files differnew file mode 100644 index 00000000..32c621e9 --- /dev/null +++ b/kig/icons/hi22-action-python.png diff --git a/kig/icons/hi22-action-radicalline.png b/kig/icons/hi22-action-radicalline.png Binary files differnew file mode 100644 index 00000000..8ba5cfdb --- /dev/null +++ b/kig/icons/hi22-action-radicalline.png diff --git a/kig/icons/hi22-action-ray.png b/kig/icons/hi22-action-ray.png Binary files differnew file mode 100644 index 00000000..7efa138d --- /dev/null +++ b/kig/icons/hi22-action-ray.png diff --git a/kig/icons/hi22-action-rotation.png b/kig/icons/hi22-action-rotation.png Binary files differnew file mode 100644 index 00000000..8eb81599 --- /dev/null +++ b/kig/icons/hi22-action-rotation.png diff --git a/kig/icons/hi22-action-scale.png b/kig/icons/hi22-action-scale.png Binary files differnew file mode 100644 index 00000000..7196ed73 --- /dev/null +++ b/kig/icons/hi22-action-scale.png diff --git a/kig/icons/hi22-action-segment.png b/kig/icons/hi22-action-segment.png Binary files differnew file mode 100644 index 00000000..e0510b1a --- /dev/null +++ b/kig/icons/hi22-action-segment.png diff --git a/kig/icons/hi22-action-segment_midpoint.png b/kig/icons/hi22-action-segment_midpoint.png Binary files differnew file mode 100644 index 00000000..475ca7ae --- /dev/null +++ b/kig/icons/hi22-action-segment_midpoint.png diff --git a/kig/icons/hi22-action-segmentaxis.png b/kig/icons/hi22-action-segmentaxis.png Binary files differnew file mode 100644 index 00000000..15b4d6fc --- /dev/null +++ b/kig/icons/hi22-action-segmentaxis.png diff --git a/kig/icons/hi22-action-similitude.png b/kig/icons/hi22-action-similitude.png Binary files differnew file mode 100644 index 00000000..79c4116f --- /dev/null +++ b/kig/icons/hi22-action-similitude.png diff --git a/kig/icons/hi22-action-sizer.png b/kig/icons/hi22-action-sizer.png Binary files differnew file mode 100644 index 00000000..92b1b029 --- /dev/null +++ b/kig/icons/hi22-action-sizer.png diff --git a/kig/icons/hi22-action-slope.png b/kig/icons/hi22-action-slope.png Binary files differnew file mode 100644 index 00000000..66ffe77c --- /dev/null +++ b/kig/icons/hi22-action-slope.png diff --git a/kig/icons/hi22-action-square.png b/kig/icons/hi22-action-square.png Binary files differnew file mode 100644 index 00000000..251d5542 --- /dev/null +++ b/kig/icons/hi22-action-square.png diff --git a/kig/icons/hi22-action-stretch.png b/kig/icons/hi22-action-stretch.png Binary files differnew file mode 100644 index 00000000..250ecaae --- /dev/null +++ b/kig/icons/hi22-action-stretch.png diff --git a/kig/icons/hi22-action-tangent.png b/kig/icons/hi22-action-tangent.png Binary files differnew file mode 100644 index 00000000..dce7d6ea --- /dev/null +++ b/kig/icons/hi22-action-tangent.png diff --git a/kig/icons/hi22-action-test.png b/kig/icons/hi22-action-test.png Binary files differnew file mode 100644 index 00000000..252e4e68 --- /dev/null +++ b/kig/icons/hi22-action-test.png diff --git a/kig/icons/hi22-action-testcollinear.png b/kig/icons/hi22-action-testcollinear.png Binary files differnew file mode 100644 index 00000000..02b03978 --- /dev/null +++ b/kig/icons/hi22-action-testcollinear.png diff --git a/kig/icons/hi22-action-testcontains.png b/kig/icons/hi22-action-testcontains.png Binary files differnew file mode 100644 index 00000000..4abac538 --- /dev/null +++ b/kig/icons/hi22-action-testcontains.png diff --git a/kig/icons/hi22-action-testdistance.png b/kig/icons/hi22-action-testdistance.png Binary files differnew file mode 100644 index 00000000..e32c4353 --- /dev/null +++ b/kig/icons/hi22-action-testdistance.png diff --git a/kig/icons/hi22-action-testorthogonal.png b/kig/icons/hi22-action-testorthogonal.png Binary files differnew file mode 100644 index 00000000..9d768baa --- /dev/null +++ b/kig/icons/hi22-action-testorthogonal.png diff --git a/kig/icons/hi22-action-testparallel.png b/kig/icons/hi22-action-testparallel.png Binary files differnew file mode 100644 index 00000000..73fe257f --- /dev/null +++ b/kig/icons/hi22-action-testparallel.png diff --git a/kig/icons/hi22-action-translation.png b/kig/icons/hi22-action-translation.png Binary files differnew file mode 100644 index 00000000..d5f670ab --- /dev/null +++ b/kig/icons/hi22-action-translation.png diff --git a/kig/icons/hi22-action-triangle.png b/kig/icons/hi22-action-triangle.png Binary files differnew file mode 100644 index 00000000..688a00ed --- /dev/null +++ b/kig/icons/hi22-action-triangle.png diff --git a/kig/icons/hi22-action-vector.png b/kig/icons/hi22-action-vector.png Binary files differnew file mode 100644 index 00000000..49ef6ac9 --- /dev/null +++ b/kig/icons/hi22-action-vector.png diff --git a/kig/icons/hi22-action-vectordifference.png b/kig/icons/hi22-action-vectordifference.png Binary files differnew file mode 100644 index 00000000..38cb1dd6 --- /dev/null +++ b/kig/icons/hi22-action-vectordifference.png diff --git a/kig/icons/hi22-action-vectorsum.png b/kig/icons/hi22-action-vectorsum.png Binary files differnew file mode 100644 index 00000000..95d38c98 --- /dev/null +++ b/kig/icons/hi22-action-vectorsum.png diff --git a/kig/icons/hi22-action-view_fit_to_page.png b/kig/icons/hi22-action-view_fit_to_page.png Binary files differnew file mode 100644 index 00000000..2e850c91 --- /dev/null +++ b/kig/icons/hi22-action-view_fit_to_page.png diff --git a/kig/icons/hi22-action-w.png b/kig/icons/hi22-action-w.png Binary files differnew file mode 100644 index 00000000..1c781187 --- /dev/null +++ b/kig/icons/hi22-action-w.png diff --git a/kig/icons/hi32-action-angle.png b/kig/icons/hi32-action-angle.png Binary files differnew file mode 100644 index 00000000..95eceec3 --- /dev/null +++ b/kig/icons/hi32-action-angle.png diff --git a/kig/icons/hi32-action-angle_bisector.png b/kig/icons/hi32-action-angle_bisector.png Binary files differnew file mode 100644 index 00000000..24461aab --- /dev/null +++ b/kig/icons/hi32-action-angle_bisector.png diff --git a/kig/icons/hi32-action-angle_size.png b/kig/icons/hi32-action-angle_size.png Binary files differnew file mode 100644 index 00000000..a76c84e2 --- /dev/null +++ b/kig/icons/hi32-action-angle_size.png diff --git a/kig/icons/hi32-action-arc.png b/kig/icons/hi32-action-arc.png Binary files differnew file mode 100644 index 00000000..0f7effd0 --- /dev/null +++ b/kig/icons/hi32-action-arc.png diff --git a/kig/icons/hi32-action-arc_center.png b/kig/icons/hi32-action-arc_center.png Binary files differnew file mode 100644 index 00000000..4a64172b --- /dev/null +++ b/kig/icons/hi32-action-arc_center.png diff --git a/kig/icons/hi32-action-areaCircle.png b/kig/icons/hi32-action-areaCircle.png Binary files differnew file mode 100644 index 00000000..4908ca91 --- /dev/null +++ b/kig/icons/hi32-action-areaCircle.png diff --git a/kig/icons/hi32-action-attacher.png b/kig/icons/hi32-action-attacher.png Binary files differnew file mode 100644 index 00000000..bc66fd40 --- /dev/null +++ b/kig/icons/hi32-action-attacher.png diff --git a/kig/icons/hi32-action-baseCircle.png b/kig/icons/hi32-action-baseCircle.png Binary files differnew file mode 100644 index 00000000..03c22c3b --- /dev/null +++ b/kig/icons/hi32-action-baseCircle.png diff --git a/kig/icons/hi32-action-bisection.png b/kig/icons/hi32-action-bisection.png Binary files differnew file mode 100644 index 00000000..fe24f5e2 --- /dev/null +++ b/kig/icons/hi32-action-bisection.png diff --git a/kig/icons/hi32-action-centerofcurvature.png b/kig/icons/hi32-action-centerofcurvature.png Binary files differnew file mode 100644 index 00000000..a5e089af --- /dev/null +++ b/kig/icons/hi32-action-centerofcurvature.png diff --git a/kig/icons/hi32-action-centralsymmetry.png b/kig/icons/hi32-action-centralsymmetry.png Binary files differnew file mode 100644 index 00000000..222c06ca --- /dev/null +++ b/kig/icons/hi32-action-centralsymmetry.png diff --git a/kig/icons/hi32-action-circlebcl.png b/kig/icons/hi32-action-circlebcl.png Binary files differnew file mode 100644 index 00000000..73ee7909 --- /dev/null +++ b/kig/icons/hi32-action-circlebcl.png diff --git a/kig/icons/hi32-action-circlebcp.png b/kig/icons/hi32-action-circlebcp.png Binary files differnew file mode 100644 index 00000000..91f0e6ec --- /dev/null +++ b/kig/icons/hi32-action-circlebcp.png diff --git a/kig/icons/hi32-action-circlebpd.png b/kig/icons/hi32-action-circlebpd.png Binary files differnew file mode 100644 index 00000000..e51dda03 --- /dev/null +++ b/kig/icons/hi32-action-circlebpd.png diff --git a/kig/icons/hi32-action-circlebps.png b/kig/icons/hi32-action-circlebps.png Binary files differnew file mode 100644 index 00000000..c1c054ac --- /dev/null +++ b/kig/icons/hi32-action-circlebps.png diff --git a/kig/icons/hi32-action-circlebtp.png b/kig/icons/hi32-action-circlebtp.png Binary files differnew file mode 100644 index 00000000..a0e438e4 --- /dev/null +++ b/kig/icons/hi32-action-circlebtp.png diff --git a/kig/icons/hi32-action-circlelineintersection.png b/kig/icons/hi32-action-circlelineintersection.png Binary files differnew file mode 100644 index 00000000..fbd65d4e --- /dev/null +++ b/kig/icons/hi32-action-circlelineintersection.png diff --git a/kig/icons/hi32-action-circumference.png b/kig/icons/hi32-action-circumference.png Binary files differnew file mode 100644 index 00000000..7af7f209 --- /dev/null +++ b/kig/icons/hi32-action-circumference.png diff --git a/kig/icons/hi32-action-conicasymptotes.png b/kig/icons/hi32-action-conicasymptotes.png Binary files differnew file mode 100644 index 00000000..bd9feaeb --- /dev/null +++ b/kig/icons/hi32-action-conicasymptotes.png diff --git a/kig/icons/hi32-action-conicb5p.png b/kig/icons/hi32-action-conicb5p.png Binary files differnew file mode 100644 index 00000000..3062bacc --- /dev/null +++ b/kig/icons/hi32-action-conicb5p.png diff --git a/kig/icons/hi32-action-coniclineintersection.png b/kig/icons/hi32-action-coniclineintersection.png Binary files differnew file mode 100644 index 00000000..7dbeb738 --- /dev/null +++ b/kig/icons/hi32-action-coniclineintersection.png diff --git a/kig/icons/hi32-action-conicsradicalline.png b/kig/icons/hi32-action-conicsradicalline.png Binary files differnew file mode 100644 index 00000000..455735b9 --- /dev/null +++ b/kig/icons/hi32-action-conicsradicalline.png diff --git a/kig/icons/hi32-action-convexhull.png b/kig/icons/hi32-action-convexhull.png Binary files differnew file mode 100644 index 00000000..c5777e2c --- /dev/null +++ b/kig/icons/hi32-action-convexhull.png diff --git a/kig/icons/hi32-action-curvelineintersection.png b/kig/icons/hi32-action-curvelineintersection.png Binary files differnew file mode 100644 index 00000000..8cf6b2c5 --- /dev/null +++ b/kig/icons/hi32-action-curvelineintersection.png diff --git a/kig/icons/hi32-action-directrix.png b/kig/icons/hi32-action-directrix.png Binary files differnew file mode 100644 index 00000000..694cbd83 --- /dev/null +++ b/kig/icons/hi32-action-directrix.png diff --git a/kig/icons/hi32-action-distance.png b/kig/icons/hi32-action-distance.png Binary files differnew file mode 100644 index 00000000..9e85728c --- /dev/null +++ b/kig/icons/hi32-action-distance.png diff --git a/kig/icons/hi32-action-ellipsebffp.png b/kig/icons/hi32-action-ellipsebffp.png Binary files differnew file mode 100644 index 00000000..e324bfe4 --- /dev/null +++ b/kig/icons/hi32-action-ellipsebffp.png diff --git a/kig/icons/hi32-action-en.png b/kig/icons/hi32-action-en.png Binary files differnew file mode 100644 index 00000000..0f689847 --- /dev/null +++ b/kig/icons/hi32-action-en.png diff --git a/kig/icons/hi32-action-equilateralhyperbolab4p.png b/kig/icons/hi32-action-equilateralhyperbolab4p.png Binary files differnew file mode 100644 index 00000000..d32fec90 --- /dev/null +++ b/kig/icons/hi32-action-equilateralhyperbolab4p.png diff --git a/kig/icons/hi32-action-equitriangle.png b/kig/icons/hi32-action-equitriangle.png Binary files differnew file mode 100644 index 00000000..198788c3 --- /dev/null +++ b/kig/icons/hi32-action-equitriangle.png diff --git a/kig/icons/hi32-action-genericaffinity.png b/kig/icons/hi32-action-genericaffinity.png Binary files differnew file mode 100644 index 00000000..4450bbf1 --- /dev/null +++ b/kig/icons/hi32-action-genericaffinity.png diff --git a/kig/icons/hi32-action-genericprojectivity.png b/kig/icons/hi32-action-genericprojectivity.png Binary files differnew file mode 100644 index 00000000..55908522 --- /dev/null +++ b/kig/icons/hi32-action-genericprojectivity.png diff --git a/kig/icons/hi32-action-halflinebyvector.png b/kig/icons/hi32-action-halflinebyvector.png Binary files differnew file mode 100644 index 00000000..3e8e92a8 --- /dev/null +++ b/kig/icons/hi32-action-halflinebyvector.png diff --git a/kig/icons/hi32-action-harmonichomology.png b/kig/icons/hi32-action-harmonichomology.png Binary files differnew file mode 100644 index 00000000..1d6e42b4 --- /dev/null +++ b/kig/icons/hi32-action-harmonichomology.png diff --git a/kig/icons/hi32-action-hexagonbcv.png b/kig/icons/hi32-action-hexagonbcv.png Binary files differnew file mode 100644 index 00000000..97309f49 --- /dev/null +++ b/kig/icons/hi32-action-hexagonbcv.png diff --git a/kig/icons/hi32-action-hyperbolabffp.png b/kig/icons/hi32-action-hyperbolabffp.png Binary files differnew file mode 100644 index 00000000..4dc82334 --- /dev/null +++ b/kig/icons/hi32-action-hyperbolabffp.png diff --git a/kig/icons/hi32-action-intersection.png b/kig/icons/hi32-action-intersection.png Binary files differnew file mode 100644 index 00000000..be2fb008 --- /dev/null +++ b/kig/icons/hi32-action-intersection.png diff --git a/kig/icons/hi32-action-inversion.png b/kig/icons/hi32-action-inversion.png Binary files differnew file mode 100644 index 00000000..7d8223d1 --- /dev/null +++ b/kig/icons/hi32-action-inversion.png diff --git a/kig/icons/hi32-action-kig_polygon.png b/kig/icons/hi32-action-kig_polygon.png Binary files differnew file mode 100644 index 00000000..67d9982c --- /dev/null +++ b/kig/icons/hi32-action-kig_polygon.png diff --git a/kig/icons/hi32-action-kig_text.png b/kig/icons/hi32-action-kig_text.png Binary files differnew file mode 100644 index 00000000..22136c5b --- /dev/null +++ b/kig/icons/hi32-action-kig_text.png diff --git a/kig/icons/hi32-action-line.png b/kig/icons/hi32-action-line.png Binary files differnew file mode 100644 index 00000000..a0e4c656 --- /dev/null +++ b/kig/icons/hi32-action-line.png diff --git a/kig/icons/hi32-action-linebyvector.png b/kig/icons/hi32-action-linebyvector.png Binary files differnew file mode 100644 index 00000000..c64205fe --- /dev/null +++ b/kig/icons/hi32-action-linebyvector.png diff --git a/kig/icons/hi32-action-locus.png b/kig/icons/hi32-action-locus.png Binary files differnew file mode 100644 index 00000000..90bb23a7 --- /dev/null +++ b/kig/icons/hi32-action-locus.png diff --git a/kig/icons/hi32-action-mirrorpoint.png b/kig/icons/hi32-action-mirrorpoint.png Binary files differnew file mode 100644 index 00000000..840f5e9e --- /dev/null +++ b/kig/icons/hi32-action-mirrorpoint.png diff --git a/kig/icons/hi32-action-move.png b/kig/icons/hi32-action-move.png Binary files differnew file mode 100644 index 00000000..88ab3cef --- /dev/null +++ b/kig/icons/hi32-action-move.png diff --git a/kig/icons/hi32-action-paint.png b/kig/icons/hi32-action-paint.png Binary files differnew file mode 100644 index 00000000..feb1f2ab --- /dev/null +++ b/kig/icons/hi32-action-paint.png diff --git a/kig/icons/hi32-action-parabolabtp.png b/kig/icons/hi32-action-parabolabtp.png Binary files differnew file mode 100644 index 00000000..4edea289 --- /dev/null +++ b/kig/icons/hi32-action-parabolabtp.png diff --git a/kig/icons/hi32-action-parallel.png b/kig/icons/hi32-action-parallel.png Binary files differnew file mode 100644 index 00000000..64e4b09d --- /dev/null +++ b/kig/icons/hi32-action-parallel.png diff --git a/kig/icons/hi32-action-perpendicular.png b/kig/icons/hi32-action-perpendicular.png Binary files differnew file mode 100644 index 00000000..d9c82f5a --- /dev/null +++ b/kig/icons/hi32-action-perpendicular.png diff --git a/kig/icons/hi32-action-point.png b/kig/icons/hi32-action-point.png Binary files differnew file mode 100644 index 00000000..a541f857 --- /dev/null +++ b/kig/icons/hi32-action-point.png diff --git a/kig/icons/hi32-action-pointOnLine.png b/kig/icons/hi32-action-pointOnLine.png Binary files differnew file mode 100644 index 00000000..f9cb6168 --- /dev/null +++ b/kig/icons/hi32-action-pointOnLine.png diff --git a/kig/icons/hi32-action-pointxy.png b/kig/icons/hi32-action-pointxy.png Binary files differnew file mode 100644 index 00000000..d3ede624 --- /dev/null +++ b/kig/icons/hi32-action-pointxy.png diff --git a/kig/icons/hi32-action-polygonsides.png b/kig/icons/hi32-action-polygonsides.png Binary files differnew file mode 100644 index 00000000..fe57f99a --- /dev/null +++ b/kig/icons/hi32-action-polygonsides.png diff --git a/kig/icons/hi32-action-polygonvertices.png b/kig/icons/hi32-action-polygonvertices.png Binary files differnew file mode 100644 index 00000000..84c22f1c --- /dev/null +++ b/kig/icons/hi32-action-polygonvertices.png diff --git a/kig/icons/hi32-action-python.png b/kig/icons/hi32-action-python.png Binary files differnew file mode 100644 index 00000000..ee93b7d5 --- /dev/null +++ b/kig/icons/hi32-action-python.png diff --git a/kig/icons/hi32-action-radicalline.png b/kig/icons/hi32-action-radicalline.png Binary files differnew file mode 100644 index 00000000..47786034 --- /dev/null +++ b/kig/icons/hi32-action-radicalline.png diff --git a/kig/icons/hi32-action-ray.png b/kig/icons/hi32-action-ray.png Binary files differnew file mode 100644 index 00000000..68545397 --- /dev/null +++ b/kig/icons/hi32-action-ray.png diff --git a/kig/icons/hi32-action-rotation.png b/kig/icons/hi32-action-rotation.png Binary files differnew file mode 100644 index 00000000..a2e1f3df --- /dev/null +++ b/kig/icons/hi32-action-rotation.png diff --git a/kig/icons/hi32-action-scale.png b/kig/icons/hi32-action-scale.png Binary files differnew file mode 100644 index 00000000..f0d70870 --- /dev/null +++ b/kig/icons/hi32-action-scale.png diff --git a/kig/icons/hi32-action-segment.png b/kig/icons/hi32-action-segment.png Binary files differnew file mode 100644 index 00000000..fc5a0c4d --- /dev/null +++ b/kig/icons/hi32-action-segment.png diff --git a/kig/icons/hi32-action-segment_midpoint.png b/kig/icons/hi32-action-segment_midpoint.png Binary files differnew file mode 100644 index 00000000..6fa5bf1d --- /dev/null +++ b/kig/icons/hi32-action-segment_midpoint.png diff --git a/kig/icons/hi32-action-segmentaxis.png b/kig/icons/hi32-action-segmentaxis.png Binary files differnew file mode 100644 index 00000000..885cb9e7 --- /dev/null +++ b/kig/icons/hi32-action-segmentaxis.png diff --git a/kig/icons/hi32-action-similitude.png b/kig/icons/hi32-action-similitude.png Binary files differnew file mode 100644 index 00000000..87f6e486 --- /dev/null +++ b/kig/icons/hi32-action-similitude.png diff --git a/kig/icons/hi32-action-sizer.png b/kig/icons/hi32-action-sizer.png Binary files differnew file mode 100644 index 00000000..82ff17ab --- /dev/null +++ b/kig/icons/hi32-action-sizer.png diff --git a/kig/icons/hi32-action-slope.png b/kig/icons/hi32-action-slope.png Binary files differnew file mode 100644 index 00000000..6bfdb0e3 --- /dev/null +++ b/kig/icons/hi32-action-slope.png diff --git a/kig/icons/hi32-action-square.png b/kig/icons/hi32-action-square.png Binary files differnew file mode 100644 index 00000000..a9f1b401 --- /dev/null +++ b/kig/icons/hi32-action-square.png diff --git a/kig/icons/hi32-action-stretch.png b/kig/icons/hi32-action-stretch.png Binary files differnew file mode 100644 index 00000000..43b4e899 --- /dev/null +++ b/kig/icons/hi32-action-stretch.png diff --git a/kig/icons/hi32-action-tangent.png b/kig/icons/hi32-action-tangent.png Binary files differnew file mode 100644 index 00000000..92922634 --- /dev/null +++ b/kig/icons/hi32-action-tangent.png diff --git a/kig/icons/hi32-action-test.png b/kig/icons/hi32-action-test.png Binary files differnew file mode 100644 index 00000000..4a240dfe --- /dev/null +++ b/kig/icons/hi32-action-test.png diff --git a/kig/icons/hi32-action-testcollinear.png b/kig/icons/hi32-action-testcollinear.png Binary files differnew file mode 100644 index 00000000..c999715e --- /dev/null +++ b/kig/icons/hi32-action-testcollinear.png diff --git a/kig/icons/hi32-action-testcontains.png b/kig/icons/hi32-action-testcontains.png Binary files differnew file mode 100644 index 00000000..0694e261 --- /dev/null +++ b/kig/icons/hi32-action-testcontains.png diff --git a/kig/icons/hi32-action-testdistance.png b/kig/icons/hi32-action-testdistance.png Binary files differnew file mode 100644 index 00000000..518a4d51 --- /dev/null +++ b/kig/icons/hi32-action-testdistance.png diff --git a/kig/icons/hi32-action-testorthogonal.png b/kig/icons/hi32-action-testorthogonal.png Binary files differnew file mode 100644 index 00000000..4ce981c0 --- /dev/null +++ b/kig/icons/hi32-action-testorthogonal.png diff --git a/kig/icons/hi32-action-testparallel.png b/kig/icons/hi32-action-testparallel.png Binary files differnew file mode 100644 index 00000000..c2fa12af --- /dev/null +++ b/kig/icons/hi32-action-testparallel.png diff --git a/kig/icons/hi32-action-translation.png b/kig/icons/hi32-action-translation.png Binary files differnew file mode 100644 index 00000000..e31ff95e --- /dev/null +++ b/kig/icons/hi32-action-translation.png diff --git a/kig/icons/hi32-action-triangle.png b/kig/icons/hi32-action-triangle.png Binary files differnew file mode 100644 index 00000000..85c69a1b --- /dev/null +++ b/kig/icons/hi32-action-triangle.png diff --git a/kig/icons/hi32-action-vector.png b/kig/icons/hi32-action-vector.png Binary files differnew file mode 100644 index 00000000..1de28bba --- /dev/null +++ b/kig/icons/hi32-action-vector.png diff --git a/kig/icons/hi32-action-vectordifference.png b/kig/icons/hi32-action-vectordifference.png Binary files differnew file mode 100644 index 00000000..ef60e0bf --- /dev/null +++ b/kig/icons/hi32-action-vectordifference.png diff --git a/kig/icons/hi32-action-vectorsum.png b/kig/icons/hi32-action-vectorsum.png Binary files differnew file mode 100644 index 00000000..07771710 --- /dev/null +++ b/kig/icons/hi32-action-vectorsum.png diff --git a/kig/icons/hi32-action-w.png b/kig/icons/hi32-action-w.png Binary files differnew file mode 100644 index 00000000..156707c2 --- /dev/null +++ b/kig/icons/hi32-action-w.png diff --git a/kig/icons/hisc-action-angle.svgz b/kig/icons/hisc-action-angle.svgz Binary files differnew file mode 100644 index 00000000..b08e88a8 --- /dev/null +++ b/kig/icons/hisc-action-angle.svgz diff --git a/kig/icons/hisc-action-angle_bisector.svgz b/kig/icons/hisc-action-angle_bisector.svgz Binary files differnew file mode 100644 index 00000000..c2ce2411 --- /dev/null +++ b/kig/icons/hisc-action-angle_bisector.svgz diff --git a/kig/icons/hisc-action-angle_size.svgz b/kig/icons/hisc-action-angle_size.svgz Binary files differnew file mode 100644 index 00000000..aced5dfd --- /dev/null +++ b/kig/icons/hisc-action-angle_size.svgz diff --git a/kig/icons/hisc-action-arc.svgz b/kig/icons/hisc-action-arc.svgz Binary files differnew file mode 100644 index 00000000..af79d93d --- /dev/null +++ b/kig/icons/hisc-action-arc.svgz diff --git a/kig/icons/hisc-action-arc_center.svgz b/kig/icons/hisc-action-arc_center.svgz Binary files differnew file mode 100644 index 00000000..65d4da89 --- /dev/null +++ b/kig/icons/hisc-action-arc_center.svgz diff --git a/kig/icons/hisc-action-areaCircle.svgz b/kig/icons/hisc-action-areaCircle.svgz Binary files differnew file mode 100644 index 00000000..36223591 --- /dev/null +++ b/kig/icons/hisc-action-areaCircle.svgz diff --git a/kig/icons/hisc-action-attacher.svgz b/kig/icons/hisc-action-attacher.svgz Binary files differnew file mode 100644 index 00000000..6f5f7743 --- /dev/null +++ b/kig/icons/hisc-action-attacher.svgz diff --git a/kig/icons/hisc-action-baseCircle.svgz b/kig/icons/hisc-action-baseCircle.svgz Binary files differnew file mode 100644 index 00000000..3a431868 --- /dev/null +++ b/kig/icons/hisc-action-baseCircle.svgz diff --git a/kig/icons/hisc-action-bisection.svgz b/kig/icons/hisc-action-bisection.svgz Binary files differnew file mode 100644 index 00000000..7b1bfc01 --- /dev/null +++ b/kig/icons/hisc-action-bisection.svgz diff --git a/kig/icons/hisc-action-centerofcurvature.svgz b/kig/icons/hisc-action-centerofcurvature.svgz Binary files differnew file mode 100644 index 00000000..a66db2bd --- /dev/null +++ b/kig/icons/hisc-action-centerofcurvature.svgz diff --git a/kig/icons/hisc-action-centralsymmetry.svgz b/kig/icons/hisc-action-centralsymmetry.svgz Binary files differnew file mode 100644 index 00000000..b065a6d3 --- /dev/null +++ b/kig/icons/hisc-action-centralsymmetry.svgz diff --git a/kig/icons/hisc-action-circlebcl.svgz b/kig/icons/hisc-action-circlebcl.svgz Binary files differnew file mode 100644 index 00000000..1e9162d5 --- /dev/null +++ b/kig/icons/hisc-action-circlebcl.svgz diff --git a/kig/icons/hisc-action-circlebcp.svgz b/kig/icons/hisc-action-circlebcp.svgz Binary files differnew file mode 100644 index 00000000..f05a2f2c --- /dev/null +++ b/kig/icons/hisc-action-circlebcp.svgz diff --git a/kig/icons/hisc-action-circlebpd.svgz b/kig/icons/hisc-action-circlebpd.svgz Binary files differnew file mode 100644 index 00000000..c636bfd8 --- /dev/null +++ b/kig/icons/hisc-action-circlebpd.svgz diff --git a/kig/icons/hisc-action-circlebps.svgz b/kig/icons/hisc-action-circlebps.svgz Binary files differnew file mode 100644 index 00000000..27463564 --- /dev/null +++ b/kig/icons/hisc-action-circlebps.svgz diff --git a/kig/icons/hisc-action-circlebtp.svgz b/kig/icons/hisc-action-circlebtp.svgz Binary files differnew file mode 100644 index 00000000..cd1df6e9 --- /dev/null +++ b/kig/icons/hisc-action-circlebtp.svgz diff --git a/kig/icons/hisc-action-circlelineintersection.svgz b/kig/icons/hisc-action-circlelineintersection.svgz Binary files differnew file mode 100644 index 00000000..031ee82b --- /dev/null +++ b/kig/icons/hisc-action-circlelineintersection.svgz diff --git a/kig/icons/hisc-action-circumference.svgz b/kig/icons/hisc-action-circumference.svgz Binary files differnew file mode 100644 index 00000000..b695ef3c --- /dev/null +++ b/kig/icons/hisc-action-circumference.svgz diff --git a/kig/icons/hisc-action-conicasymptotes.svgz b/kig/icons/hisc-action-conicasymptotes.svgz Binary files differnew file mode 100644 index 00000000..9d63239c --- /dev/null +++ b/kig/icons/hisc-action-conicasymptotes.svgz diff --git a/kig/icons/hisc-action-conicb5p.svgz b/kig/icons/hisc-action-conicb5p.svgz Binary files differnew file mode 100644 index 00000000..80d15b8e --- /dev/null +++ b/kig/icons/hisc-action-conicb5p.svgz diff --git a/kig/icons/hisc-action-coniclineintersection.svgz b/kig/icons/hisc-action-coniclineintersection.svgz Binary files differnew file mode 100644 index 00000000..853cca71 --- /dev/null +++ b/kig/icons/hisc-action-coniclineintersection.svgz diff --git a/kig/icons/hisc-action-conicsradicalline.svgz b/kig/icons/hisc-action-conicsradicalline.svgz Binary files differnew file mode 100644 index 00000000..6991b9b7 --- /dev/null +++ b/kig/icons/hisc-action-conicsradicalline.svgz diff --git a/kig/icons/hisc-action-convexhull.svgz b/kig/icons/hisc-action-convexhull.svgz Binary files differnew file mode 100644 index 00000000..1d1121df --- /dev/null +++ b/kig/icons/hisc-action-convexhull.svgz diff --git a/kig/icons/hisc-action-curvelineintersection.svgz b/kig/icons/hisc-action-curvelineintersection.svgz Binary files differnew file mode 100644 index 00000000..948b027b --- /dev/null +++ b/kig/icons/hisc-action-curvelineintersection.svgz diff --git a/kig/icons/hisc-action-directrix.svgz b/kig/icons/hisc-action-directrix.svgz Binary files differnew file mode 100644 index 00000000..f1e36ab5 --- /dev/null +++ b/kig/icons/hisc-action-directrix.svgz diff --git a/kig/icons/hisc-action-distance.svgz b/kig/icons/hisc-action-distance.svgz Binary files differnew file mode 100644 index 00000000..aa982a87 --- /dev/null +++ b/kig/icons/hisc-action-distance.svgz diff --git a/kig/icons/hisc-action-ellipsebffp.svgz b/kig/icons/hisc-action-ellipsebffp.svgz Binary files differnew file mode 100644 index 00000000..b927f644 --- /dev/null +++ b/kig/icons/hisc-action-ellipsebffp.svgz diff --git a/kig/icons/hisc-action-en.svgz b/kig/icons/hisc-action-en.svgz Binary files differnew file mode 100644 index 00000000..2f461a23 --- /dev/null +++ b/kig/icons/hisc-action-en.svgz diff --git a/kig/icons/hisc-action-equilateralhyperbolab4p.svgz b/kig/icons/hisc-action-equilateralhyperbolab4p.svgz Binary files differnew file mode 100644 index 00000000..3e029768 --- /dev/null +++ b/kig/icons/hisc-action-equilateralhyperbolab4p.svgz diff --git a/kig/icons/hisc-action-equitriangle.svgz b/kig/icons/hisc-action-equitriangle.svgz Binary files differnew file mode 100644 index 00000000..46dd9b2d --- /dev/null +++ b/kig/icons/hisc-action-equitriangle.svgz diff --git a/kig/icons/hisc-action-genericaffinity.svgz b/kig/icons/hisc-action-genericaffinity.svgz Binary files differnew file mode 100644 index 00000000..3b1c7d46 --- /dev/null +++ b/kig/icons/hisc-action-genericaffinity.svgz diff --git a/kig/icons/hisc-action-genericprojectivity.svgz b/kig/icons/hisc-action-genericprojectivity.svgz Binary files differnew file mode 100644 index 00000000..bc74c671 --- /dev/null +++ b/kig/icons/hisc-action-genericprojectivity.svgz diff --git a/kig/icons/hisc-action-halflinebyvector.svgz b/kig/icons/hisc-action-halflinebyvector.svgz Binary files differnew file mode 100644 index 00000000..92be9ece --- /dev/null +++ b/kig/icons/hisc-action-halflinebyvector.svgz diff --git a/kig/icons/hisc-action-harmonichomology.svgz b/kig/icons/hisc-action-harmonichomology.svgz Binary files differnew file mode 100644 index 00000000..30ba3d89 --- /dev/null +++ b/kig/icons/hisc-action-harmonichomology.svgz diff --git a/kig/icons/hisc-action-hexagonbcv.svgz b/kig/icons/hisc-action-hexagonbcv.svgz Binary files differnew file mode 100644 index 00000000..82cd94e2 --- /dev/null +++ b/kig/icons/hisc-action-hexagonbcv.svgz diff --git a/kig/icons/hisc-action-hyperbolabffp.svgz b/kig/icons/hisc-action-hyperbolabffp.svgz Binary files differnew file mode 100644 index 00000000..4c0fb479 --- /dev/null +++ b/kig/icons/hisc-action-hyperbolabffp.svgz diff --git a/kig/icons/hisc-action-intersection.svgz b/kig/icons/hisc-action-intersection.svgz Binary files differnew file mode 100644 index 00000000..05171c8c --- /dev/null +++ b/kig/icons/hisc-action-intersection.svgz diff --git a/kig/icons/hisc-action-inversion.svgz b/kig/icons/hisc-action-inversion.svgz Binary files differnew file mode 100644 index 00000000..4508bc49 --- /dev/null +++ b/kig/icons/hisc-action-inversion.svgz diff --git a/kig/icons/hisc-action-kig_polygon.svgz b/kig/icons/hisc-action-kig_polygon.svgz Binary files differnew file mode 100644 index 00000000..cf74ff91 --- /dev/null +++ b/kig/icons/hisc-action-kig_polygon.svgz diff --git a/kig/icons/hisc-action-kig_text.svgz b/kig/icons/hisc-action-kig_text.svgz Binary files differnew file mode 100644 index 00000000..7934c739 --- /dev/null +++ b/kig/icons/hisc-action-kig_text.svgz diff --git a/kig/icons/hisc-action-line.svgz b/kig/icons/hisc-action-line.svgz Binary files differnew file mode 100644 index 00000000..10f4e4f1 --- /dev/null +++ b/kig/icons/hisc-action-line.svgz diff --git a/kig/icons/hisc-action-linebyvector.svgz b/kig/icons/hisc-action-linebyvector.svgz Binary files differnew file mode 100644 index 00000000..c5e5e6ea --- /dev/null +++ b/kig/icons/hisc-action-linebyvector.svgz diff --git a/kig/icons/hisc-action-locus.svgz b/kig/icons/hisc-action-locus.svgz Binary files differnew file mode 100644 index 00000000..1eda537a --- /dev/null +++ b/kig/icons/hisc-action-locus.svgz diff --git a/kig/icons/hisc-action-mirrorpoint.svgz b/kig/icons/hisc-action-mirrorpoint.svgz Binary files differnew file mode 100644 index 00000000..072016c3 --- /dev/null +++ b/kig/icons/hisc-action-mirrorpoint.svgz diff --git a/kig/icons/hisc-action-move.svgz b/kig/icons/hisc-action-move.svgz Binary files differnew file mode 100644 index 00000000..b26cdb23 --- /dev/null +++ b/kig/icons/hisc-action-move.svgz diff --git a/kig/icons/hisc-action-paint.svgz b/kig/icons/hisc-action-paint.svgz Binary files differnew file mode 100644 index 00000000..7739240d --- /dev/null +++ b/kig/icons/hisc-action-paint.svgz diff --git a/kig/icons/hisc-action-parabolabtp.svgz b/kig/icons/hisc-action-parabolabtp.svgz Binary files differnew file mode 100644 index 00000000..91c7d6cf --- /dev/null +++ b/kig/icons/hisc-action-parabolabtp.svgz diff --git a/kig/icons/hisc-action-parallel.svgz b/kig/icons/hisc-action-parallel.svgz Binary files differnew file mode 100644 index 00000000..d6992058 --- /dev/null +++ b/kig/icons/hisc-action-parallel.svgz diff --git a/kig/icons/hisc-action-perpendicular.svgz b/kig/icons/hisc-action-perpendicular.svgz Binary files differnew file mode 100644 index 00000000..b0713e36 --- /dev/null +++ b/kig/icons/hisc-action-perpendicular.svgz diff --git a/kig/icons/hisc-action-point.svgz b/kig/icons/hisc-action-point.svgz Binary files differnew file mode 100644 index 00000000..df7b856a --- /dev/null +++ b/kig/icons/hisc-action-point.svgz diff --git a/kig/icons/hisc-action-pointOnLine.svgz b/kig/icons/hisc-action-pointOnLine.svgz Binary files differnew file mode 100644 index 00000000..53ac009e --- /dev/null +++ b/kig/icons/hisc-action-pointOnLine.svgz diff --git a/kig/icons/hisc-action-pointxy.svgz b/kig/icons/hisc-action-pointxy.svgz Binary files differnew file mode 100644 index 00000000..4fab2148 --- /dev/null +++ b/kig/icons/hisc-action-pointxy.svgz diff --git a/kig/icons/hisc-action-polygonsides.svgz b/kig/icons/hisc-action-polygonsides.svgz Binary files differnew file mode 100644 index 00000000..6348509c --- /dev/null +++ b/kig/icons/hisc-action-polygonsides.svgz diff --git a/kig/icons/hisc-action-polygonvertices.svgz b/kig/icons/hisc-action-polygonvertices.svgz Binary files differnew file mode 100644 index 00000000..27a2876b --- /dev/null +++ b/kig/icons/hisc-action-polygonvertices.svgz diff --git a/kig/icons/hisc-action-python.svgz b/kig/icons/hisc-action-python.svgz Binary files differnew file mode 100644 index 00000000..47415a43 --- /dev/null +++ b/kig/icons/hisc-action-python.svgz diff --git a/kig/icons/hisc-action-radicalline.svgz b/kig/icons/hisc-action-radicalline.svgz Binary files differnew file mode 100644 index 00000000..87416911 --- /dev/null +++ b/kig/icons/hisc-action-radicalline.svgz diff --git a/kig/icons/hisc-action-ray.svgz b/kig/icons/hisc-action-ray.svgz Binary files differnew file mode 100644 index 00000000..aff05709 --- /dev/null +++ b/kig/icons/hisc-action-ray.svgz diff --git a/kig/icons/hisc-action-rotation.svgz b/kig/icons/hisc-action-rotation.svgz Binary files differnew file mode 100644 index 00000000..9ab8dc14 --- /dev/null +++ b/kig/icons/hisc-action-rotation.svgz diff --git a/kig/icons/hisc-action-scale.svgz b/kig/icons/hisc-action-scale.svgz Binary files differnew file mode 100644 index 00000000..43ad5f72 --- /dev/null +++ b/kig/icons/hisc-action-scale.svgz diff --git a/kig/icons/hisc-action-segment.svgz b/kig/icons/hisc-action-segment.svgz Binary files differnew file mode 100644 index 00000000..baeeaef3 --- /dev/null +++ b/kig/icons/hisc-action-segment.svgz diff --git a/kig/icons/hisc-action-segment_midpoint.svgz b/kig/icons/hisc-action-segment_midpoint.svgz Binary files differnew file mode 100644 index 00000000..57cb6d66 --- /dev/null +++ b/kig/icons/hisc-action-segment_midpoint.svgz diff --git a/kig/icons/hisc-action-segmentaxis.svgz b/kig/icons/hisc-action-segmentaxis.svgz Binary files differnew file mode 100644 index 00000000..40f82b6c --- /dev/null +++ b/kig/icons/hisc-action-segmentaxis.svgz diff --git a/kig/icons/hisc-action-similitude.svgz b/kig/icons/hisc-action-similitude.svgz Binary files differnew file mode 100644 index 00000000..823a384d --- /dev/null +++ b/kig/icons/hisc-action-similitude.svgz diff --git a/kig/icons/hisc-action-sizer.svgz b/kig/icons/hisc-action-sizer.svgz Binary files differnew file mode 100644 index 00000000..d49c69bc --- /dev/null +++ b/kig/icons/hisc-action-sizer.svgz diff --git a/kig/icons/hisc-action-slope.svgz b/kig/icons/hisc-action-slope.svgz Binary files differnew file mode 100644 index 00000000..c4e993dc --- /dev/null +++ b/kig/icons/hisc-action-slope.svgz diff --git a/kig/icons/hisc-action-square.svgz b/kig/icons/hisc-action-square.svgz Binary files differnew file mode 100644 index 00000000..4e5b6527 --- /dev/null +++ b/kig/icons/hisc-action-square.svgz diff --git a/kig/icons/hisc-action-stretch.svgz b/kig/icons/hisc-action-stretch.svgz Binary files differnew file mode 100644 index 00000000..54db9e2c --- /dev/null +++ b/kig/icons/hisc-action-stretch.svgz diff --git a/kig/icons/hisc-action-tangent.svgz b/kig/icons/hisc-action-tangent.svgz Binary files differnew file mode 100644 index 00000000..74cda333 --- /dev/null +++ b/kig/icons/hisc-action-tangent.svgz diff --git a/kig/icons/hisc-action-test.svgz b/kig/icons/hisc-action-test.svgz Binary files differnew file mode 100644 index 00000000..77f16d69 --- /dev/null +++ b/kig/icons/hisc-action-test.svgz diff --git a/kig/icons/hisc-action-testcollinear.svgz b/kig/icons/hisc-action-testcollinear.svgz Binary files differnew file mode 100644 index 00000000..ee3de1ae --- /dev/null +++ b/kig/icons/hisc-action-testcollinear.svgz diff --git a/kig/icons/hisc-action-testcontains.svgz b/kig/icons/hisc-action-testcontains.svgz Binary files differnew file mode 100644 index 00000000..af07e486 --- /dev/null +++ b/kig/icons/hisc-action-testcontains.svgz diff --git a/kig/icons/hisc-action-testdistance.svgz b/kig/icons/hisc-action-testdistance.svgz Binary files differnew file mode 100644 index 00000000..4f62efd4 --- /dev/null +++ b/kig/icons/hisc-action-testdistance.svgz diff --git a/kig/icons/hisc-action-testorthogonal.svgz b/kig/icons/hisc-action-testorthogonal.svgz Binary files differnew file mode 100644 index 00000000..c5fa277b --- /dev/null +++ b/kig/icons/hisc-action-testorthogonal.svgz diff --git a/kig/icons/hisc-action-testparallel.svgz b/kig/icons/hisc-action-testparallel.svgz Binary files differnew file mode 100644 index 00000000..b4b52d58 --- /dev/null +++ b/kig/icons/hisc-action-testparallel.svgz diff --git a/kig/icons/hisc-action-translation.svgz b/kig/icons/hisc-action-translation.svgz Binary files differnew file mode 100644 index 00000000..6a0bc10a --- /dev/null +++ b/kig/icons/hisc-action-translation.svgz diff --git a/kig/icons/hisc-action-triangle.svgz b/kig/icons/hisc-action-triangle.svgz Binary files differnew file mode 100644 index 00000000..c2ea4424 --- /dev/null +++ b/kig/icons/hisc-action-triangle.svgz diff --git a/kig/icons/hisc-action-vector.svgz b/kig/icons/hisc-action-vector.svgz Binary files differnew file mode 100644 index 00000000..8e18082f --- /dev/null +++ b/kig/icons/hisc-action-vector.svgz diff --git a/kig/icons/hisc-action-vectordifference.svgz b/kig/icons/hisc-action-vectordifference.svgz Binary files differnew file mode 100644 index 00000000..dfa6c75e --- /dev/null +++ b/kig/icons/hisc-action-vectordifference.svgz diff --git a/kig/icons/hisc-action-vectorsum.svgz b/kig/icons/hisc-action-vectorsum.svgz Binary files differnew file mode 100644 index 00000000..7bd5543f --- /dev/null +++ b/kig/icons/hisc-action-vectorsum.svgz diff --git a/kig/icons/hisc-action-w.svgz b/kig/icons/hisc-action-w.svgz Binary files differnew file mode 100644 index 00000000..d6d56abc --- /dev/null +++ b/kig/icons/hisc-action-w.svgz diff --git a/kig/kfile/Makefile.am b/kig/kfile/Makefile.am new file mode 100644 index 00000000..b8de2d3d --- /dev/null +++ b/kig/kfile/Makefile.am @@ -0,0 +1,24 @@ +INCLUDES = $(all_includes) + +noinst_HEADERS = \ + kfile_drgeo.h \ + kfile_kig.h + +kde_module_LTLIBRARIES = \ + kfile_drgeo.la \ + kfile_kig.la + +kfile_drgeo_la_SOURCES = kfile_drgeo.cpp +kfile_drgeo_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) +kfile_drgeo_la_LIBADD = $(LIB_KIO) + +kfile_kig_la_SOURCES = kfile_kig.cpp +kfile_kig_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) +kfile_kig_la_LIBADD = $(LIB_KIO) + +METASOURCES = AUTO + +services_DATA = \ + kfile_drgeo.desktop \ + kfile_kig.desktop +servicesdir = $(kde_servicesdir) diff --git a/kig/kfile/kfile_drgeo.cpp b/kig/kfile/kfile_drgeo.cpp new file mode 100644 index 00000000..26ed7923 --- /dev/null +++ b/kig/kfile/kfile_drgeo.cpp @@ -0,0 +1,99 @@ +/*************************************************************************** + * Copyright (C) 2004 by Pino Toscano * + * toscano.pino@tiscali.it * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "kfile_drgeo.h" + +#include <qdom.h> +#include <qfile.h> + +#include <kgenericfactory.h> + +typedef KGenericFactory<DrgeoPlugin> drgeoFactory; + +K_EXPORT_COMPONENT_FACTORY( kfile_drgeo, drgeoFactory( "kfile_drgeo" ) ) + +DrgeoPlugin::DrgeoPlugin( QObject *parent, const char *name, const QStringList &args ) + : KFilePlugin( parent, name, args ) +{ + info = addMimeTypeInfo( "application/x-drgeo" ); + + KFileMimeTypeInfo::GroupInfo* group = addGroupInfo( info, "DrgeoInfo", i18n( "Summary" ) ); + KFileMimeTypeInfo::ItemInfo* item; + item = addItemInfo( group, "NumOfFigures", i18n( "Figures" ), QVariant::Int ); + item = addItemInfo( group, "NumOfTexts", i18n( "Texts" ), QVariant::Int ); + item = addItemInfo( group, "NumOfMacros", i18n( "Macros" ), QVariant::Int ); + + group_contents = addGroupInfo( info, "DrgeoContents", i18n( "Translators: what this drgeo " + "file contains", "Contents" ) ); +} + +bool DrgeoPlugin::readInfo( KFileMetaInfo& metainfo, uint /*what*/ ) +{ + KFileMetaInfoGroup metagroup = appendGroup( metainfo, "DrgeoContents"); + + KFileMimeTypeInfo::ItemInfo* item; + + QFile f( metainfo.path() ); + QDomDocument doc( "drgenius" ); + if ( !doc.setContent( &f ) ) + return false; + QDomElement main = doc.documentElement(); + int numfig = 0; + int numtext = 0; + int nummacro = 0; + QString sectname; + // reading figures... + for ( QDomNode n = main.firstChild(); ! n.isNull(); n = n.nextSibling() ) + { + QDomElement e = n.toElement(); + if ( e.isNull() ) continue; + else if ( e.tagName() == "drgeo" ) + { + numfig++; + sectname = QString( "Figure" ) + QString::number( numfig ); + item = addItemInfo( group_contents, sectname, i18n( "Figure" ), QVariant::String ); + appendItem( metagroup, sectname, e.attribute( "name" ) ); + } + else if ( e.tagName() == "text" ) + { + numtext++; + sectname = QString( "Text" ) + QString::number( numtext ); + item = addItemInfo( group_contents, sectname, i18n( "Text" ), QVariant::String ); + appendItem( metagroup, sectname, e.attribute( "name" ) ); + } + else if ( e.tagName() == "macro" ) + { + nummacro++; + sectname = QString( "Macro" ) + QString::number( nummacro ); + item = addItemInfo( group_contents, sectname, i18n( "Macro" ), QVariant::String ); + appendItem( metagroup, sectname, e.attribute( "name" ) ); + } + } + + metagroup = appendGroup( metainfo, "DrgeoInfo"); + appendItem( metagroup, "NumOfFigures", numfig ); + appendItem( metagroup, "NumOfTexts", numtext ); + appendItem( metagroup, "NumOfMacros", nummacro ); + + return true; +} + +#include "kfile_drgeo.moc" + diff --git a/kig/kfile/kfile_drgeo.desktop b/kig/kfile/kfile_drgeo.desktop new file mode 100644 index 00000000..1b9ea997 --- /dev/null +++ b/kig/kfile/kfile_drgeo.desktop @@ -0,0 +1,55 @@ +[Desktop Entry] +Type=Service +Name=Dr. Geo Info +Name[af]=Dr. Geo inligting +Name[be]=ЗвеÑткі Dr. Geo +Name[bn]=ড. জিও সংকà§à¦°à¦¾à¦¨à§à¦¤ তথà§à¦¯ +Name[br]=Titouroù diwar-benn Dr. Geo +Name[ca]=Informació Dr. Geo +Name[cs]=Dr. Geo info +Name[csb]=Wëdowiédzô Dr Geo +Name[cy]=Gwybodaeth Dr. Geo +Name[da]=Dr. Geo info +Name[el]=ΠληÏοφοÏίες για το Dr. Geo +Name[eo]=Dr. Geo info +Name[es]=Información de Dr. Geo +Name[et]=Dr. Geo info +Name[eu]=Dr. Geo informazioa +Name[fa]=اطلاعات دکتر جیو +Name[fi]=Dr. Geo +Name[fr]=Informations de Dr. Geo +Name[ga]=Eolas faoi Dr. Geo +Name[gl]=Información de Dr. Geo +Name[he]=מידע על Dr. Geo +Name[hi]=डॉ. जिओ जानकारी +Name[hu]=Dr. Geo-jellemzÅ‘k +Name[is]=Dr. Geo upplýsingar +Name[it]=Informazioni Dr. Geo +Name[ja]=Dr. Geo æƒ…å ± +Name[ka]=Dr. Geo - მáƒáƒœáƒáƒªáƒ”მები +Name[km]=áž–áŸážáŸŒáž˜áž¶áž“ Dr. Geo +Name[mk]=Dr. Geo инфо +Name[nb]=Dr. Geo-info +Name[ne]=डा. जिव जानकारी +Name[nn]=Dr. Geo-info +Name[pa]=Dr. Geo ਜਾਣਕਾਰੀ +Name[pl]=Informacja Dr. Geo +Name[pt]=Informação do Dr. Geo +Name[pt_BR]=Informações do Dr. Geo +Name[ru]=Файл Dr. Geo +Name[sl]=Podatki Dr. Geo +Name[sr]=Dr. Geo информације +Name[sr@Latn]=Dr. Geo informacije +Name[sv]=Dr. Geo-information +Name[ta]=டா. ஜியோ தகவல௠+Name[tg]=Ðхборот дар бораи Ð“ÐµÐ¾Ð¼ÐµÑ‚Ñ€Ð¸Ñ +Name[tr]=Dr. Geo Bilgisi +Name[uk]=Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð´Ð»Ñ Dr. Geo +Name[vi]=Tiến sÄ© Thông tin Hình há»c +Name[zh_CN]=Dr. Geo ä¿¡æ¯ +Name[zh_TW]=Dr. Geo 資訊 +ServiceTypes=KFilePlugin +X-KDE-Library=kfile_drgeo +MimeType=application/x-drgeo +PreferredGroups=DrgeoInfo +PreferredItems=NumOfFigures;NumOfTexts;NumOfMacros diff --git a/kig/kfile/kfile_drgeo.h b/kig/kfile/kfile_drgeo.h new file mode 100644 index 00000000..b6e388da --- /dev/null +++ b/kig/kfile/kfile_drgeo.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2004 by Pino Toscano * + * toscano.pino@tiscali.it * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef KIG_KFILE_KFILE_DRGEO_H +#define KIG_KFILE_KFILE_DRGEO_H + +#include <kfilemetainfo.h> + +class QStringList; + +class DrgeoPlugin: public KFilePlugin +{ + Q_OBJECT + +public: + DrgeoPlugin( QObject *parent, const char *name, const QStringList& args ); + + virtual bool readInfo( KFileMetaInfo& metainfo, uint what); +protected: + KFileMimeTypeInfo* info; + KFileMimeTypeInfo::GroupInfo* group_contents; +}; + +#endif diff --git a/kig/kfile/kfile_kig.cpp b/kig/kfile/kfile_kig.cpp new file mode 100644 index 00000000..eca2f79d --- /dev/null +++ b/kig/kfile/kfile_kig.cpp @@ -0,0 +1,153 @@ +/*************************************************************************** + * Copyright (C) 2004 by Pino Toscano * + * toscano.pino@tiscali.it * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "kfile_kig.h" + +#include <qdom.h> +#include <qfile.h> +#include <qregexp.h> + +#include <karchive.h> +#include <kgenericfactory.h> +#include <kglobal.h> +#include <klocale.h> +#include <kstandarddirs.h> +#include <ktar.h> + +typedef KGenericFactory<KigPlugin> kigFactory; + +K_EXPORT_COMPONENT_FACTORY( kfile_kig, kigFactory( "kfile_kig" ) ) + +KigPlugin::KigPlugin( QObject *parent, const char *name, const QStringList &args ) + : KFilePlugin( parent, name, args ) +{ + KFileMimeTypeInfo::ItemInfo* item; + + info = addMimeTypeInfo( "application/x-kig" ); + + group = addGroupInfo( info, "KigInfo", i18n( "Summary" ) ); + item = addItemInfo( group, "Version", i18n( "Version" ), QVariant::String ); + item = addItemInfo( group, "CompatVersion", i18n( "Compatibility Version" ), QVariant::String ); + item = addItemInfo( group, "CoordSystem", i18n( "Coordinate System" ), QVariant::String ); + item = addItemInfo( group, "Grid", i18n( "Grid" ), QVariant::String ); + item = addItemInfo( group, "Axes", i18n( "Axes" ), QVariant::String ); + item = addItemInfo( group, "Compressed", i18n( "Compressed" ), QVariant::String ); +} + +bool KigPlugin::readInfo( KFileMetaInfo& metainfo, uint /*what*/ ) +{ + KFileMetaInfoGroup metagroup = appendGroup( metainfo, "KigInfo"); + + QString sfile = metainfo.path(); + bool iscompressed = false; + QFile f( sfile ); + if ( !sfile.endsWith( ".kig", false ) ) + { + iscompressed = true; + + QString tempdir = KGlobal::dirs()->saveLocation( "tmp" ); + if ( tempdir.isEmpty() ) + return false; + + QString tempname = sfile.section( '/', -1 ); + if ( sfile.endsWith( ".kigz", false ) ) + { + tempname.remove( QRegExp( "\\.[Kk][Ii][Gg][Zz]$" ) ); + } + else + return false; + // reading compressed file + KTar* ark = new KTar( sfile, "application/x-gzip" ); + ark->open( IO_ReadOnly ); + const KArchiveDirectory* dir = ark->directory(); + QStringList entries = dir->entries(); + QStringList kigfiles = entries.grep( QRegExp( "\\.kig$" ) ); + if ( kigfiles.count() != 1 ) + return false; + const KArchiveEntry* kigz = dir->entry( kigfiles[0] ); + if ( !kigz->isFile() ) + return false; + dynamic_cast<const KArchiveFile*>( kigz )->copyTo( tempdir ); + + f.setName( tempdir + kigz->name() ); + } + + if ( !f.open( IO_ReadOnly ) ) + return false; + + QDomDocument doc( "KigDocument" ); + if ( !doc.setContent( &f ) ) + return false; + + f.close(); + + // removing temp file + if ( iscompressed ) + f.remove(); + + QDomElement main = doc.documentElement(); + + // reading the version... + QString version = main.attribute( "Version" ); + if ( version.isEmpty() ) version = main.attribute( "version" ); + if ( version.isEmpty() ) version = i18n( "Translators: Not Available", "n/a" ); + appendItem( metagroup, "Version", version ); + + // reading the compatibility version... + QString compatversion = main.attribute( "CompatibilityVersion" ); + if ( compatversion.isEmpty() ) + compatversion = i18n( "%1 represents Kig version", + "%1 (as the version)" ).arg( version ); + appendItem( metagroup, "CompatVersion", compatversion ); + + // reading the Coordinate System... + QCString coordsystem; + for ( QDomNode n = main.firstChild(); ! n.isNull(); n = n.nextSibling() ) + { + QDomElement e = n.toElement(); + if ( e.isNull() ) continue; + if ( e.tagName() == "CoordinateSystem" ) + coordsystem = e.text().latin1(); + } + appendItem( metagroup, "CoordSystem", coordsystem ); + + // has Kig document the grid? + bool btmp = true; + QString stmp = main.attribute( "grid" ); + if ( !( stmp.isEmpty() || ( stmp != "0" ) ) ) + btmp = ( stmp != "0" ); + QString stmp2 = btmp ? i18n( "Yes" ) : i18n( "No" ); + appendItem( metagroup, "Grid", stmp2 ); + + // has Kig document the axes? + btmp = true; + stmp = main.attribute( "axes" ); + if ( !( stmp.isEmpty() || ( stmp != "0" ) ) ) + btmp = ( stmp != "0" ); + stmp2 = btmp ? i18n( "Yes" ) : i18n( "No" ); + appendItem( metagroup, "Axes", stmp2 ); + + stmp2 = iscompressed ? i18n( "Yes" ) : i18n( "No" ); + appendItem( metagroup, "Compressed", stmp2 ); + + return true; +} + +#include "kfile_kig.moc" diff --git a/kig/kfile/kfile_kig.desktop b/kig/kfile/kfile_kig.desktop new file mode 100644 index 00000000..03ee474b --- /dev/null +++ b/kig/kfile/kfile_kig.desktop @@ -0,0 +1,54 @@ +[Desktop Entry] +Type=Service +Name=Kig Info +Name[af]=Kig inligting +Name[be]=ЗвеÑткі Kig +Name[bn]=কিগ সংকà§à¦°à¦¾à¦¨à§à¦¤ তথà§à¦¯ +Name[br]=Titouroù diwar-benn Kig +Name[ca]=Informació Kig +Name[cs]=Kig info +Name[csb]=Wëdowiédzô Kig +Name[cy]=Gwybodaeth Kig +Name[da]=Kig info +Name[el]=ΠληÏοφοÏίες για το Kig +Name[eo]=Kig info +Name[es]=Información de Kig +Name[et]=Kigi info +Name[eu]=Kig informazioa +Name[fa]=اطلاعات Kig +Name[fr]=Informations de Kig +Name[ga]=Eolas faoi Kig +Name[gl]=Información de Kig +Name[he]=Kig מידע +Name[hi]=केआईजी जानकारी +Name[hu]=Kig-jellemzÅ‘k +Name[is]=Kig upplýsingar +Name[it]=Informazioni Kig +Name[ja]=Kig æƒ…å ± +Name[ka]=Kig - მáƒáƒœáƒáƒªáƒ”მები +Name[km]=áž–áŸážáŸŒáž˜áž¶áž“ Kig +Name[mk]=Kig инфо +Name[nb]=Kig-info +Name[ne]=किग जानकारी +Name[nn]=Kig-info +Name[pa]=ਕਿਗ ਜਾਣਕਾਰੀ +Name[pl]=Informacja Kig +Name[pt]=Informação do Kig +Name[pt_BR]=Informações do Kig +Name[ru]=Файл Kig +Name[sl]=Podatki Kig +Name[sr]=Kig информације +Name[sr@Latn]=Kig informacije +Name[sv]=Kig-information +Name[ta]=கிக௠தகவல௠+Name[tg]=Ðхборот дар бораи Kig +Name[tr]=Kig Bilgisi +Name[uk]=Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð´Ð»Ñ Kig +Name[vi]=Thông tin Kig +Name[zh_CN]=Kig ä¿¡æ¯ +Name[zh_TW]=Kig 資訊 +ServiceTypes=KFilePlugin +X-KDE-Library=kfile_kig +MimeType=application/x-kig +PreferredGroups=KigInfo +PreferredItems=Version;CoordSytem diff --git a/kig/kfile/kfile_kig.h b/kig/kfile/kfile_kig.h new file mode 100644 index 00000000..8580ec48 --- /dev/null +++ b/kig/kfile/kfile_kig.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2004 by Pino Toscano * + * toscano.pino@tiscali.it * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef KIG_KFILE_KFILE_KIG_H +#define KIG_KFILE_KFILE_KIG_H + +#include <kfilemetainfo.h> + +class QStringList; + +class KigPlugin: public KFilePlugin +{ + Q_OBJECT + +public: + KigPlugin( QObject *parent, const char *name, const QStringList& args ); + + virtual bool readInfo( KFileMetaInfo& metainfo, uint what); +protected: + KFileMimeTypeInfo* info; + KFileMimeTypeInfo::GroupInfo* group; +}; + +#endif diff --git a/kig/kig.lsm.in b/kig/kig.lsm.in new file mode 100644 index 00000000..8b1d504d --- /dev/null +++ b/kig/kig.lsm.in @@ -0,0 +1,18 @@ +Begin3 +Title: Kig +Version: @KIGVERSION@ +Entered-date: 8 Dec 02 +Description: Kig is an application meant to allow high school + students to interactively explore mathematical + concepts, much like Dr.Geo, KGeo, KSeg and Cabri. +Keywords: KDE Interactive Geometry KGeo KDE-Edu KSeg Cabri + math GPL +Author: Kig developers <kde-edu-devel@kde.org> +Maintained-by: Kig developers <kde-edu-devel@kde.org> +Home-page: http://edu.kde.org/kig +Primary-site: ftp://ftp.kde.org/pub/kde/stable/apps/KDE3.x/math/ + xxxxxx kig-@KIGVERSION@.tar.bz2 + xxx kig-@KIGVERSION@.lsm +Platform: Unix +Copying-policy: GPL +End diff --git a/kig/kig/Makefile.am b/kig/kig/Makefile.am new file mode 100644 index 00000000..719886cc --- /dev/null +++ b/kig/kig/Makefile.am @@ -0,0 +1,53 @@ +# this has all of the subdirectories that make will recurse into. if +# there are none, comment this out +#SUBDIRS = pics icons + +# set the include path for X, qt and KDE +INCLUDES = $(all_includes) + +# these are the headers for your project +noinst_HEADERS = kig.h kig_document.h kig_part.h kig_view.h kig_commands.h + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +KDE_ICON = AUTO + +# this Makefile creates both a KPart application and a KPart +######################################################################### +# APPLICATION SECTION +######################################################################### +# this is the program that gets installed. it's name is used for all +# of the other Makefile.am variables +bin_PROGRAMS = kig + +# the application source, library search path, and link libraries +kig_SOURCES = main.cpp kig.cpp +kig_LDFLAGS = $(KDE_RPATH) $(all_libraries) +kig_LDADD = $(LIB_KPARTS) + +# this is where the desktop file will go +xdg_apps_DATA = kig.desktop + +# this is where the shell's XML-GUI resource file goes +rcdir = $(kde_datadir)/kig +rc_DATA = kigui.rc kigpartui.rc + +######################################################################### +# KPART SECTION +######################################################################### +noinst_LTLIBRARIES = libkigparttemp.la + +# the Part's source, library search path, and link libraries +libkigparttemp_la_SOURCES = \ + kig_document.cc \ + kig_part.cpp \ + kig_view.cpp \ + kig_commands.cpp + +libkigparttemp_la_LDFLAGS = $(all_libraries) +libkigparttemp_la_LIBADD = $(LIB_KDEPRINT) $(LIB_KIO) + +# this is where the desktop file will go +partdesktopdir = $(kde_servicesdir) +partdesktop_DATA = kig_part.desktop diff --git a/kig/kig/aboutdata.h b/kig/kig/aboutdata.h new file mode 100644 index 00000000..db4f3689 --- /dev/null +++ b/kig/kig/aboutdata.h @@ -0,0 +1,89 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include <kaboutdata.h> +#include <klocale.h> + +#include "config.h" + +inline KAboutData* kigAboutData( const char* name, const char* iname ) +{ + const char* version = "v" KIGVERSION; + const char* description = I18N_NOOP( "KDE Interactive Geometry" ); + + KAboutData* tmp = new KAboutData( name, iname, version, + description, KAboutData::License_GPL, + I18N_NOOP( "(C) 2002-2005, The Kig developers" ), + 0, "http://edu.kde.org/kig" ); + tmp->addAuthor("Dominique Devriese", + I18N_NOOP("Original author, long time maintainer, design and lots of code."), + "devriese@kde.org" ); + + tmp->addAuthor("Maurizio Paolini", + I18N_NOOP( "Did a lot of important work all around Kig, " + "including, but not limited to conics, cubics, " + "transformations and property tests support." ), + "paolini@dmf.unicatt.it"); + + tmp->addAuthor( "Pino Toscano", + I18N_NOOP( "Actual maintainer, Dr. Geo import filter, point and " + "line styles, Italian translation, " + "miscellaneous stuff here and there." ), + "toscano.pino@tiscali.it" ); + + tmp->addAuthor( "Franco Pasquarelli", + I18N_NOOP( "Helped a lot with the implementation of the Locus object, " + "there's quite some math involved in doing it right, and " + "Franco wrote the most difficult parts." ), + "pasqui@dmf.unicatt.it" ); + + tmp->addCredit( "Eric Depagne", + I18N_NOOP( "The French translator, who also sent me some useful " + "feedback, like feature requests and bug reports." ), + "edepagne@eso.org" ); + + tmp->addCredit("Marc Bartsch", + I18N_NOOP("Author of KGeo, where I got inspiration, " + "some source, and most of the artwork from." ), + "marc.bartsch@web.de"); + + tmp->addCredit("Christophe Devriese", + I18N_NOOP( "Domi's brother, who he got to write the algorithm for " + "calculating the center of the circle with three " + "points given." ), + "oelewapperke@ulyssis.org" ); + + tmp->addCredit("Christophe Prud'homme", + I18N_NOOP( "Sent me a patch for some bugs." ), + "prudhomm@mit.edu" ); + + tmp->addCredit("Robert Gogolok", + I18N_NOOP("Gave me some good feedback on Kig, some feature " + "requests, cleanups and style fixes, and someone " + "to chat with on irc :)" ), + "robertgogolok@gmx.de"); + + tmp->addCredit("David Vignoni", + I18N_NOOP("Responsible for the nice application SVG Icon." ), + "david80v@tin.it"); + + tmp->addCredit( "Danny Allen", + I18N_NOOP( "Responsible for the new object action icons." ), + "danny@dannyallen.co.uk" ); + + return tmp; +} diff --git a/kig/kig/hi128-app-kig.png b/kig/kig/hi128-app-kig.png Binary files differnew file mode 100644 index 00000000..48eac877 --- /dev/null +++ b/kig/kig/hi128-app-kig.png diff --git a/kig/kig/hi16-app-kig.png b/kig/kig/hi16-app-kig.png Binary files differnew file mode 100644 index 00000000..67e1d839 --- /dev/null +++ b/kig/kig/hi16-app-kig.png diff --git a/kig/kig/hi22-app-kig.png b/kig/kig/hi22-app-kig.png Binary files differnew file mode 100644 index 00000000..bb3f7359 --- /dev/null +++ b/kig/kig/hi22-app-kig.png diff --git a/kig/kig/hi32-app-kig.png b/kig/kig/hi32-app-kig.png Binary files differnew file mode 100644 index 00000000..f7d51c72 --- /dev/null +++ b/kig/kig/hi32-app-kig.png diff --git a/kig/kig/hi48-app-kig.png b/kig/kig/hi48-app-kig.png Binary files differnew file mode 100644 index 00000000..b28d3b96 --- /dev/null +++ b/kig/kig/hi48-app-kig.png diff --git a/kig/kig/hi64-app-kig.png b/kig/kig/hi64-app-kig.png Binary files differnew file mode 100644 index 00000000..e9586819 --- /dev/null +++ b/kig/kig/hi64-app-kig.png diff --git a/kig/kig/hisc-app-kig.svgz b/kig/kig/hisc-app-kig.svgz Binary files differnew file mode 100644 index 00000000..baf0f149 --- /dev/null +++ b/kig/kig/hisc-app-kig.svgz diff --git a/kig/kig/kig.cpp b/kig/kig/kig.cpp new file mode 100644 index 00000000..2429c843 --- /dev/null +++ b/kig/kig/kig.cpp @@ -0,0 +1,308 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#include "kig.h" +#include "kig.moc" + +#include <qevent.h> +#include <qtimer.h> + +#include <kaction.h> +#include <kapplication.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kedittoolbar.h> +#include <kfiledialog.h> +#include <kkeydialog.h> +#include <klibloader.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kstatusbar.h> +#include <kstdaction.h> +#include <ktip.h> +#include <kurl.h> +#include <kurldrag.h> + +#include <assert.h> + +Kig::Kig() + : KParts::MainWindow( 0L, "Kig" ), m_part( 0 ) +{ + // setting the configation file + config = new KConfig( "kigrc" ); + // set the shell's ui resource file + setXMLFile("kigui.rc"); + // then, setup our actions + setupActions(); + + // this routine will find and load our Part. it finds the Part by + // name which is a bad idea usually.. but it's alright in this + // case since our Part is made for this Shell + + // we use globalLibrary() because if we use python scripting, then + // we need the python symbols to be exported, in order for python to + // be able to load its dll modules.. Another part of the problem is + // that boost.python fails to link against python ( on Debian at + // least ). + KLibrary* library = KLibLoader::self()->globalLibrary("libkigpart"); + KLibFactory* factory = 0; + if ( library ) factory = library->factory(); + if (factory) + { + // now that the Part is loaded, we cast it to a Part to get + // our hands on it + m_part = static_cast<KParts::ReadWritePart*> + (factory->create(this, "kig_part", "KParts::ReadWritePart" )); + if (m_part) + { + // tell the KParts::MainWindow that this is indeed the main widget + setCentralWidget(m_part->widget()); + + // and integrate the part's GUI with the shell's + createGUI(m_part); + // finally show tip-of-day ( if the user wants it :) ) + QTimer::singleShot( 0, this, SLOT( startupTipOfDay() ) ); + } + } + else + { + // if we couldn't find our Part, we exit since the Shell by + // itself can't do anything useful + KMessageBox::error(this, i18n( "Could not find the necessary Kig library, check your installation." ) ); + KApplication::exit(); + return; + } + + // we have drag'n'drop + setAcceptDrops(true); + + // save the window settings on exit. + setAutoSaveSettings(); +} + +Kig::~Kig() +{ + m_recentFilesAction->saveEntries(config); + delete config; +} + +void Kig::setupActions() +{ + KStdAction::openNew(this, SLOT(fileNew()), actionCollection()); + KStdAction::open(this, SLOT(fileOpen()), actionCollection()); + KStdAction::quit(this, SLOT(close()), actionCollection()); + +#ifdef KIG_DONT_USE_NEW_KMAINWINDOW_FEATURES + m_toolbarAction = KStdAction::showToolbar(this, SLOT(optionsShowToolbar()), actionCollection()); + m_statusbarAction = KStdAction::showStatusbar(this, SLOT(optionsShowStatusbar()), actionCollection()); +#else + createStandardStatusBarAction(); + setStandardToolBarMenuEnabled(true); +#endif + + // FIXME: this (recent files) should be app-wide, not specific to each window... + m_recentFilesAction = KStdAction::openRecent(this, SLOT(openURL(const KURL&)), actionCollection()); + m_recentFilesAction->loadEntries(config); + +#if KDE_IS_VERSION( 3, 2, 90 ) + KStdAction::keyBindings( guiFactory(), SLOT( configureShortcuts() ), actionCollection() ); +#else + KStdAction::keyBindings(this, SLOT(optionsConfigureKeys()), actionCollection()); +#endif + KStdAction::configureToolbars(this, SLOT(optionsConfigureToolbars()), actionCollection()); + + KStdAction::tipOfDay( this, SLOT( tipOfDay() ), actionCollection(), "help_tipofday" ); +} + +void Kig::saveProperties(KConfig* config) +{ + // the 'config' object points to the session managed + // config file. anything you write here will be available + // later when this app is restored + config->writePathEntry("fileName", m_part->url().path()); +} + +void Kig::readProperties(KConfig* config) +{ + // the 'config' object points to the session managed + // config file. this function is automatically called whenever + // the app is being restored. read in here whatever you wrote + // in 'saveProperties' + load ( KURL(config->readPathEntry("fileName"))); +} + +void Kig::load(const KURL& url) +{ + // we check for m_part not being 0, because in the case of us not + // finding our library, we would otherwise get a crash... + if ( m_part && m_part->openURL( url ) ) m_recentFilesAction->addURL( url ); +} + +void Kig::fileNew() +{ + // this slot is called whenever the File->New menu is selected, + // the New shortcut is pressed (usually CTRL+N) or the New toolbar + // button is clicked + + // create a new window if we aren't in the "initial state" ( see + // the KDE style guide on the file menu stuff...) + if ( ! m_part->url().isEmpty() || m_part->isModified() ) + (new Kig)->show(); +} + +void Kig::openURL(const KURL& url) +{ + // Called for opening a file by either the KRecentFilesAction or our + // own fileOpen() method. + // if we are in the "initial state", we open the url in this window: + if ( m_part->url().isEmpty() && ! m_part->isModified() ) + { + load( url ); + } + else + { + // otherwise we open it in a new window... + Kig* widget = new Kig; + widget->load(url); + widget->show(); + }; +} + +void Kig::optionsConfigureKeys() +{ +#if KDE_IS_VERSION( 3, 2, 90 ) + assert( false ); +#else + KKeyDialog dlg( true, this ); + dlg.insert( actionCollection() ); + dlg.insert( m_part->actionCollection() ); + (void) dlg.configure( true ); +#endif +} + +void Kig::optionsConfigureToolbars() +{ + saveMainWindowSettings(KGlobal::config(), "MainWindow"); + + // use the standard toolbar editor + KEditToolbar dlg(factory()); + connect(&dlg, SIGNAL(newToolbarConfig()), + this, SLOT(applyNewToolbarConfig())); + dlg.exec(); +} + +void Kig::applyNewToolbarConfig() +{ + applyMainWindowSettings(KGlobal::config(), "MainWindow"); +} + +bool Kig::queryClose() +{ + if (!m_part->isReadWrite() || !m_part->isModified()) return true; + switch( KMessageBox::warningYesNoCancel + ( + widget(), + i18n("Save changes to document %1?").arg(m_part->url().path()), + i18n("Save Changes?"),KStdGuiItem::save(),KStdGuiItem::discard() + )) + { + case KMessageBox::Yes: + if (m_part->save()) return true; + else return false; + break; + case KMessageBox::No: + return true; + break; + default: // cancel + return false; + break; + }; +} + +void Kig::dragEnterEvent(QDragEnterEvent* e) +{ + e->accept(KURLDrag::canDecode(e)); +} + +void Kig::dropEvent(QDropEvent* e) +{ + KURL::List urls; + if ( KURLDrag::decode (e, urls) ) + { + for (KURL::List::iterator u = urls.begin(); u != urls.end(); ++u) + { + Kig* k = new Kig; + k->show(); + k->load(*u); + }; + }; +} + +void Kig::fileOpen() +{ + QString formats = + i18n( "*.kig *.kigz *.kgeo *.seg|All Supported Files (*.kig *.kigz *.kgeo *.seg)\n" + "*.kig|Kig Documents (*.kig)\n" + "*.kigz|Compressed Kig Documents (*.kigz)\n" + "*.kgeo|KGeo Documents (*.kgeo)\n" + "*.seg|KSeg Documents (*.seg)\n" + "*.fgeo|Dr. Geo Documents (*.fgeo)\n" + "*.fig *.FIG|Cabri Documents (*.fig *.FIG)" ); + + // this slot is connected to the KStdAction::open action... + QString file_name = KFileDialog::getOpenFileName(":document", formats ); + + if (!file_name.isEmpty()) openURL(file_name); +} + +// ifdef's disabled, cause Qt moc doesn't handle ifdef's.. +// #ifdef KIG_DONT_USE_NEW_KMAINWINDOW_FEATURES +void Kig::optionsShowToolbar() +{ +#ifdef KIG_DONT_USE_NEW_KMAINWINDOW_FEATURES + if (m_toolbarAction->isChecked()) + toolBar()->show(); + else + toolBar()->hide(); +#else + assert( false ); +#endif +} + +void Kig::optionsShowStatusbar() +{ +#ifdef KIG_DONT_USE_NEW_KMAINWINDOW_FEATURES + if (m_statusbarAction->isChecked()) + statusBar()->show(); + else + statusBar()->hide(); +#else + assert( false ); +#endif +} +// #endif + +void Kig::tipOfDay() { + KTipDialog::showTip( "kig/tips", true ); +} + +void Kig::startupTipOfDay() { + KTipDialog::showTip( "kig/tips" ); +} diff --git a/kig/kig/kig.desktop b/kig/kig/kig.desktop new file mode 100644 index 00000000..b2e11e40 --- /dev/null +++ b/kig/kig/kig.desktop @@ -0,0 +1,121 @@ +[Desktop Entry] +Name=Kig +Name[bn]=কিগ +Name[hi]=केआईजी +Name[ne]=किग +Name[pa]=ਕਿਗ +Name[ta]=கிக௠+GenericName=Interactive Geometry +GenericName[af]=Interaktiewe Meetkunde +GenericName[be]=ІнтÑÑ€Ð°ÐºÑ‚Ñ‹ÑžÐ½Ð°Ñ Ð³ÐµÐ°Ð¼ÐµÑ‚Ñ€Ñ‹Ñ +GenericName[bg]=Интерактивна Ð³ÐµÐ¾Ð¼ÐµÑ‚Ñ€Ð¸Ñ +GenericName[bn]=ইনà§à¦Ÿà¦¾à¦°à¦…à§à¦¯à¦¾à¦•à¦Ÿà¦¿à¦ জà§à¦¯à¦¾à¦®à¦¿à¦¤à¦¿ +GenericName[bs]=Interaktivna geometrija +GenericName[ca]=Geometria interactiva +GenericName[cs]=Interaktivnà geometrie +GenericName[csb]=Interaktiwnô geòmetrëjô +GenericName[cy]=Geometreg Rhyngweithiol +GenericName[da]=Interaktiv geometri +GenericName[de]=Interaktive Geometrie +GenericName[el]=AλληλεπιδÏαστική γεωμετÏία +GenericName[eo]=Interaga geometrios +GenericName[es]=GeometrÃa interactiva +GenericName[et]=Interaktiivne geomeetria +GenericName[eu]=Geometria interaktiboa +GenericName[fa]=هندسۀ تعاملی +GenericName[fi]=Interaktiivinen Geometria +GenericName[fr]=Géométrie interactive +GenericName[ga]=Céimseata IdirghnÃomhach +GenericName[gl]=XeometrÃa Interactiva +GenericName[he]=×’×ומטריה ××™× ×˜×¨×קטיבית +GenericName[hi]=इंटरà¤à¤•à¥à¤Ÿà¤¿à¤µ जà¥à¤¯à¥‰à¤®à¤¿à¤¤à¥€ +GenericName[hr]=Interaktivna geometrija +GenericName[hu]=InteraktÃv geometria +GenericName[is]=Gagnvirk rúmfræði +GenericName[it]=Geometria interattiva +GenericName[ja]=幾何å¦ã¨ã®å¯¾è©± +GenericName[ka]=ინტერáƒáƒ¥áƒ¢áƒ˜áƒ£áƒ ი გეáƒáƒ›áƒ”ტრირ+GenericName[km]=ធរណីមាážáŸ’រ​អន្ážážšáž€áž˜áŸ’ម +GenericName[lt]=Interaktyvi geometrija +GenericName[ms]=Geometri Interaktif +GenericName[nb]=Interaktiv geometri +GenericName[nds]=Wesselwarken Geometrie +GenericName[ne]=अनà¥à¤¤à¤°à¥à¤•à¥à¤°à¤¿à¤¯à¤¾à¤¤à¥à¤®à¤• à¤à¥‚गोल +GenericName[nl]=Interactieve geometry +GenericName[nn]=Interaktiv geometri +GenericName[pa]=ਦਿਲ-ਖਿਚਵੀਂ ਜà©à¨®à©ˆà¨Ÿà¨°à©€ +GenericName[pl]=Interaktywna geometria +GenericName[pt]=Geometria Interactiva +GenericName[pt_BR]=Geometria interativa +GenericName[ru]=Ð˜Ð½Ñ‚ÐµÑ€Ð°ÐºÑ‚Ð¸Ð²Ð½Ð°Ñ Ð³ÐµÐ¾Ð¼ÐµÑ‚Ñ€Ð¸Ñ +GenericName[sk]=InteraktÃvna geometria +GenericName[sl]=Interaktivna geometrija +GenericName[sr]=Интерактивна геометрија +GenericName[sr@Latn]=Interaktivna geometrija +GenericName[sv]=Interaktiv geometri +GenericName[ta]= ஊடாடà¯à®®à¯ வடிவியல௠+GenericName[tg]=ГеометриÑи интерактивӣ +GenericName[tr]=EtkileÅŸimli Geometri +GenericName[uk]=Інтерактивна Ð³ÐµÐ¾Ð¼ÐµÑ‚Ñ€Ñ–Ñ +GenericName[ven]=Geometry yau shumea nayo +GenericName[vi]=Hình há»c TÆ°Æ¡ng tác +GenericName[xh]=Umyili Wesiboniso Esingaphakathi +GenericName[zh_CN]=äº¤äº’å‡ ä½• +GenericName[zh_TW]=交互å¼å¹¾ä½•ä½œåœ– +GenericName[zu]=Ukubekeka Kwebalazwe Kokukhulumisanayo +Comment=Explore Geometric Constructions +Comment[be]=ВывучÑнне геаметрычных фігур +Comment[bg]=Геометрични конÑтрукции +Comment[bn]=জà§à¦¯à¦¾à¦®à¦¿à¦¤à¦¿ ছবি আà¦à¦•à¦¾ চরà§à¦šà¦¾ করà§à¦¨ +Comment[bs]=Istražite geometrijske konstrukcije +Comment[ca]=Explora construccions geomètriques +Comment[cs]=Objevujte geometrické konstrukce +Comment[csb]=Exploatëjë geòmetriczne kònstrukcëjë +Comment[da]=Udforsk geometriske konstruktioner +Comment[de]=Geometrische Konstruktionen untersuchen +Comment[el]=ΕξεÏεÏνηση γεωμετÏικών κατασκευών +Comment[eo]=Explorado de geometriaj konstruaĵoj +Comment[es]=Exploración de construcciones geométricas +Comment[et]=Geomeetriliste kujundite tundmaõppimine +Comment[eu]=Arakatu geometria egiturak +Comment[fa]=کاوش ساختارهای هندسی +Comment[fi]=Tutki geometrisia rakenteita +Comment[fr]=Explorer les constructions géométriques +Comment[ga]=Taiscéal Tógálacha Céimseatúla +Comment[gl]=Explore as Figuras Xeométricas +Comment[he]=חקור ×ž×‘× ×™× ×’××•×ž×˜×¨×™×™× +Comment[hr]=Upoznajte geometrijske konstrukcije +Comment[hu]=Geometriai oktatóprogram +Comment[is]=Kanna rúmfræðilega byggingu forma +Comment[it]=Esplora le costruzioni geometriche +Comment[ja]=幾何å¦ã¨ã®å¯¾è©± +Comment[ka]=გეáƒáƒ›áƒ”ტრიულ ფიგურáƒáƒ—რშესწáƒáƒ•áƒšáƒ +Comment[km]=រុករក​សំណង់​ធរណីមាážáŸ’ážš +Comment[lt]=Geometrinių konstrukcijų tyrinÄ—jimas +Comment[ms]=Jelahan Pembinaan Geometrik +Comment[nb]=Utforsk geometriske konstruksjoner +Comment[nds]=Geometersch Konstrukschonen bekieken +Comment[ne]=à¤à¥‚गोल बनावट अनà¥à¤µà¥‡à¤·à¤£ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Ruimtelijke constructies verkennen +Comment[nn]=Utforsk geometriske konstruksjonar +Comment[pl]=Zabawa konstrukcjami geometrycznymi +Comment[pt]=Explorar Construções Geométricas +Comment[pt_BR]=Explore construções geométricas +Comment[ru]=Работа Ñ Ð³ÐµÐ¾Ð¼ÐµÑ‚Ñ€Ð¸Ñ‡ÐµÑкими поÑтроениÑми +Comment[sk]=Prieskum geometrických konÅ¡trukcià +Comment[sl]=Raziskovanje geometrijskih konstrukcij +Comment[sr]=ИÑтражујте геометријÑке конÑтрукције +Comment[sr@Latn]=Istražujte geometrijske konstrukcije +Comment[sv]=Utforska geometriska konstruktioner +Comment[tr]=Geometik Åžekilleri KeÅŸfedin +Comment[uk]=ДоÑÐ»Ñ–Ð´Ð¶ÐµÐ½Ð½Ñ Ð³ÐµÐ¾Ð¼ÐµÑ‚Ñ€Ð¸Ñ‡Ð½Ð¸Ñ… конÑтрукцій +Comment[vi]=Khám phá các phép Dụng Hình há»c +Comment[zh_CN]=æŽ¢ç´¢å‡ ä½•æž„é€ çš„ä¸–ç•Œ +Comment[zh_TW]=作出幾何圖形 +Exec=kig %i %m -caption "%c" +MimeType=application/x-kig;application/x-kgeo; +Icon=kig +Type=Application +DocPath=kig/index.html +Terminal=false +Categories=Qt;KDE;Education;Math; diff --git a/kig/kig/kig.h b/kig/kig/kig.h new file mode 100644 index 00000000..d6ad9fea --- /dev/null +++ b/kig/kig/kig.h @@ -0,0 +1,148 @@ +// This file is part of Kig, a KDE program for Interactive Geometry... +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_KIG_H +#define KIG_KIG_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <kdeversion.h> + +#ifdef KDE_IS_VERSION +#if KDE_IS_VERSION( 3, 1, 90 ) +#undef KIG_DONT_USE_NEW_KMAINWINDOW_FEATURES +#else +#define KIG_DONT_USE_NEW_KMAINWINDOW_FEATURES +#endif +#else +#define KIG_DONT_USE_NEW_KMAINWINDOW_FEATURES +#endif + +#include <kapplication.h> +#include <kparts/mainwindow.h> +#include <dcopclient.h> + +class KToggleAction; +class KRecentFilesAction; + +/** + * This is the application "Shell". It has a menubar, toolbar, and + * statusbar but relies on the "Part" to do all the real work. + */ +class Kig : public KParts::MainWindow +{ + Q_OBJECT + public: + /** + * Default Constructor + */ + Kig(); + + /** + * Default Destructor + */ + virtual ~Kig(); + + public slots: + /** + * Open file in this window + * \param file file to open + */ + void load (const KURL& file); + + /** + * this opens the file specified in \p s in a new window + * + * \param s the url of the file to open + */ + void openURL (const QString& s) { openURL(KURL(s)); } + void openURL (const KURL& s); + + protected: + + /** + * The user started dragging something onto us... + * + * \param e + */ + void dragEnterEvent(QDragEnterEvent* e); + + /** + * The user dropped something onto us... + * + * \param e + */ + void dropEvent (QDropEvent* e); + + /** + * this is called by the framework before closing the window, to + * allow the user to save his changes... returning false cancels the + * close request... + */ + bool queryClose(); + + /** + * This method is called when it is time for the app to save its + * properties for session management purposes. + */ + void saveProperties(KConfig *); + + /** + * This method is called when this app is restored. The KConfig + * object points to the session management config file that was saved + * with \ref saveProperties + */ + void readProperties(KConfig *); + + private slots: + void fileNew(); + void fileOpen(); + // Qt moc doesn't handle ifdef's, so i'm disabling it.. +// #ifdef KIG_DONT_USE_NEW_KMAINWINDOW_FEATURES + void optionsShowToolbar(); + void optionsShowStatusbar(); +// #endif +// #if KDE_IS_VERSION( 3, 2, 90 ) + void optionsConfigureKeys(); +// #endif + void optionsConfigureToolbars(); + + void applyNewToolbarConfig(); + + void tipOfDay(); + void startupTipOfDay(); + + private: + void setupActions(); + + KParts::ReadWritePart *m_part; + +//#ifdef KIG_DONT_USE_NEW_KMAINWINDOW_FEATURES + KToggleAction *m_toolbarAction; + KToggleAction *m_statusbarAction; +//#endif + KRecentFilesAction *m_recentFilesAction; + + KConfig* config; + + static bool kimageioRegistered; +}; + +#endif // KIG_KIG_H diff --git a/kig/kig/kig_commands.cpp b/kig/kig/kig_commands.cpp new file mode 100644 index 00000000..b9846fa6 --- /dev/null +++ b/kig/kig/kig_commands.cpp @@ -0,0 +1,398 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#include "kig_commands.h" +#include "kig_commands.moc" + +#include "kig_part.h" +#include "kig_document.h" +#include "kig_view.h" + +#include "../modes/mode.h" +#include "../objects/object_imp.h" +#include "../objects/object_drawer.h" +#include "../misc/calcpaths.h" +#include "../misc/coordinate_system.h" + +#include <vector> + +using std::vector; +using std::max; +using std::min; + +class KigCommand::Private +{ +public: + Private( KigPart& d ) : doc( d ) {} + KigPart& doc; + vector<KigCommandTask*> tasks; +}; + +KigCommand::KigCommand( KigPart& doc, const QString& name ) + : KNamedCommand(name), d( new Private( doc ) ) +{ +} + +KigCommand::~KigCommand() +{ + for ( uint i = 0; i < d->tasks.size(); ++i ) + delete d->tasks[i]; + delete d; +} + +void KigCommand::execute() +{ + for ( uint i = 0; i < d->tasks.size(); ++i ) + d->tasks[i]->execute( d->doc ); + d->doc.redrawScreen(); +} + +void KigCommand::unexecute() +{ + for ( uint i = 0; i < d->tasks.size(); ++i ) + d->tasks[i]->unexecute( d->doc ); + d->doc.redrawScreen(); +} + +void KigCommand::addTask( KigCommandTask* t ) +{ + d->tasks.push_back( t ); +} + +KigCommand* KigCommand::removeCommand( KigPart& doc, ObjectHolder* o ) +{ + std::vector<ObjectHolder*> os; + os.push_back( o ); + return removeCommand( doc, os ); +} + +KigCommand* KigCommand::addCommand( KigPart& doc, ObjectHolder* o ) +{ + std::vector<ObjectHolder*> os; + os.push_back( o ); + return addCommand( doc, os ); +} + +KigCommand* KigCommand::removeCommand( KigPart& doc, const std::vector<ObjectHolder*>& os ) +{ + assert( os.size() > 0 ); + QString text; + if ( os.size() == 1 ) + text = os.back()->imp()->type()->removeAStatement(); + else + text = i18n( "Remove %1 Objects" ).arg( os.size() ); + KigCommand* ret = new KigCommand( doc, text ); + ret->addTask( new RemoveObjectsTask( os ) ); + return ret; +} + +KigCommand* KigCommand::addCommand( KigPart& doc, const std::vector<ObjectHolder*>& os ) +{ + QString text; + if ( os.size() == 1 ) + text = os.back()->imp()->type()->addAStatement(); + else + text = i18n( "Add %1 Objects" ).arg( os.size() ); + KigCommand* ret = new KigCommand( doc, text ); + ret->addTask( new AddObjectsTask( os ) ); + return ret; +} + +KigCommand* KigCommand::changeCoordSystemCommand( KigPart& doc, CoordinateSystem* s ) +{ + QString text = CoordinateSystemFactory::setCoordinateSystemStatement( s->id() ); + KigCommand* ret = new KigCommand( doc, text ); + ret->addTask( new ChangeCoordSystemTask( s ) ); + return ret; +} + +KigCommandTask::KigCommandTask() +{ +} + +KigCommandTask::~KigCommandTask() +{ +} + +AddObjectsTask::AddObjectsTask( const std::vector<ObjectHolder*>& os) + : KigCommandTask(), undone( true ), mobjs( os ) +{ +} + +void AddObjectsTask::execute( KigPart& doc ) +{ + doc._addObjects( mobjs ); + undone = false; +} + +void AddObjectsTask::unexecute( KigPart& doc ) +{ + doc._delObjects( mobjs ); + undone = true; +} + +AddObjectsTask::~AddObjectsTask() +{ + if ( undone ) + for ( std::vector<ObjectHolder*>::iterator i = mobjs.begin(); + i != mobjs.end(); ++i ) + delete *i; +} + +RemoveObjectsTask::RemoveObjectsTask( const std::vector<ObjectHolder*>& os ) + : AddObjectsTask( os ) +{ + undone = false; +} + +void RemoveObjectsTask::execute( KigPart& doc ) +{ + AddObjectsTask::unexecute( doc ); +} + +void RemoveObjectsTask::unexecute( KigPart& doc ) +{ + AddObjectsTask::execute( doc ); +} + +ChangeObjectConstCalcerTask::ChangeObjectConstCalcerTask( ObjectConstCalcer* calcer, ObjectImp* newimp ) + : KigCommandTask(), mcalcer( calcer ), mnewimp( newimp ) +{ +} + +void ChangeObjectConstCalcerTask::execute( KigPart& doc ) +{ + mnewimp = mcalcer->switchImp( mnewimp ); + + std::set<ObjectCalcer*> allchildren = getAllChildren( mcalcer.get() ); + std::vector<ObjectCalcer*> allchildrenvect( allchildren.begin(), allchildren.end() ); + allchildrenvect = calcPath( allchildrenvect ); + for ( std::vector<ObjectCalcer*>::iterator i = allchildrenvect.begin(); + i != allchildrenvect.end(); ++i ) + ( *i )->calc( doc.document() ); +} + +void ChangeObjectConstCalcerTask::unexecute( KigPart& doc ) +{ + execute( doc ); +} + +struct MoveDataStruct +{ + ObjectConstCalcer* o; + ObjectImp* oldimp; + MoveDataStruct( ObjectConstCalcer* io, ObjectImp* oi ) + : o( io ), oldimp( oi ) { } +}; + +class MonitorDataObjects::Private +{ +public: + vector<MoveDataStruct> movedata; +}; + +MonitorDataObjects::MonitorDataObjects( const std::vector<ObjectCalcer*>& objs ) + : d( new Private ) +{ + monitor( objs ); +} + +void MonitorDataObjects::monitor( const std::vector<ObjectCalcer*>& objs ) +{ + for ( std::vector<ObjectCalcer*>::const_iterator i = objs.begin(); i != objs.end(); ++i ) + if ( dynamic_cast<ObjectConstCalcer*>( *i ) ) + { + MoveDataStruct n( static_cast<ObjectConstCalcer*>( *i ), (*i)->imp()->copy() ); + d->movedata.push_back( n ); + }; +} + +void MonitorDataObjects::finish( KigCommand* comm ) +{ + for ( uint i = 0; i < d->movedata.size(); ++i ) + { + ObjectConstCalcer* o = d->movedata[i].o; + if ( ! d->movedata[i].oldimp->equals( *o->imp() ) ) + { + ObjectImp* newimp = o->switchImp( d->movedata[i].oldimp ); + comm->addTask( new ChangeObjectConstCalcerTask( o, newimp ) ); + } + else + delete d->movedata[i].oldimp; + }; + d->movedata.clear(); +} + +MonitorDataObjects::~MonitorDataObjects() +{ + assert( d->movedata.empty() ); + delete d; +} + +ChangeCoordSystemTask::ChangeCoordSystemTask( CoordinateSystem* s ) + : KigCommandTask(), mcs( s ) +{ +} + +void ChangeCoordSystemTask::execute( KigPart& doc ) +{ + mcs = doc.document().switchCoordinateSystem( mcs ); + std::vector<ObjectCalcer*> calcpath = calcPath( getAllCalcers( doc.document().objects() ) ); + for ( std::vector<ObjectCalcer*>::iterator i = calcpath.begin(); i != calcpath.end(); ++i ) + ( *i )->calc( doc.document() ); + doc.coordSystemChanged( doc.document().coordinateSystem().id() ); +} + +void ChangeCoordSystemTask::unexecute( KigPart& doc ) +{ + execute( doc ); +} + +ChangeCoordSystemTask::~ChangeCoordSystemTask() +{ + delete mcs; +} + +class ChangeParentsAndTypeTask::Private +{ +public: + ObjectTypeCalcer* o; + std::vector<ObjectCalcer::shared_ptr> newparents; + const ObjectType* newtype; +}; + +ChangeParentsAndTypeTask::~ChangeParentsAndTypeTask() +{ + delete d; +} + +ChangeParentsAndTypeTask::ChangeParentsAndTypeTask( + ObjectTypeCalcer* o, const std::vector<ObjectCalcer*>& newparents, + const ObjectType* newtype ) + : KigCommandTask(), d( new Private ) +{ + d->o = o; + std::copy( newparents.begin(), newparents.end(), + std::back_inserter( d->newparents ) ); + d->newtype = newtype; +} + +void ChangeParentsAndTypeTask::execute( KigPart& doc ) +{ + const ObjectType* oldtype = d->o->type(); + d->o->setType( d->newtype ); + d->newtype = oldtype; + + std::vector<ObjectCalcer*> oldparentso = d->o->parents(); + std::vector<ObjectCalcer::shared_ptr> oldparents( + oldparentso.begin(), oldparentso.end() ); + std::vector<ObjectCalcer*> newparents; + for ( std::vector<ObjectCalcer::shared_ptr>::iterator i = d->newparents.begin(); + i != d->newparents.end(); ++i ) + newparents.push_back( i->get() ); + d->o->setParents( newparents ); + d->newparents = oldparents; + + for ( std::vector<ObjectCalcer*>::iterator i = newparents.begin(); i != newparents.end(); ++i ) + ( *i )->calc( doc.document() ); + d->o->calc( doc.document() ); + std::set<ObjectCalcer*> allchildren = getAllChildren( d->o ); + std::vector<ObjectCalcer*> allchildrenvect( allchildren.begin(), allchildren.end() ); + allchildrenvect = calcPath( allchildrenvect ); + for ( std::vector<ObjectCalcer*>::iterator i = allchildrenvect.begin(); + i != allchildrenvect.end(); ++i ) + ( *i )->calc( doc.document() ); +} + +void ChangeParentsAndTypeTask::unexecute( KigPart& doc ) +{ + execute( doc ); +} + +class KigViewShownRectChangeTask::Private +{ +public: + Private( KigWidget& view, const Rect& r ) : v( view ), rect( r ) { } + KigWidget& v; + Rect rect; +}; + +KigViewShownRectChangeTask::KigViewShownRectChangeTask( + KigWidget& v, const Rect& newrect ) + : KigCommandTask() +{ + d = new Private( v, newrect ); +} + +KigViewShownRectChangeTask::~KigViewShownRectChangeTask() +{ + delete d; +} + +void KigViewShownRectChangeTask::execute( KigPart& doc ) +{ + Rect oldrect = d->v.showingRect(); + d->v.setShowingRect( d->rect ); + doc.mode()->redrawScreen( &d->v ); + d->v.updateScrollBars(); + d->rect = oldrect; +} + +void KigViewShownRectChangeTask::unexecute( KigPart& doc ) +{ + execute( doc ); +} + +ChangeObjectDrawerTask::~ChangeObjectDrawerTask() +{ + delete mnewdrawer; +} + +ChangeObjectDrawerTask::ChangeObjectDrawerTask( + ObjectHolder* holder, ObjectDrawer* newdrawer ) + : KigCommandTask(), mholder( holder ), mnewdrawer( newdrawer ) +{ +} + +void ChangeObjectDrawerTask::execute( KigPart& ) +{ + mnewdrawer = mholder->switchDrawer( mnewdrawer ); +} + +void ChangeObjectDrawerTask::unexecute( KigPart& doc ) +{ + execute( doc ); +} + +MonitorDataObjects::MonitorDataObjects( ObjectCalcer* c ) + : d( new Private ) +{ + if ( dynamic_cast<ObjectConstCalcer*>( c ) ) + { + MoveDataStruct n( static_cast<ObjectConstCalcer*>( c ), c->imp()->copy() ); + d->movedata.push_back( n ); + }; +} + +ChangeObjectConstCalcerTask::~ChangeObjectConstCalcerTask() +{ + delete mnewimp; +} + diff --git a/kig/kig/kig_commands.h b/kig/kig/kig_commands.h new file mode 100644 index 00000000..8e4fd044 --- /dev/null +++ b/kig/kig/kig_commands.h @@ -0,0 +1,217 @@ +/* + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + + +#ifndef KIG_COMMANDS_H +#define KIG_COMMANDS_H + +#include <kcommand.h> +#include <klocale.h> +#include <kdebug.h> + +#include "../objects/object_holder.h" + +class KigDocument; +class KigPart; +class CoordinateSystem; + +class KigCommandTask; +class KigWidget; +class Rect; + +/** + * a KigCommand represents almost every action performed in Kig. + * Used mainly in the Undo/Redo stuff... + */ +class KigCommand + : public QObject, public KNamedCommand +{ + Q_OBJECT + class Private; + Private* d; +public: + KigCommand( KigPart& inDoc, const QString& name ); + ~KigCommand(); + + /** + * To avoid confusion, this doesn't add a command to anything, this + * creates an AddCommand ;) + */ + static KigCommand* addCommand( KigPart& doc, const std::vector<ObjectHolder*>& os ); + static KigCommand* addCommand( KigPart& doc, ObjectHolder* os ); + /** + * make sure that when you delete something, you are also deleting + * its parents. This class assumes you've done that. + * \ref KigDocument::delObjects() takes care of this for you. + */ + static KigCommand* removeCommand( KigPart& doc, const std::vector<ObjectHolder*>& os ); + static KigCommand* removeCommand( KigPart& doc, ObjectHolder* o ); + + static KigCommand* changeCoordSystemCommand( KigPart& doc, CoordinateSystem* s ); + + void addTask( KigCommandTask* ); + + void execute(); + void unexecute(); +}; + +class KigCommandTask +{ +public: + KigCommandTask(); + virtual ~KigCommandTask(); + + virtual void execute( KigPart& doc ) = 0; + virtual void unexecute( KigPart& doc ) = 0; +}; + +class AddObjectsTask + : public KigCommandTask +{ +public: + AddObjectsTask( const std::vector<ObjectHolder*>& os); + ~AddObjectsTask (); + void execute( KigPart& doc ); + void unexecute( KigPart& doc ); +protected: + bool undone; + + std::vector<ObjectHolder*> mobjs; +}; + +class RemoveObjectsTask + : public AddObjectsTask +{ +public: + RemoveObjectsTask( const std::vector<ObjectHolder*>& os ); + void execute( KigPart& ); + void unexecute( KigPart& ); +}; + +class ChangeObjectConstCalcerTask + : public KigCommandTask +{ +public: + ChangeObjectConstCalcerTask( ObjectConstCalcer* calcer, ObjectImp* newimp ); + ~ChangeObjectConstCalcerTask(); + + void execute( KigPart& ); + void unexecute( KigPart& ); +protected: + ObjectConstCalcer::shared_ptr mcalcer; + ObjectImp* mnewimp; +}; + +/** + * this class monitors a set of DataObjects for changes and returns an + * appropriate ChangeObjectImpsCommand if necessary.. + * E.g. MovingMode wants to move certain objects, so it monitors all + * the parents of the explicitly moving objects: + * \code + * MonitorDataObjects mon( getAllParents( emo ) ); + * \endcode + * It then moves them around, and when it is finished, it asks to add + * the KigCommandTasks to a KigCommand, and applies that.. + * \code + * KigCommand* comm = new KigCommand( doc, i18n( "Move Stuff" ) ); + * mon.finish( comm ); + * \endcode + */ +class MonitorDataObjects +{ + class Private; + Private* d; +public: + /** + * all the DataObjects in \p objs will be watched.. + */ + MonitorDataObjects( const std::vector<ObjectCalcer*>& objs ); + MonitorDataObjects( ObjectCalcer* c ); + ~MonitorDataObjects(); + + /** + * add \p objs to the list of objs to be watched, and save their + * current imp's.. + */ + void monitor( const std::vector<ObjectCalcer*>& objs ); + + /** + * add the generated KigCommandTasks to the command \p comm .. + * monitoring stops after this is called.. + */ + void finish( KigCommand* comm ); +}; + +class ChangeCoordSystemTask + : public KigCommandTask +{ + CoordinateSystem* mcs; +public: + /** + * a command that changes the coordinate-system to \p s .. + */ + ChangeCoordSystemTask( CoordinateSystem* s ); + ~ChangeCoordSystemTask(); + + void execute( KigPart& doc ); + void unexecute( KigPart& doc ); +}; + +class ChangeParentsAndTypeTask + : public KigCommandTask +{ + class Private; + Private* d; +public: + ChangeParentsAndTypeTask( ObjectTypeCalcer* o, const std::vector<ObjectCalcer*>& newparents, + const ObjectType* newtype ); + ~ChangeParentsAndTypeTask(); + + void execute( KigPart& doc ); + void unexecute( KigPart& doc ); +}; + +class KigViewShownRectChangeTask + : public KigCommandTask +{ + class Private; + Private* d; +public: + KigViewShownRectChangeTask( KigWidget& v, const Rect& newrect ); + ~KigViewShownRectChangeTask(); + + void execute( KigPart& doc ); + void unexecute( KigPart& doc ); +}; + +class ChangeObjectDrawerTask + : public KigCommandTask +{ + ObjectHolder* mholder; + ObjectDrawer* mnewdrawer; +public: + ChangeObjectDrawerTask( ObjectHolder* holder, ObjectDrawer* newdrawer ); + ~ChangeObjectDrawerTask(); + + void execute( KigPart& doc ); + void unexecute( KigPart& doc ); +}; + +#endif diff --git a/kig/kig/kig_document.cc b/kig/kig/kig_document.cc new file mode 100644 index 00000000..884b41c5 --- /dev/null +++ b/kig/kig/kig_document.cc @@ -0,0 +1,200 @@ +// Copyright (C) 2004 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "kig_document.h" + +#include "../objects/object_calcer.h" +#include "../objects/object_holder.h" +#include "../objects/point_imp.h" +#include "../objects/polygon_imp.h" +#include "../misc/coordinate_system.h" +#include "../misc/rect.h" + +#include <assert.h> + +KigDocument::KigDocument( std::set<ObjectHolder*> objects, CoordinateSystem* coordsystem, + bool showgrid, bool showaxes, bool nv ) + : mobjects( objects ), mcoordsystem( coordsystem ), mshowgrid( showgrid ), + mshowaxes( showaxes ), mnightvision( nv ) +{ +} + +const CoordinateSystem& KigDocument::coordinateSystem() const +{ + assert( mcoordsystem ); + return *mcoordsystem; +} + +const std::vector<ObjectHolder*> KigDocument::objects() const +{ + return std::vector<ObjectHolder*>( mobjects.begin(), mobjects.end() ); +} + +const std::set<ObjectHolder*>& KigDocument::objectsSet() const +{ + return mobjects; +} + +void KigDocument::setCoordinateSystem( CoordinateSystem* s ) +{ + delete switchCoordinateSystem( s ); +} + +CoordinateSystem* KigDocument::switchCoordinateSystem( CoordinateSystem* s ) +{ + CoordinateSystem* ret = mcoordsystem; + mcoordsystem = s; + return ret; +} + +std::vector<ObjectHolder*> KigDocument::whatAmIOn( const Coordinate& p, const KigWidget& w ) const +{ + std::vector<ObjectHolder*> ret; + std::vector<ObjectHolder*> curves; + std::vector<ObjectHolder*> fatobjects; + for ( std::set<ObjectHolder*>::const_iterator i = mobjects.begin(); + i != mobjects.end(); ++i ) + { + if(!(*i)->contains(p, w, mnightvision)) continue; + if ( (*i)->imp()->inherits( PointImp::stype() ) ) ret.push_back( *i ); + else + if ( !(*i)->imp()->inherits( PolygonImp::stype() ) ) curves.push_back( *i ); + else fatobjects.push_back( *i ); + }; + std::copy( curves.begin(), curves.end(), std::back_inserter( ret ) ); + std::copy( fatobjects.begin(), fatobjects.end(), std::back_inserter( ret ) ); + return ret; +} + +std::vector<ObjectHolder*> KigDocument::whatIsInHere( const Rect& p, const KigWidget& w ) +{ + std::vector<ObjectHolder*> ret; + std::vector<ObjectHolder*> nonpoints; + for ( std::set<ObjectHolder*>::const_iterator i = mobjects.begin(); + i != mobjects.end(); ++i ) + { + if(! (*i)->inRect( p, w ) ) continue; + if ( (*i)->imp()->inherits( PointImp::stype() ) ) ret.push_back( *i ); + else nonpoints.push_back( *i ); + }; + std::copy( nonpoints.begin(), nonpoints.end(), std::back_inserter( ret ) ); + return ret; +} + +Rect KigDocument::suggestedRect() const +{ + bool rectInited = false; + Rect r(0.,0.,0.,0.); + for ( std::set<ObjectHolder*>::const_iterator i = mobjects.begin(); + i != mobjects.end(); ++i ) + { + if ( (*i)->shown() ) + { + Rect cr = (*i)->imp()->surroundingRect(); + if ( ! cr.valid() ) continue; + if( !rectInited ) + { + r = cr; + rectInited = true; + } + else + r.eat( cr ); + }; + }; + + if ( ! rectInited ) + return Rect( -5.5, -5.5, 11., 11. ); + r.setContains( Coordinate( 0, 0 ) ); + if( r.width() == 0 ) r.setWidth( 1 ); + if( r.height() == 0 ) r.setHeight( 1 ); + Coordinate center = r.center(); + r *= 2; + r.setCenter(center); + return r; +} + +void KigDocument::addObject( ObjectHolder* o ) +{ + mobjects.insert( o ); +} + +void KigDocument::addObjects( const std::vector<ObjectHolder*>& os ) +{ + for ( std::vector<ObjectHolder*>::const_iterator i = os.begin(); + i != os.end(); ++i ) + ( *i )->calc( *this ); + std::copy( os.begin(), os.end(), std::inserter( mobjects, mobjects.begin() ) ); +} + +void KigDocument::delObject( ObjectHolder* o ) +{ + mobjects.erase( o ); +} + +void KigDocument::delObjects( const std::vector<ObjectHolder*>& os ) +{ + for ( std::vector<ObjectHolder*>::const_iterator i = os.begin(); + i != os.end(); ++i ) + mobjects.erase( *i ); +} + +KigDocument::KigDocument() + : mcoordsystem( new EuclideanCoords ) +{ + mshowgrid = true; + mshowaxes = true; + mnightvision = false; +} + +KigDocument::~KigDocument() +{ + typedef std::set<ObjectHolder*> s; + for ( s::iterator i = mobjects.begin(); i != mobjects.end(); ++i ) { + delete *i; + } + delete mcoordsystem; +} + +void KigDocument::setGrid( bool showgrid ) +{ + mshowgrid = showgrid; +} + +const bool KigDocument::grid() const +{ + return mshowgrid; +} + +void KigDocument::setAxes( bool showaxes ) +{ + mshowaxes = showaxes; +} + +void KigDocument::setNightVision( bool nv ) +{ + mnightvision = nv; +} + +const bool KigDocument::axes() const +{ + return mshowaxes; +} + +const bool KigDocument::getNightVision() const +{ + return mnightvision; +} diff --git a/kig/kig/kig_document.h b/kig/kig/kig_document.h new file mode 100644 index 00000000..6bcdb623 --- /dev/null +++ b/kig/kig/kig_document.h @@ -0,0 +1,137 @@ +// Copyright (C) 2004 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_KIG_KIG_DOCUMENT_H +#define KIG_KIG_KIG_DOCUMENT_H + +#include <set> +#include <vector> + +class Coordinate; +class CoordinateSystem; +class KigWidget; +class ObjectHolder; +class Rect; + +/** + * KigDocument is the class holding the real data in a Kig document. + * It owns a set of Objects, and a CoordinateSystem, which comprise + * the entire content of a Kig document. + */ +class KigDocument { + /** + * Here we keep the objects in the document. These are only the + * objects that the user is aware of. Other objects exist as well, + * but there's no ObjectHolder for them, and they only exist because + * some other ObjectCalcer has them as its ancestor. + */ + std::set<ObjectHolder*> mobjects; + + /** + * The CoordinateSystem as the user sees it: this has little to do + * with the internal coordinates of the objects... It really serves + * to translate user coordinates from and to internal coordinates. + */ + CoordinateSystem* mcoordsystem; + /** + * Whether to show the grid. + */ + bool mshowgrid; + /** + * Whether to show the axes. + */ + bool mshowaxes; + /** + * Whether to enable visibility of hidden objects. + */ + bool mnightvision; +public: + KigDocument(); + KigDocument( std::set<ObjectHolder*> objects, CoordinateSystem* coordsystem, + bool showgrid = true, bool showaxes = true, bool nv = false ); + ~KigDocument(); + + const CoordinateSystem& coordinateSystem() const; + const bool grid() const; + const bool axes() const; + const bool getNightVision() const; + /** + * Get a hold of the objects of this KigDocument. + */ + const std::vector<ObjectHolder*> objects() const; + const std::set<ObjectHolder*>& objectsSet() const; + + /** + * sets the coordinate system to \p s , and returns the old one.. + */ + CoordinateSystem* switchCoordinateSystem( CoordinateSystem* s ); + + /** + * sets the coordinate system to \p s , and deletes the old one.. + */ + void setCoordinateSystem( CoordinateSystem* s ); + + /** + * set to show/hide the grid. + */ + void setGrid( bool showgrid ); + + /** + * set to show/hide the grid. + */ + void setAxes( bool showaxes ); + + /** + * set to enable/disable night-vision (visibility of hidden objects) + */ + void setNightVision( bool nv ); + + /** + * Return a vector of objects that contain the given point. + */ + std::vector<ObjectHolder*> whatAmIOn( const Coordinate& p, const KigWidget& w ) const; + + /** + * Return a vector of objects that are in the given Rect. + */ + std::vector<ObjectHolder*> whatIsInHere( const Rect& p, const KigWidget& ); + + /** + * Return a rect containing most of the objects, which would be a + * fine suggestion to map to the widget... + */ + Rect suggestedRect() const; + + /** + * Add the objects \p o to the document. + */ + void addObject( ObjectHolder* oObject ); + /** + * Add the objects \p os to the document. + */ + void addObjects( const std::vector<ObjectHolder*>& os); + /** + * Remove the object \p o from the document. + */ + void delObject( ObjectHolder* o ); + /** + * Remove the objects \p os from the document. + */ + void delObjects( const std::vector<ObjectHolder*>& os ); +}; + +#endif diff --git a/kig/kig/kig_iface.cpp b/kig/kig/kig_iface.cpp new file mode 100644 index 00000000..1f666a74 --- /dev/null +++ b/kig/kig/kig_iface.cpp @@ -0,0 +1,31 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + + +#include "kig_iface.h" + +KigIface::KigIface() + : DCOPObject("KigIface") +{ +} + +KigIface::~KigIface() +{ +} diff --git a/kig/kig/kig_iface.h b/kig/kig/kig_iface.h new file mode 100644 index 00000000..d3f74e19 --- /dev/null +++ b/kig/kig/kig_iface.h @@ -0,0 +1,37 @@ +/* + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + + +#ifndef KIG_IFACE_H +#define KIG_IFACE_H + +#include <dcopobject.h> + +class KigIface : virtual public DCOPObject +{ + K_DCOP +public: + KigIface(); + ~KigIface(); +k_dcop: + virtual void openURL(const QString& s) = 0; +}; + +#endif diff --git a/kig/kig/kig_part.cpp b/kig/kig/kig_part.cpp new file mode 100644 index 00000000..163f5429 --- /dev/null +++ b/kig/kig/kig_part.cpp @@ -0,0 +1,1041 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#include "kig_part.h" +#include "kig_part.moc" + +#include "aboutdata.h" +#include "kig_commands.h" +#include "kig_document.h" +#include "kig_view.h" + +#include "../filters/exporter.h" +#include "../filters/filter.h" +#include "../misc/builtin_stuff.h" +#include "../misc/calcpaths.h" +#include "../misc/coordinate_system.h" +#include "../misc/guiaction.h" +#include "../misc/kigpainter.h" +#include "../misc/lists.h" +#include "../misc/object_constructor.h" +#include "../misc/screeninfo.h" +#include "../modes/normal.h" +#include "../objects/object_drawer.h" +#include "../objects/point_imp.h" + +#include <algorithm> +#include <functional> + +#include <kaction.h> +#include <kapplication.h> +#include <kdebug.h> +#include <kfiledialog.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <kinstance.h> +#include <klocale.h> +#include <kmainwindow.h> +#include <kmessagebox.h> +#include <kmimetype.h> +#include <kprinter.h> +#include <kstandarddirs.h> +#include <kstdaction.h> +#include <ktoolbar.h> +#include <kparts/genericfactory.h> +#include <kdeprint/kprintdialogpage.h> + +#include <qcheckbox.h> +#include <qfile.h> +#include <qlayout.h> +#include <qpaintdevicemetrics.h> +#include <qsizepolicy.h> +#include <qtimer.h> +#if QT_VERSION >= 0x030100 +#include <qeventloop.h> +#endif + +using namespace std; + +static const QString typesFile = "macros.kigt"; + +// export this library... +typedef KParts::GenericFactory<KigPart> KigPartFactory; +K_EXPORT_COMPONENT_FACTORY ( libkigpart, KigPartFactory ) + +KAboutData* KigPart::createAboutData() +{ + return kigAboutData( "kig", I18N_NOOP( "KigPart" ) ); +} + +class SetCoordinateSystemAction + : public KSelectAction +{ + KigPart& md; +public: + SetCoordinateSystemAction( KigPart& d, KActionCollection* parent ); + void slotActivated( int index ); +}; + +SetCoordinateSystemAction::SetCoordinateSystemAction( + KigPart& d, KActionCollection* parent ) + : KSelectAction( i18n( "&Set Coordinate System" ), 0, parent, "settings_set_coordinate_system" ), + md( d ) +{ + setItems( CoordinateSystemFactory::names() ); + setCurrentItem( md.document().coordinateSystem().id() ); +} + +void SetCoordinateSystemAction::slotActivated( int index ) +{ + CoordinateSystem* sys = CoordinateSystemFactory::build( index ); + assert( sys ); + md.history()->addCommand( KigCommand::changeCoordSystemCommand( md, sys ) ); + setCurrentItem( index ); +} + +class KigPrintDialogPage + : public KPrintDialogPage +{ +public: + KigPrintDialogPage( QWidget* parent = 0, const char* name = 0 ); + ~KigPrintDialogPage(); + + void getOptions( QMap<QString,QString>& opts, bool ); + void setOptions( const QMap<QString,QString>& opts ); + bool isValid( QString& ); + +private: + QCheckBox *showgrid; + QCheckBox *showaxes; +}; + +KigPrintDialogPage::KigPrintDialogPage( QWidget* parent, const char* name ) + : KPrintDialogPage( parent, name ) +{ + setTitle( i18n( "Kig Options" ) ); + + QVBoxLayout* vl = new QVBoxLayout( this, 0 , 11 ); + + showgrid = new QCheckBox( i18n( "Show grid" ), this ); + vl->addWidget( showgrid ); + + showaxes = new QCheckBox( i18n( "Show axes" ), this ); + vl->addWidget( showaxes ); + + vl->addItem( new QSpacerItem( 10, 10, QSizePolicy::Fixed, QSizePolicy::Expanding ) ); +} + +KigPrintDialogPage::~KigPrintDialogPage() +{ +} + +void KigPrintDialogPage::getOptions( QMap< QString, QString >& opts, bool ) +{ + opts[ "kde-kig-showgrid" ] = QString::number( showgrid->isChecked() ); + opts[ "kde-kig-showaxes" ] = QString::number( showaxes->isChecked() ); +} + +void KigPrintDialogPage::setOptions( const QMap< QString, QString >& opts ) +{ + QString tmp = opts[ "kde-kig-showgrid" ]; + bool bt = ( tmp != "0" ); + showgrid->setChecked( bt ); + tmp = opts[ "kde-kig-showaxes" ]; + bt = ( tmp != "0" ); + showaxes->setChecked( bt ); +} + +bool KigPrintDialogPage::isValid( QString& ) +{ + return true; +} + +KigPart::KigPart( QWidget *parentWidget, const char *, + QObject *parent, const char *name, + const QStringList& ) + : KParts::ReadWritePart( parent, name ), + mMode( 0 ), mdocument( new KigDocument() ) +{ + // we need an instance + setInstance( KigPartFactory::instance() ); + + mMode = new NormalMode( *this ); + + // we need a widget, to actually show the document + m_widget = new KigView(this, false, parentWidget, "kig_view"); + // notify the part that this is our internal widget + setWidget( m_widget ); + + // create our actions... + setupActions(); + + // set our XML-UI resource file + setXMLFile("kigpartui.rc"); + + // our types... + setupTypes(); + + // construct our command history + mhistory = new KCommandHistory(actionCollection()); + mhistory->documentSaved(); + connect( mhistory, SIGNAL( documentRestored() ), this, SLOT( setUnmodified() ) ); + + // we are read-write by default + setReadWrite(true); + + setModified (false); + + GUIActionList::instance()->regDoc( this ); +} + +void KigPart::setupActions() +{ + // save actions.. + (void) KStdAction::saveAs(this, SLOT(fileSaveAs()), actionCollection()); + (void) KStdAction::save(this, SLOT(fileSave()), actionCollection()); + + // print actions + (void) KStdAction::print( this, SLOT( filePrint() ), actionCollection() ); + (void) KStdAction::printPreview( this, SLOT( filePrintPreview() ), actionCollection() ); + + // selection actions + aSelectAll = KStdAction::selectAll( + this, SLOT( slotSelectAll() ), actionCollection() ); + aDeselectAll = KStdAction::deselect( + this, SLOT( slotDeselectAll() ), actionCollection() ); + aInvertSelection = new KAction( + i18n( "Invert Selection" ), "", 0, this, + SLOT( slotInvertSelection() ), actionCollection(), + "edit_invert_selection" ); + + // we need icons... + KIconLoader* l = instance()->iconLoader(); + QPixmap tmp; + + aDeleteObjects = new KAction( + i18n("&Delete Objects"), "editdelete", Key_Delete, this, + SLOT(deleteObjects()), actionCollection(), "delete_objects"); + aDeleteObjects->setToolTip(i18n("Delete the selected objects")); + + aCancelConstruction = new KAction( + i18n("Cancel Construction"), "stop", Key_Escape, this, + SLOT(cancelConstruction()), actionCollection(), "cancel_construction"); + aCancelConstruction->setToolTip( + i18n("Cancel the construction of the object being constructed")); + aCancelConstruction->setEnabled(false); + + aShowHidden = new KAction( + i18n("U&nhide All"), 0, this, SLOT( showHidden() ), + actionCollection(), "edit_unhide_all"); + aShowHidden->setToolTip(i18n("Show all hidden objects")); + aShowHidden->setEnabled( true ); + + aNewMacro = new KAction( + i18n("&New Macro..."), "gear", 0, this, SLOT(newMacro()), + actionCollection(), "macro_action"); + aNewMacro->setToolTip(i18n("Define a new macro")); + + aConfigureTypes = new KAction( + i18n("Manage &Types..."), 0, this, SLOT(editTypes()), + actionCollection(), "types_edit"); + aConfigureTypes->setToolTip(i18n("Manage macro types.")); + + KigExportManager::instance()->addMenuAction( this, m_widget->realWidget(), + actionCollection() ); + + KAction* a = KStdAction::zoomIn( m_widget, SLOT( slotZoomIn() ), + actionCollection() ); + a->setToolTip( i18n( "Zoom in on the document" ) ); + a->setWhatsThis( i18n( "Zoom in on the document" ) ); + + a = KStdAction::zoomOut( m_widget, SLOT( slotZoomOut() ), + actionCollection() ); + a->setToolTip( i18n( "Zoom out of the document" ) ); + a->setWhatsThis( i18n( "Zoom out of the document" ) ); + + a = KStdAction::fitToPage( m_widget, SLOT( slotRecenterScreen() ), + actionCollection() ); + // grr.. why isn't there an icon for this.. + a->setIconSet( QIconSet( l->loadIcon( "view_fit_to_page", KIcon::Toolbar ) ) ); + a->setToolTip( i18n( "Recenter the screen on the document" ) ); + a->setWhatsThis( i18n( "Recenter the screen on the document" ) ); + +#ifdef KDE_IS_VERSION +#if KDE_IS_VERSION(3,1,90) +#define KIG_PART_CPP_STD_FULLSCREEN_ACTION +#endif +#endif +#ifdef KIG_PART_CPP_STD_FULLSCREEN_ACTION + a = KStdAction::fullScreen( m_widget, SLOT( toggleFullScreen() ), actionCollection(), (QWidget*)(widget()->parent()),"fullscreen" ); +#else + tmp = l->loadIcon( "window_fullscreen", KIcon::Toolbar ); + a = new KAction( + i18n( "Full Screen" ), tmp, CTRL+SHIFT+Key_F, + m_widget, SLOT( toggleFullScreen() ), + actionCollection(), "fullscreen" ); +#endif + a->setToolTip( i18n( "View this document full-screen." ) ); + a->setWhatsThis( i18n( "View this document full-screen." ) ); + + // TODO: an icon for this.. + a = new KAction( + i18n( "&Select Shown Area" ), "viewmagfit", 0, m_widget, SLOT( zoomRect() ), + actionCollection(), "view_select_shown_rect" ); + a->setToolTip( i18n( "Select the area that you want to be shown in the window." ) ); + a->setWhatsThis( i18n( "Select the area that you want to be shown in the window." ) ); + + a = new KAction( + i18n( "S&elect Zoom Area" ), "viewmag", 0, m_widget, SLOT( zoomArea() ), + actionCollection(), "view_zoom_area" ); +// a->setToolTip( i18n( "Select the area that you want to be shown in the window." ) ); +// a->setWhatsThis( i18n( "Select the area that you want to be shown in the window." ) ); + + aToggleGrid = new KToggleAction( + i18n( "Show &Grid" ), 0, this, SLOT( toggleGrid() ), + actionCollection(), "settings_show_grid" ); + aToggleGrid->setToolTip( i18n( "Show or hide the grid." ) ); + aToggleGrid->setChecked( true ); + + aToggleAxes = new KToggleAction( + i18n( "Show &Axes" ), 0, this, SLOT( toggleAxes() ), + actionCollection(), "settings_show_axes" ); + aToggleAxes->setToolTip( i18n( "Show or hide the axes." ) ); + aToggleAxes->setChecked( true ); + + aToggleNightVision = new KToggleAction( + i18n( "Wear Infrared Glasses" ), 0, this, SLOT( toggleNightVision() ), + actionCollection(), "settings_toggle_nightvision" ); + aToggleNightVision->setToolTip( i18n( "Enable/Disable hidden objects visibility." ) ); + aToggleNightVision->setChecked( false ); + + // select coordinate system KActionMenu.. + aCoordSystem = new SetCoordinateSystemAction( *this, actionCollection() ); +} + +void KigPart::setupTypes() +{ + setupBuiltinStuff(); + setupBuiltinMacros(); + setupMacroTypes(); + GUIActionList& l = *GUIActionList::instance(); + typedef GUIActionList::avectype::const_iterator iter; + for ( iter i = l.actions().begin(); i != l.actions().end(); ++i ) + { + KigGUIAction* ret = new KigGUIAction( *i, *this, actionCollection() ); + aActions.push_back( ret ); + ret->plug( this ); + }; +} + +KigPart::~KigPart() +{ + GUIActionList::instance()->unregDoc( this ); + + // save our types... + saveTypes(); + + // objects get deleted automatically, when mobjsref gets + // destructed.. + + delete_all( aActions.begin(), aActions.end() ); + aActions.clear(); + + // cleanup + delete mMode; + delete mhistory; + + delete mdocument; +} + +bool KigPart::openFile() +{ + QFileInfo fileinfo( m_file ); + if ( ! fileinfo.exists() ) + { + KMessageBox::sorry( widget(), + i18n( "The file \"%1\" you tried to open does not exist. " + "Please verify that you entered the correct path." ).arg( m_file ), + i18n( "File Not Found" ) ); + return false; + }; + + // m_file is always local, so we can use findByPath instead of + // findByURL... + KMimeType::Ptr mimeType = KMimeType::findByPath ( m_file ); + kdDebug() << k_funcinfo << "mimetype: " << mimeType->name() << endl; + KigFilter* filter = KigFilters::instance()->find( mimeType->name() ); + if ( !filter ) + { + // we don't support this mime type... + KMessageBox::sorry + ( + widget(), + i18n( "You tried to open a document of type \"%1\"; unfortunately, " + "Kig does not support this format. If you think the format in " + "question would be worth implementing support for, you can " + "always ask us nicely on mailto:toscano.pino@tiscali.it " + "or do the work yourself and send me a patch." + ).arg(mimeType->name()), + i18n( "Format Not Supported" ) + ); + return false; + }; + + KigDocument* newdoc = filter->load (m_file); + if ( !newdoc ) + { + closeURL(); + m_url = KURL(); + return false; + } + delete mdocument; + mdocument = newdoc; + coordSystemChanged( mdocument->coordinateSystem().id() ); + aToggleGrid->setChecked( mdocument->grid() ); + aToggleAxes->setChecked( mdocument->axes() ); + aToggleNightVision->setChecked( mdocument->getNightVision() ); + + setModified(false); + mhistory->clear(); + + std::vector<ObjectCalcer*> tmp = calcPath( getAllParents( getAllCalcers( document().objects() ) ) ); + for ( std::vector<ObjectCalcer*>::iterator i = tmp.begin(); i != tmp.end(); ++i ) + ( *i )->calc( document() ); + emit recenterScreen(); + + redrawScreen(); + + return true; +} + +bool KigPart::saveFile() +{ + if ( m_file.isEmpty() || m_bTemp ) return internalSaveAs(); + // mimetype: + KMimeType::Ptr mimeType = KMimeType::findByPath ( m_file ); + if ( mimeType->name() != "application/x-kig" ) + { + // we don't support this mime type... + if( KMessageBox::warningYesNo( widget(), + i18n( "Kig does not support saving to any other file format than " + "its own. Save to Kig's format instead?" ), + i18n( "Format Not Supported" ), i18n("Save Kig Format"), KStdGuiItem::cancel() ) == KMessageBox::No ) + return false; + internalSaveAs(); + }; + + if ( KigFilters::instance()->save( document(), m_file ) ) + { + setModified ( false ); + mhistory->documentSaved(); + return true; + } + return false; +} + +void KigPart::addObject(ObjectHolder* o) +{ + mhistory->addCommand( KigCommand::addCommand( *this, o ) ); +} + +void KigPart::addObjects( const std::vector<ObjectHolder*>& os ) +{ + mhistory->addCommand( KigCommand::addCommand( *this, os ) ); +} + +void KigPart::_addObject( ObjectHolder* o ) +{ + document().addObject( o ); + setModified(true); +} + +void KigPart::delObject( ObjectHolder* o ) +{ + // we delete all children and their children etc. too... + std::vector<ObjectHolder*> os; + os.push_back( o ); + delObjects( os ); +} + +void KigPart::_delObjects( const std::vector<ObjectHolder*>& o ) +{ + document().delObjects( o ); + setModified( true ); +} + +void KigPart::_delObject(ObjectHolder* o) +{ + document().delObject( o ); + setModified(true); +} + +void KigPart::setMode( KigMode* m ) +{ + mMode = m; + m->enableActions(); + redrawScreen(); +} + +void KigPart::_addObjects( const std::vector<ObjectHolder*>& os ) +{ + document().addObjects( os ); + setModified( true ); +} + +void KigPart::deleteObjects() +{ + mode()->deleteObjects(); +} + +void KigPart::cancelConstruction() +{ + mode()->cancelConstruction(); +} + +void KigPart::showHidden() +{ + mode()->showHidden(); +} + +void KigPart::newMacro() +{ + mode()->newMacro(); +} + +void KigPart::editTypes() +{ + mode()->editTypes(); +} + +void KigPart::setUnmodified() +{ + setModified( false ); +} + +KCommandHistory* KigPart::history() +{ + return mhistory; +} + +void KigPart::delObjects( const std::vector<ObjectHolder*>& os ) +{ + if ( os.size() < 1 ) return; + std::set<ObjectHolder*> delobjs; + + std::set<ObjectCalcer*> delcalcers = getAllChildren( getAllCalcers( os ) ); + std::map<ObjectCalcer*, ObjectHolder*> holdermap; + + std::set<ObjectHolder*> curobjs = document().objectsSet(); + + for ( std::set<ObjectHolder*>::iterator i = curobjs.begin(); + i != curobjs.end(); ++i ) + holdermap[( *i )->calcer()] = *i; + + for ( std::set<ObjectCalcer*>::iterator i = delcalcers.begin(); + i != delcalcers.end(); ++i ) + { + std::map<ObjectCalcer*, ObjectHolder*>::iterator j = holdermap.find( *i ); + if ( j != holdermap.end() ) + delobjs.insert( j->second ); + } + + assert( delobjs.size() >= os.size() ); + + std::vector<ObjectHolder*> delobjsvect( delobjs.begin(), delobjs.end() ); + mhistory->addCommand( KigCommand::removeCommand( *this, delobjsvect ) ); +} + +void KigPart::enableConstructActions( bool enabled ) +{ + for_each( aActions.begin(), aActions.end(), + bind2nd( mem_fun( &KAction::setEnabled ), + enabled ) ); +} + +void KigPart::unplugActionLists() +{ + unplugActionList( "user_conic_types" ); + unplugActionList( "user_segment_types" ); + unplugActionList( "user_point_types" ); + unplugActionList( "user_circle_types" ); + unplugActionList( "user_line_types" ); + unplugActionList( "user_other_types" ); + unplugActionList( "user_types" ); +} + +void KigPart::plugActionLists() +{ + plugActionList( "user_conic_types", aMNewConic ); + plugActionList( "user_segment_types", aMNewSegment ); + plugActionList( "user_point_types", aMNewPoint ); + plugActionList( "user_circle_types", aMNewCircle ); + plugActionList( "user_line_types", aMNewLine ); + plugActionList( "user_other_types", aMNewOther ); + plugActionList( "user_types", aMNewAll ); +} + +void KigPart::emitStatusBarText( const QString& text ) +{ + emit setStatusBarText( text ); +} + +void KigPart::fileSaveAs() +{ + internalSaveAs(); +} + +void KigPart::fileSave() +{ + save(); +} + +bool KigPart::internalSaveAs() +{ + // this slot is connected to the KStdAction::saveAs action... + QString formats = i18n( "*.kig|Kig Documents (*.kig)\n" + "*.kigz|Compressed Kig Documents (*.kigz)" ); + + // formats += "\n"; + // formats += KImageIO::pattern( KImageIO::Writing ); + + QString file_name = KFileDialog::getSaveFileName(":document", formats ); + if (file_name.isEmpty()) return false; + else if ( QFileInfo( file_name ).exists() ) + { + int ret = KMessageBox::warningContinueCancel( m_widget, + i18n( "The file \"%1\" already exists. Do you wish to overwrite it?" ) + .arg( file_name ), i18n( "Overwrite File?" ), i18n("Overwrite") ); + if ( ret != KMessageBox::Continue ) + { + return false; + } + } + saveAs(KURL::fromPathOrURL( file_name )); + return true; +} + +void KigPart::runMode( KigMode* m ) +{ + KigMode* prev = mMode; + + setMode( m ); + +#if QT_VERSION >= 0x030100 + (void) kapp->eventLoop()->enterLoop(); +#else + (void) kapp->enter_loop(); +#endif + + setMode( prev ); + redrawScreen(); +} + +void KigPart::doneMode( KigMode* d ) +{ + assert( d == mMode ); + // pretend to use this var.. + (void)d; +#if QT_VERSION >= 0x030100 + kapp->eventLoop()->exitLoop(); +#else + kapp->exit_loop(); +#endif +} + +void KigPart::actionRemoved( GUIAction* a, GUIUpdateToken& t ) +{ + KigGUIAction* rem = 0; + for ( std::vector<KigGUIAction*>::iterator i = aActions.begin(); i != aActions.end(); ++i ) + { + if ( (*i)->guiAction() == a ) + { + rem = *i; + aActions.erase( i ); + break; + } + }; + assert( rem ); + aMNewSegment.remove( rem ); + aMNewConic.remove( rem ); + aMNewPoint.remove( rem ); + aMNewCircle.remove( rem ); + aMNewLine.remove( rem ); + aMNewOther.remove( rem ); + aMNewAll.remove( rem ); + t.push_back( rem ); +} + +void KigPart::actionAdded( GUIAction* a, GUIUpdateToken& ) +{ + KigGUIAction* ret = new KigGUIAction( a, *this, actionCollection() ); + aActions.push_back( ret ); + ret->plug( this ); +} + +void KigPart::endGUIActionUpdate( GUIUpdateToken& t ) +{ + unplugActionLists(); + plugActionLists(); + delete_all( t.begin(), t.end() ); + t.clear(); +} + +KigPart::GUIUpdateToken KigPart::startGUIActionUpdate() +{ + return GUIUpdateToken(); +} + +void KigPart::setupMacroTypes() +{ + static bool alreadysetup = false; + if ( ! alreadysetup ) + { + alreadysetup = true; + + // the user's saved macro types: + QStringList dataFiles = + KGlobal::dirs()->findAllResources("appdata", "kig-types/*.kigt", + true, false ); + std::vector<Macro*> macros; + for ( QStringList::iterator file = dataFiles.begin(); + file != dataFiles.end(); ++file ) + { + std::vector<Macro*> nmacros; + bool ok = MacroList::instance()->load( *file, nmacros, *this ); + if ( ! ok ) continue; + copy( nmacros.begin(), nmacros.end(), back_inserter( macros ) ); + } + MacroList::instance()->add( macros ); + }; + // hack: we need to plug the action lists _after_ the gui is + // built.. i can't find a better solution than this... + QTimer::singleShot( 0, this, SLOT( plugActionLists() ) ); +} + +void KigPart::setupBuiltinMacros() +{ + static bool alreadysetup = false; + if ( ! alreadysetup ) + { + alreadysetup = true; + // builtin macro types ( we try to make the user think these are + // normal types ).. + QStringList builtinfiles = + KGlobal::dirs()->findAllResources( "appdata", "builtin-macros/*.kigt", true, false ); + for ( QStringList::iterator file = builtinfiles.begin(); + file != builtinfiles.end(); ++file ) + { + std::vector<Macro*> macros; + bool ok = MacroList::instance()->load( *file, macros, *this ); + if ( ! ok ) continue; + for ( uint i = 0; i < macros.size(); ++i ) + { + ObjectConstructorList* ctors = ObjectConstructorList::instance(); + GUIActionList* actions = GUIActionList::instance(); + Macro* macro = macros[i]; + macro->ctor->setBuiltin( true ); + ctors->add( macro->ctor ); + actions->add( macro->action ); + macro->ctor = 0; + macro->action = 0; + delete macro; + }; + }; + }; +} + +void KigPart::addWidget( KigWidget* v ) +{ + mwidgets.push_back( v ); +} + +void KigPart::delWidget( KigWidget* v ) +{ + mwidgets.erase( std::remove( mwidgets.begin(), mwidgets.end(), v ), mwidgets.end() ); +} + +void KigPart::filePrintPreview() +{ + KPrinter printer; + printer.setPreviewOnly( true ); + doPrint( printer ); +} + +void KigPart::filePrint() +{ + KPrinter printer; + KigPrintDialogPage* kp = new KigPrintDialogPage(); + printer.addDialogPage( kp ); + printer.setFullPage( true ); + printer.setOption( "kde-kig-showgrid", QString::number( document().grid() ) ); + printer.setOption( "kde-kig-showaxes", QString::number( document().axes() ) ); + printer.setPageSelection( KPrinter::ApplicationSide ); + if ( printer.setup( m_widget, i18n("Print Geometry") ) ) + { + doPrint( printer ); + }; +} + +void KigPart::doPrint( KPrinter& printer ) +{ + QPaintDeviceMetrics metrics( &printer ); + Rect rect = document().suggestedRect(); + QRect qrect( 0, 0, metrics.width(), metrics.height() ); + if ( rect.width() * qrect.height() > rect.height() * qrect.width() ) + { + // qrect is too high.. + int nh = static_cast<int>( qrect.width() * rect.height() / rect.width() ); + int rest = qrect.height() - nh; + qrect.setTop( qrect.top() - rest / 2 ); + qrect.setTop( rest / 2 ); + } + else + { + // qrect is too wide.. + int nw = static_cast<int>( qrect.height() * rect.width() / rect.height() ); + int rest = qrect.width() - nw; + qrect.setLeft( rest / 2 ); + qrect.setRight( qrect.right() - rest / 2 ); + }; + ScreenInfo si( rect, qrect ); + KigPainter painter( si, &printer, document() ); + painter.setWholeWinOverlay(); + bool sg = true; + bool sa = true; + if ( !printer.previewOnly() ) + { + sg = ( printer.option( "kde-kig-showgrid" ) != "0" ); + sa = ( printer.option( "kde-kig-showaxes" ) != "0" ); + } + else + { + sg = document().grid(); + sg = document().axes(); + } + painter.drawGrid( document().coordinateSystem(), sg, sa ); + painter.drawObjects( document().objects(), false ); +} + +void KigPart::slotSelectAll() +{ + mMode->selectAll(); +} + +void KigPart::slotDeselectAll() +{ + mMode->deselectAll(); +} + +void KigPart::slotInvertSelection() +{ + mMode->invertSelection(); +} + +void KigPart::hideObjects( const std::vector<ObjectHolder*>& inos ) +{ + std::vector<ObjectHolder*> os; + for (std::vector<ObjectHolder*>::const_iterator i = inos.begin(); i != inos.end(); ++i ) + { + if ( (*i)->shown() ) + os.push_back( *i ); + }; + KigCommand* kc = 0; + if ( os.size() == 0 ) return; + else if ( os.size() == 1 ) + kc = new KigCommand( *this, os[0]->imp()->type()->hideAStatement() ); + else kc = new KigCommand( *this, i18n( "Hide %n Object", "Hide %n Objects", os.size() ) ); + for ( std::vector<ObjectHolder*>::iterator i = os.begin(); + i != os.end(); ++i ) + kc->addTask( new ChangeObjectDrawerTask( *i, ( *i )->drawer()->getCopyShown( false ) ) ); + mhistory->addCommand( kc ); +} + +void KigPart::showObjects( const std::vector<ObjectHolder*>& inos ) +{ + std::vector<ObjectHolder*> os; + for (std::vector<ObjectHolder*>::const_iterator i = inos.begin(); i != inos.end(); ++i ) + { + if ( !(*i)->shown() ) + os.push_back( *i ); + }; + KigCommand* kc = 0; + if ( os.size() == 0 ) return; + else if ( os.size() == 1 ) + kc = new KigCommand( *this, os[0]->imp()->type()->showAStatement() ); + else kc = new KigCommand( *this, i18n( "Show %n Object", "Show %n Objects", os.size() ) ); + for ( std::vector<ObjectHolder*>::iterator i = os.begin(); + i != os.end(); ++i ) + kc->addTask( new ChangeObjectDrawerTask( *i, ( *i )->drawer()->getCopyShown( true ) ) ); + mhistory->addCommand( kc ); +} + +void KigPart::redrawScreen( KigWidget* w ) +{ + mode()->redrawScreen( w ); +} + +void KigPart::redrawScreen() +{ + for ( std::vector<KigWidget*>::iterator i = mwidgets.begin(); + i != mwidgets.end(); ++i ) + { + mode()->redrawScreen( *i ); + } +} + +const KigDocument& KigPart::document() const +{ + return *mdocument; +} + +KigDocument& KigPart::document() +{ + return *mdocument; +} + +extern "C" int convertToNative( const KURL& url, const QCString& outfile ) +{ + kdDebug() << "converting " << url.prettyURL() << " to " << outfile << endl; + + if ( ! url.isLocalFile() ) + { + // TODO + kdError() << "--convert-to-native only supports local files for now." << endl; + return -1; + } + + QString file = url.path(); + + QFileInfo fileinfo( file ); + if ( ! fileinfo.exists() ) + { + kdError() << "The file \"" << file << "\" does not exist" << endl; + return -1; + }; + + KMimeType::Ptr mimeType = KMimeType::findByPath ( file ); + kdDebug() << k_funcinfo << "mimetype: " << mimeType->name() << endl; + KigFilter* filter = KigFilters::instance()->find( mimeType->name() ); + if ( !filter ) + { + kdError() << "The file \"" << file << "\" is of a filetype not currently supported by Kig." << endl; + return -1; + }; + + KigDocument* doc = filter->load (file); + if ( !doc ) + { + kdError() << "Parse error in file \"" << file << "\"." << endl; + return -1; + } + + std::vector<ObjectCalcer*> tmp = calcPath( getAllParents( getAllCalcers( doc->objects() ) ) ); + for ( std::vector<ObjectCalcer*>::iterator i = tmp.begin(); i != tmp.end(); ++i ) + ( *i )->calc( *doc ); + for ( std::vector<ObjectCalcer*>::iterator i = tmp.begin(); i != tmp.end(); ++i ) + ( *i )->calc( *doc ); + + QString out = ( outfile == "-" ) ? QString::null : outfile; + bool success = KigFilters::instance()->save( *doc, out ); + if ( !success ) + { + kdError() << "something went wrong while saving" << endl; + return -1; + } + + delete doc; + + return 0; +} + +void KigPart::toggleGrid() +{ + bool toshow = !mdocument->grid(); + aToggleGrid->setChecked( toshow ); + mdocument->setGrid( toshow ); + + redrawScreen(); +} + +void KigPart::toggleAxes() +{ + bool toshow = !mdocument->axes(); + aToggleAxes->setChecked( toshow ); + mdocument->setAxes( toshow ); + + redrawScreen(); +} + +void KigPart::toggleNightVision() +{ + bool nv = !mdocument->getNightVision(); + aToggleNightVision->setChecked( nv ); + mdocument->setNightVision( nv ); + + redrawScreen(); +} + +void KigPart::coordSystemChanged( int id ) +{ + aCoordSystem->setCurrentItem( id ); +} + +void KigPart::saveTypes() +{ + QString typesDir = KGlobal::dirs()->saveLocation( "appdata", "kig-types" ); + if ( typesDir[ typesDir.length() - 1 ] != '/' ) + typesDir += '/'; + QString typesFileWithPath = typesDir + typesFile; + + // removing existant types file + if ( QFile::exists( typesFileWithPath ) ) + QFile::remove( typesFileWithPath ); + + MacroList* macrolist = MacroList::instance(); + macrolist->save( macrolist->macros(), typesFileWithPath ); +} + +void KigPart::loadTypes() +{ + QString typesDir = KGlobal::dirs()->saveLocation( "appdata", "kig-types" ); + if ( typesDir[ typesDir.length() - 1 ] != '/' ) + typesDir += '/'; + QString typesFileWithPath = typesDir + typesFile; + + if ( QFile::exists( typesFileWithPath ) ) + { + std::vector<Macro*> macros; + MacroList::instance()->load( typesFileWithPath, macros, *this ); + MacroList::instance()->add( macros ); + } +} + +void KigPart::deleteTypes() +{ + unplugActionLists(); + typedef MacroList::vectype vec; + MacroList* macrolist = MacroList::instance(); + const vec& macros = macrolist->macros(); + for ( vec::const_reverse_iterator i = macros.rbegin(); i != macros.rend(); ++i ) + { + macrolist->remove( *i ); + } + plugActionLists(); +} diff --git a/kig/kig/kig_part.desktop b/kig/kig/kig_part.desktop new file mode 100644 index 00000000..cffd6e3d --- /dev/null +++ b/kig/kig/kig_part.desktop @@ -0,0 +1,16 @@ +[Desktop Entry] +Name=KigPart +Name[ar]=جزء كيج +Name[bn]=কিগপারà§à¦Ÿ +Name[et]=Kigi komponent +Name[hi]=के-इग-पारà¥à¤Ÿ +Name[ne]=किग पारà¥à¤Ÿ +Name[sv]=Kigdel +Name[ta]=கிகà¯à®ªà®•à¯à®¤à®¿ +Name[ven]=TshipidatshaKig +Name[zh_CN]=Kig 组件 +MimeType=application/x-kig;application/x-kgeo; +ServiceTypes=KParts/ReadOnlyPart,KParts/ReadWritePart +X-KDE-Library=libkigpart +Type=Service +Icon=kig diff --git a/kig/kig/kig_part.h b/kig/kig/kig_part.h new file mode 100644 index 00000000..2084d0a9 --- /dev/null +++ b/kig/kig/kig_part.h @@ -0,0 +1,257 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + + +#ifndef KIGPART_H +#define KIGPART_H + +#include <qptrlist.h> + +#include <kparts/part.h> + +#include "../objects/common.h" + +class KAboutData; +class KActionMenu; +class KCommandHistory; +class KPrinter; +class KSelectAction; +class KToolBar; +class KToggleAction; +class KURL; +class QWidget; + +class Coordinate; +class CoordinateSystem; +class GUIAction; +class KigGUIAction; +class KigMode; +class KigObjectsPopup; +class KigView; +class MacroWizardImpl; +class ObjectHolder; +class Rect; +class ScreenInfo; + +/** + * This is a "Part". It that does all the real work in a KPart + * application. + * Briefly, it holds the data of the document, and acts as an + * interface to shells + * + * @short Main Part + */ +class KigPart : public KParts::ReadWritePart +{ + Q_OBJECT +public: + /** + * Default constructor + */ + KigPart( QWidget* parentWidget, const char* widgetName, + QObject* parent = 0, const char* name = 0, + const QStringList& = QStringList() + ); + + /** + * Destructor + */ + virtual ~KigPart(); + +/*********************** KPart interface *************************/ + +protected: + /** + * load our internal document from m_file + */ + virtual bool openFile(); + + /** + * save our internal document to m_file + */ + virtual bool saveFile(); + +public: + void emitStatusBarText( const QString& text ); + void redrawScreen(); + void redrawScreen( KigWidget* w ); + +public slots: + void fileSaveAs(); + void fileSave(); + + void filePrint(); + void filePrintPreview(); + + void slotSelectAll(); + void slotDeselectAll(); + void slotInvertSelection(); + + void unplugActionLists(); + void plugActionLists(); + + void deleteObjects(); + void cancelConstruction(); + void showHidden(); + void newMacro(); + void editTypes(); + + void toggleGrid(); + void toggleAxes(); + void toggleNightVision(); + + /** + * equivalent to setModified( false ); ( did i mention i don't like + * signals/slots for being this inflexible... + * this is connected to mhistory->documentRestored(); + */ + void setUnmodified(); + + /****************** cooperation with stuff ******************/ +public: + void addWidget( KigWidget* ); + void delWidget( KigWidget* ); + + KigMode* mode() const { return mMode; } + void setMode( KigMode* ); + void runMode( KigMode* ); + void doneMode( KigMode* ); + + void coordSystemChanged( int ); + +signals: // these signals are for telling KigView it should do something... + /** + * emitted when we want to suggest a new size for the view + * ( basically after loading a file, and on startup... ) + */ + void recenterScreen(); + +/************** working with our internal document **********/ +public: + // guess what these do... + // actually, they only add a command object to the history, the real + // work is done in _addObject() and _delObject() + void addObject(ObjectHolder* inObject); + void addObjects( const std::vector<ObjectHolder*>& os ); + void delObject(ObjectHolder* inObject); + void delObjects( const std::vector<ObjectHolder*>& os ); + void hideObjects( const std::vector<ObjectHolder*>& os ); + void showObjects( const std::vector<ObjectHolder*>& os ); + + void _addObject( ObjectHolder* inObject ); + void _addObjects( const std::vector<ObjectHolder*>& o); + void _delObject( ObjectHolder* inObject ); + void _delObjects( const std::vector<ObjectHolder*>& o ); + +/************* internal stuff *************/ +protected: + bool internalSaveAs(); + +public: + static KAboutData* createAboutData(); +protected: + void setupActions(); + void setupTypes(); + void setupBuiltinMacros(); + void setupMacroTypes(); + +protected: + KigMode* mMode; + KSelectAction* aCoordSystem; + + /** + * the command history + */ + KCommandHistory* mhistory; + +public: + // actions: this is an annoying case, didn't really fit into my + // model with KigModes.. This is how it works now: + // the actions are owned by the Part, because we need them on + // constructing the GUI ( actions appearing when you switch modes + // would not be nice, imho ). On setting the KigPart mode, we + // connect the actions to the current mode, and disconnect them from + // the previous mode. Enabling/disabling is done at the same time, + // of course.. + // some MenuActions.. + QPtrList<KAction> aMNewSegment; + QPtrList<KAction> aMNewPoint; + QPtrList<KAction> aMNewCircle; + QPtrList<KAction> aMNewLine; + QPtrList<KAction> aMNewOther; + QPtrList<KAction> aMNewAll; + QPtrList<KAction> aMNewConic; + + + KAction* aCancelConstruction; + KAction* aSelectAll; + KAction* aDeselectAll; + KAction* aInvertSelection; + KAction* aDeleteObjects; + KAction* aNewMacro; + KAction* aShowHidden; + KAction* aConfigureTypes; + KToggleAction* aToggleGrid; + KToggleAction* aToggleAxes; + KToggleAction* aToggleNightVision; + std::vector<KigGUIAction*> aActions; + + /** + * the "token" keeps some objects that should be deleted, we only + * delete them after we replug the actionLists.. calling these + * functions should be done like: + * \code + * GUIUpdateToken t = doc->startGUIActionUpdate(); + * doc->action[Added|Removed]( act, t ); + * ... + * doc->endGUIActionUpdate( t ); + * \endcode + */ + typedef std::vector<KigGUIAction*> GUIUpdateToken; + GUIUpdateToken startGUIActionUpdate(); + void actionAdded( GUIAction* a, GUIUpdateToken& t ); + void actionRemoved( GUIAction* a, GUIUpdateToken& t ); + void endGUIActionUpdate( GUIUpdateToken& t ); + + KCommandHistory* history(); + + void enableConstructActions( bool enabled ); + +protected: + void doPrint( KPrinter& printer ); + + std::vector<KigWidget*> mwidgets; + + KigView* m_widget; + + KigDocument* mdocument; +public: + const KigDocument& document() const; + KigDocument& document(); + +/***************** types handling *******************/ + void saveTypes(); + void loadTypes(); + void deleteTypes(); + +}; + +#endif // KIGPART_H + diff --git a/kig/kig/kig_view.cpp b/kig/kig/kig_view.cpp new file mode 100644 index 00000000..7d36bc14 --- /dev/null +++ b/kig/kig/kig_view.cpp @@ -0,0 +1,593 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#include "kig_view.h" +#include "kig_view.moc" + +#include "kig_part.h" +#include "kig_document.h" +#include "kig_commands.h" +#include "../misc/coordinate_system.h" +#include "../misc/kiginputdialog.h" +#include "../misc/kigpainter.h" +#include "../modes/mode.h" +#include "../modes/dragrectmode.h" + +#include <qdialog.h> +#include <qevent.h> +#include <qwhatsthis.h> +#include <qlayout.h> +#include <qscrollbar.h> + +#include <kdebug.h> +#include <kcursor.h> +#include <klocale.h> +#include <kapplication.h> +#include <kstdaction.h> +#include <kaction.h> +#include <kiconloader.h> + +#include <cmath> +#include <algorithm> + +kdbgstream& operator<< ( kdbgstream& s, const QPoint& t ) +{ + s << "x: " << t.x() << " y: " << t.y(); + return s; +} + +KigWidget::KigWidget( KigPart* part, + KigView* view, + QWidget* parent, + const char* name, + bool fullscreen ) + : QWidget( parent, name, + fullscreen ? WStyle_Customize | WStyle_NoBorder : 0 ), + mpart( part ), + mview( view ), + stillPix(size()), + curPix(size()), + msi( Rect(), rect() ), + misfullscreen( fullscreen ) +{ + part->addWidget(this); + + setFocusPolicy(ClickFocus); + setBackgroundMode( Qt::NoBackground ); + setMouseTracking(true); + + curPix.resize( size() ); + stillPix.resize( size() ); +} + +KigWidget::~KigWidget() +{ + mpart->delWidget( this ); +} + +void KigWidget::paintEvent(QPaintEvent*) +{ + updateEntireWidget(); +} + +void KigWidget::mousePressEvent (QMouseEvent* e) +{ + if( e->button() & Qt::LeftButton ) + return mpart->mode()->leftClicked( e, this ); + if ( e->button() & Qt::MidButton ) + return mpart->mode()->midClicked( e, this ); + if ( e->button() & Qt::RightButton ) + return mpart->mode()->rightClicked( e, this ); +} + +void KigWidget::mouseMoveEvent (QMouseEvent* e) +{ + if( e->state() & Qt::LeftButton ) + return mpart->mode()->leftMouseMoved( e, this ); + if ( e->state() & Qt::MidButton ) + return mpart->mode()->midMouseMoved( e, this ); + if ( e->state() & Qt::RightButton ) + return mpart->mode()->rightMouseMoved( e, this ); + return mpart->mode()->mouseMoved( e, this ); +} + +void KigWidget::mouseReleaseEvent (QMouseEvent* e) +{ + if( e->state() & Qt::LeftButton ) + return mpart->mode()->leftReleased( e, this ); + if ( e->state() & Qt::MidButton ) + return mpart->mode()->midReleased( e, this ); + if ( e->state() & Qt::RightButton ) + return mpart->mode()->rightReleased( e, this ); +} + +void KigWidget::updateWidget( const std::vector<QRect>& overlay ) +{ +#undef SHOW_OVERLAY_RECTS +#ifdef SHOW_OVERLAY_RECTS + QPainter debug (this, this); + debug.setPen(Qt::yellow); +#endif // SHOW_OVERLAY_RECTS + // we undo our old changes... + for ( std::vector<QRect>::const_iterator i = oldOverlay.begin(); i != oldOverlay.end(); ++i ) + bitBlt( this, i->topLeft(), &curPix, *i ); + // we add our new changes... + for ( std::vector<QRect>::const_iterator i = overlay.begin(); i != overlay.end(); ++i ) + { + bitBlt( this, i->topLeft(), &curPix, *i ); +#ifdef SHOW_OVERLAY_RECTS + debug.drawRect(*i); +#endif + }; + oldOverlay = overlay; +} + +void KigWidget::updateEntireWidget() +{ + std::vector<QRect> overlay; + overlay.push_back( QRect( QPoint( 0, 0 ), size() ) ); + updateWidget( overlay ); +} + +void KigWidget::resizeEvent( QResizeEvent* e ) +{ + QSize osize = e->oldSize(); + QSize nsize = e->size(); + Rect orect = msi.shownRect(); + + curPix.resize( nsize ); + stillPix.resize( nsize ); + msi.setViewRect( rect() ); + + Rect nrect( 0., 0., + orect.width() * nsize.width() / osize.width(), + orect.height() * nsize.height() / osize.height() ); + nrect = matchScreenShape( nrect ); + nrect.setCenter( orect.center() ); + msi.setShownRect( nrect ); + + // horrible hack... We need to somehow differentiate between the + // resizeEvents we get on startup, and the ones generated by the + // user. The first require recentering the screen, the latter + // don't.. + if ( nsize.width() / osize.width() > 4 ) recenterScreen(); + + mpart->redrawScreen( this ); + updateScrollBars(); +} + +void KigWidget::updateCurPix( const std::vector<QRect>& ol ) +{ + // we make curPix look like stillPix again... + for ( std::vector<QRect>::const_iterator i = oldOverlay.begin(); i != oldOverlay.end(); ++i ) + bitBlt( &curPix, i->topLeft(), &stillPix, *i ); + for ( std::vector<QRect>::const_iterator i = ol.begin(); i != ol.end(); ++i ) + bitBlt( &curPix, i->topLeft(), &stillPix, *i ); + + // we add ol to oldOverlay, so that part of the widget will be + // updated too in updateWidget... + std::copy( ol.begin(), ol.end(), std::back_inserter( oldOverlay ) ); +} + +void KigWidget::recenterScreen() +{ + msi.setShownRect( matchScreenShape( mpart->document().suggestedRect() ) ); +} + +Rect KigWidget::matchScreenShape( const Rect& r ) const +{ + return r.matchShape( Rect::fromQRect( rect() ) ); +} + +void KigWidget::slotZoomIn() +{ + Rect nr = msi.shownRect(); + Coordinate c = nr.center(); + nr /= 2; + nr.setCenter( c ); + KigCommand* cd = + new KigCommand( *mpart, + i18n( "Zoom In" ) ); + cd->addTask( new KigViewShownRectChangeTask( *this, nr ) ); + mpart->history()->addCommand( cd ); +} + +void KigWidget::slotZoomOut() +{ + Rect nr = msi.shownRect(); + Coordinate c = nr.center(); + nr *= 2; + nr.setCenter( c ); + + // zooming in is undoable.. I know this isn't really correct, + // because the current view doesn't really belong to the document ( + // althought KGeo and KSeg both save them along, iirc ). However, + // undoing a zoom or another operation affecting the window seems a + // bit too useful to not be available. Please try to convince me if + // you feel otherwise ;-) + KigCommand* cd = + new KigCommand( *mpart, + i18n( "Zoom Out" ) ); + cd->addTask( new KigViewShownRectChangeTask( *this, nr ) ); + mpart->history()->addCommand( cd ); +} + +void KigWidget::clearStillPix() +{ + stillPix.fill(Qt::white); + oldOverlay.clear(); + oldOverlay.push_back ( QRect( QPoint(0,0), size() ) ); +} + +void KigWidget::redrawScreen( const std::vector<ObjectHolder*>& selection, bool dos ) +{ + std::vector<ObjectHolder*> nonselection; + std::set<ObjectHolder*> objs = mpart->document().objectsSet(); + std::set_difference( objs.begin(), objs.end(), selection.begin(), selection.end(), + std::back_inserter( nonselection ) ); + + // update the screen... + clearStillPix(); + KigPainter p( msi, &stillPix, mpart->document() ); + p.drawGrid( mpart->document().coordinateSystem(), mpart->document().grid(), + mpart->document().axes() ); + p.drawObjects( selection, true ); + p.drawObjects( nonselection, false ); + updateCurPix( p.overlay() ); + if ( dos ) updateEntireWidget(); +} + +const ScreenInfo& KigWidget::screenInfo() const +{ + return msi; +} + +const Rect KigWidget::showingRect() const +{ + return msi.shownRect(); +} + +const Coordinate KigWidget::fromScreen( const QPoint& p ) +{ + return msi.fromScreen( p ); +} + +double KigWidget::pixelWidth() const +{ + return msi.pixelWidth(); +} + +const Rect KigWidget::fromScreen( const QRect& r ) +{ + return msi.fromScreen( r ); +} + + +void KigWidget::updateScrollBars() +{ + mview->updateScrollBars(); +} + +KigView::KigView( KigPart* part, + bool fullscreen, + QWidget* parent, + const char* name ) + : QWidget( parent, name ), + mlayout( 0 ), mrightscroll( 0 ), mbottomscroll( 0 ), + mupdatingscrollbars( false ), + mrealwidget( 0 ), mpart( part ) +{ + connect( part, SIGNAL( recenterScreen() ), this, SLOT( slotInternalRecenterScreen() ) ); + + mlayout = new QGridLayout( this, 2, 2 ); + mrightscroll = new QScrollBar( Vertical, this, "Right Scrollbar" ); + // TODO: make this configurable... + mrightscroll->setTracking( true ); + connect( mrightscroll, SIGNAL( valueChanged( int ) ), + this, SLOT( slotRightScrollValueChanged( int ) ) ); + connect( mrightscroll, SIGNAL( sliderReleased() ), + this, SLOT( updateScrollBars() ) ); + mbottomscroll = new QScrollBar( Horizontal, this, "Bottom Scrollbar" ); + connect( mbottomscroll, SIGNAL( valueChanged( int ) ), + this, SLOT( slotBottomScrollValueChanged( int ) ) ); + connect( mbottomscroll, SIGNAL( sliderReleased() ), + this, SLOT( updateScrollBars() ) ); + mrealwidget = new KigWidget( part, this, this, "Kig Widget", fullscreen ); + mlayout->addWidget( mbottomscroll, 1, 0 ); + mlayout->addWidget( mrealwidget, 0, 0 ); + mlayout->addWidget( mrightscroll, 0, 1 ); + + resize( sizeHint() ); + mrealwidget->recenterScreen(); + part->redrawScreen( mrealwidget ); + updateScrollBars(); +} + +void KigView::updateScrollBars() +{ + // we update the scrollbars to reflect the new "total size" of the + // document... The total size is calced in entireDocumentRect(). + // ( it is calced as a rect that contains all the points in the + // document, and then enlarged a bit, and scaled to match the screen + // width/height ratio... + // What we do here is tell the scroll bars what they should show as + // their total size.. + + // see the doc of this variable in the header for this... + mupdatingscrollbars = true; + + Rect er = mrealwidget->entireDocumentRect(); + Rect sr = mrealwidget->screenInfo().shownRect(); + + // we define the total rect to be the smallest rect that contains + // both er and sr... + er |= sr; + + // we need ints, not doubles, so since "pixelwidth == widgetcoord / + // internalcoord", we use "widgetcoord/pixelwidth", which would then + // equal "internalcoord", which has to be an int ( by definition.. ) + // i know, i'm a freak to think about these sorts of things... ;) + double pw = mrealwidget->screenInfo().pixelWidth(); + + // what the scrollbars reflect is the bottom resp. the left side of + // the shown rect. This is why the maximum value is not er.top() + // (which would be the maximum value of the top of the shownRect), + // but er.top() - sr.height(), which is the maximum value the bottom of + // the shownRect can reach... + + int rightmin = static_cast<int>( er.bottom() / pw ); + int rightmax = static_cast<int>( ( er.top() - sr.height() ) / pw ); + + mrightscroll->setMinValue( rightmin ); + mrightscroll->setMaxValue( rightmax ); + mrightscroll->setLineStep( (int)( sr.height() / pw / 10 ) ); + mrightscroll->setPageStep( (int)( sr.height() / pw / 1.2 ) ); + + // note that since Qt has a coordinate system with the lowest y + // values at the top, and we have it the other way around ( i know i + // shouldn't have done this.. :( ), we invert the value that the + // scrollbar shows. This is inverted again in + // slotRightScrollValueChanged()... + mrightscroll->setValue( (int) ( rightmin + ( rightmax - ( sr.bottom() / pw ) ) ) ); + + mbottomscroll->setMinValue( (int)( er.left() / pw ) ); + mbottomscroll->setMaxValue( (int)( ( er.right() - sr.width() ) / pw ) ); + mbottomscroll->setLineStep( (int)( sr.width() / pw / 10 ) ); + mbottomscroll->setPageStep( (int)( sr.width() / pw / 1.2 ) ); + mbottomscroll->setValue( (int)( sr.left() / pw ) ); + + mupdatingscrollbars = false; +} + +Rect KigWidget::entireDocumentRect() const +{ + return matchScreenShape( mpart->document().suggestedRect() ); +} + +void KigView::slotRightScrollValueChanged( int v ) +{ + if ( ! mupdatingscrollbars ) + { + // we invert the inversion that was done in updateScrollBars() ( + // check the documentation there..; ) + v = mrightscroll->minValue() + ( mrightscroll->maxValue() - v ); + double pw = mrealwidget->screenInfo().pixelWidth(); + double nb = double( v ) * pw; + mrealwidget->scrollSetBottom( nb ); + }; +} + +void KigView::slotBottomScrollValueChanged( int v ) +{ + if ( ! mupdatingscrollbars ) + { + double pw = mrealwidget->screenInfo().pixelWidth(); + double nl = double( v ) * pw; + mrealwidget->scrollSetLeft( nl ); + }; +} + +void KigWidget::scrollSetBottom( double rhs ) +{ + Rect sr = msi.shownRect(); + Coordinate bl = sr.bottomLeft(); + bl.y = rhs; + sr.setBottomLeft( bl ); + msi.setShownRect( sr ); + mpart->redrawScreen( this ); +} + +void KigWidget::scrollSetLeft( double rhs ) +{ + Rect sr = msi.shownRect(); + Coordinate bl = sr.bottomLeft(); + bl.x = rhs; + sr.setBottomLeft( bl ); + msi.setShownRect( sr ); + mpart->redrawScreen( this ); +} + +const ScreenInfo& KigView::screenInfo() const +{ + return mrealwidget->screenInfo(); +} + +KigView::~KigView() +{ +} + +KigWidget* KigView::realWidget() const +{ + return mrealwidget; +} + +const KigDocument& KigWidget::document() const +{ + return mpart->document(); +} + +QSize KigWidget::sizeHint() const +{ + return QSize( 630, 450 ); +} + +void KigWidget::wheelEvent( QWheelEvent* e ) +{ + int delta = e->delta(); + Qt::Orientation orient = e->orientation(); + if ( orient == Qt::Vertical ) + mview->scrollVertical( delta ); + else + mview->scrollHorizontal( delta ); +} + +void KigView::scrollHorizontal( int delta ) +{ + if ( delta >= 0 ) + for ( int i = 0; i < delta; i += 120 ) + mbottomscroll->subtractLine(); + else + for ( int i = 0; i >= delta; i -= 120 ) + mbottomscroll->addLine(); +} + +void KigView::scrollVertical( int delta ) +{ + if ( delta >= 0 ) + for ( int i = 0; i < delta; i += 120 ) + mrightscroll->subtractLine(); + else + for ( int i = 0; i >= delta; i -= 120 ) + mrightscroll->addLine(); +} + +bool KigWidget::isFullScreen() const +{ + return misfullscreen; +} + +void KigView::slotZoomIn() +{ + mrealwidget->slotZoomIn(); +} + +void KigView::slotZoomOut() +{ + mrealwidget->slotZoomOut(); +} + +void KigWidget::slotRecenterScreen() +{ + Rect nr = mpart->document().suggestedRect(); + KigCommand* cd = + new KigCommand( *mpart, + i18n( "Recenter View" ) ); + + cd->addTask( new KigViewShownRectChangeTask( *this, nr ) ); + mpart->history()->addCommand( cd ); +} + +void KigView::toggleFullScreen() +{ + mrealwidget->setFullScreen( ! mrealwidget->isFullScreen() ); + if ( mrealwidget->isFullScreen() ) + topLevelWidget()->showFullScreen(); + else + topLevelWidget()->showNormal(); +} + +void KigWidget::setFullScreen( bool f ) +{ + misfullscreen = f; +} + +void KigWidget::zoomRect() +{ + mpart->emitStatusBarText( i18n( "Select the rectangle that should be shown." ) ); + DragRectMode d( *mpart, *this ); + mpart->runMode( &d ); + if ( ! d.cancelled() ) + { + Rect nr = d.rect(); + KigCommand* cd = + new KigCommand( *mpart, + i18n( "Change Shown Part of Screen" ) ); + + cd->addTask( new KigViewShownRectChangeTask( *this, nr ) ); + mpart->history()->addCommand( cd ); + }; + + mpart->redrawScreen( this ); + updateScrollBars(); +} + +void KigView::zoomRect() +{ + mrealwidget->zoomRect(); +} + +void KigWidget::setShowingRect( const Rect& r ) +{ + msi.setShownRect( r.matchShape( Rect::fromQRect( rect() ) ) ); +} + +void KigView::slotRecenterScreen() +{ + mrealwidget->slotRecenterScreen(); +} + +void KigView::slotInternalRecenterScreen() +{ + mrealwidget->recenterScreen(); +} + +void KigWidget::zoomArea() +{ +// mpart->emitStatusBarText( i18n( "Select the area that should be shown." ) ); + Rect oldrect = showingRect(); + Coordinate tl = oldrect.topLeft(); + Coordinate br = oldrect.bottomRight(); + bool ok = true; + KigInputDialog::getTwoCoordinates( i18n( "Select Zoom Area" ), + i18n( "Select the zoom area by entering the coordinates of " + "the upper left corner and the lower right corner." ) + + QString::fromLatin1("<br>") + + mpart->document().coordinateSystem().coordinateFormatNoticeMarkup(), + this, &ok, mpart->document(), &tl, &br ); + if ( ok ) + { + Coordinate nc1( tl.x, br.y ); + Coordinate nc2( br.x, tl.y ); + Rect nr( nc1, nc2 ); + KigCommand* cd = new KigCommand( *mpart, i18n( "Change Shown Part of Screen" ) ); + + cd->addTask( new KigViewShownRectChangeTask( *this, nr ) ); + mpart->history()->addCommand( cd ); + } + + mpart->redrawScreen( this ); + updateScrollBars(); +} + +void KigView::zoomArea() +{ + mrealwidget->zoomArea(); +} + diff --git a/kig/kig/kig_view.h b/kig/kig/kig_view.h new file mode 100644 index 00000000..7970e088 --- /dev/null +++ b/kig/kig/kig_view.h @@ -0,0 +1,274 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + + +#ifndef KIG_VIEW_H +#define KIG_VIEW_H + +#include <qwidget.h> +#include <qpixmap.h> + +#include <kparts/part.h> + +#include <vector> + +#include "../objects/object_holder.h" +#include "../misc/rect.h" +#include "../misc/screeninfo.h" + +class QGridLayout; +class QScrollBar; + +class KigDocument; +class KigView; + +/** + * This class is the real widget showing the document. The other is a + * wrapper, that has the scrollbars... I'm not using QScrollView + * cause i've been having problems with that, and it's easier to do + * the work myself... + * Internally, this is basically a dumb class, which is manipulated by + * KigMode's. All events are forwarded to them. + */ +class KigWidget : public QWidget +{ + Q_OBJECT + + KigPart* mpart; + KigView* mview; + + // we reimplement these from QWidget to suit our needs + void mousePressEvent( QMouseEvent* e ); + void mouseMoveEvent( QMouseEvent* e ); + void mouseReleaseEvent( QMouseEvent* e ); + void paintEvent( QPaintEvent* e ); + void wheelEvent( QWheelEvent* e ); + void resizeEvent( QResizeEvent* ); + QSize sizeHint() const; + + /** + * this is called to match a rect's dimensions to the dimensions of + * the window before we set mViewRect to it. This is done cause we + * always want circles to look like circles etc... + */ + Rect matchScreenShape( const Rect& r ) const; + +public: + /** + * what do the still objects look like + * wondering if this is appropriate, maybe it should be part of + * MovingMode ? + */ + QPixmap stillPix; + /** + * temporary, gets bitBlt'd (copied) onto the widget + * (to avoid flickering) + */ + QPixmap curPix; + +protected: + std::vector<QRect> oldOverlay; + + /** + * this is a class that maps from our widget coordinates to the + * document's coordinates ( and back ). + */ + ScreenInfo msi; + + /** + * is this a full-screen widget ? + */ + bool misfullscreen; + +public: + /** + * standard qwidget constructor. if fullscreen is true, we're a + * fullscreen widget. + */ + KigWidget( KigPart* doc, + KigView* view, + QWidget* parent = 0, + const char* name = 0, + bool fullscreen = false + ); + ~KigWidget(); + + bool isFullScreen() const; + void setFullScreen( bool f ); + + const KigView* view() const { + return mview; + } + + KigView* view() { + return mview; + } + + /** + * The following are functions used by KigMode's to tell us to draw + * stuff... + * i tried to optimise the drawing as much as possible, using + * much ideas from kgeo + * DOUBLE BUFFERING: + * we don't draw on the widget directly, we draw on a QPixmap ( + * curPix ), and bitBlt that onto the widget to avoid flickering. + * TRIPLE BUFFERING: + * we also currently keep an extra pixmap of what the widget looks + * like without objects that are moving... i'm currently wondering + * whether this isn't a performance loss rather than a gain, but + * well, i haven't done any measurements (yet ?), so for now it + * stays in... + * OVERLAYS + * Another thing: it turns out that working on the pixmaps isn't + * that slow, but working on the widget is. So we try to reduce the + * amount of work we spend on the widget. (i got this idea from + * kgeo, all credits for this go to marc.bartsch@web.de) + * on drawing, KigPainter tells us (appendOverlay) for everything it + * draws what rects it draws in, so we know on updating the widget ( + * updateWidget() that the rest is still ok and doesn't need to be + * bitBlt'd again on the widget... + */ + + /** + * clear stillPix... + */ + void clearStillPix(); + /** + * update curPix (bitBlt stillPix onto curPix..) + */ + void updateCurPix( const std::vector<QRect>& = std::vector<QRect>()); + + /** + * this means bitBlting curPix on the actual widget... + */ + void updateWidget( const std::vector<QRect>& = std::vector<QRect>() ); + void updateEntireWidget(); + + /** + * Mapping between Internal Coordinate Systems + * there are two coordinate systems: + * 1 the widget's coordinates: these are simple int's from (0,0) in + * the topleft of the widget to size() in the bottomRight... + * 2 the document's coordinates: these are the coordinates used by + * the KigDocument. Objects only know of their coordinates as + * related to this system. + * These are mapped by the KigView using the ScreenInfo class. + */ + const Rect showingRect() const; + void setShowingRect( const Rect& r ); + + const Coordinate fromScreen( const QPoint& p ); + const Rect fromScreen( const QRect& r ); + double pixelWidth() const; + + /** + * the part of the document we're currently showing + * i.e. a rectangle of the document (which has its own coordinate + * system) which is mapped onto the widget. + */ + const ScreenInfo& screenInfo() const; + + Rect entireDocumentRect() const; + + void updateScrollBars(); + + void scrollSetBottom( double rhs ); + void scrollSetLeft( double rhs ); + + const KigDocument& document() const; + +public: + /** + * this recenters the screen, that is, resets the shown rect to + * mpart->document().suggestedRect().. + */ + void recenterScreen(); + /** + * this gets called if the user clicks the recenter screen button. + * It adds a KigCommand to the CommandHistory that recenters the + * screen.. + */ + void slotRecenterScreen(); + + // called when the user clicks the appropriate buttons.. + void slotZoomIn(); + void slotZoomOut(); + + void zoomRect(); + void zoomArea(); + + void redrawScreen( const std::vector<ObjectHolder*>& selection, bool paintOnWidget = true ); +}; + +/** + * This class is a wrapper for KigWidget. It has some actions + * that belong here, and not in the part. It also maintains the + * scrollbars, but it doesn't manipulate them itself. It forwards + * most of its functionality to KigWidget... + */ +class KigView + : public QWidget +{ + Q_OBJECT + + QGridLayout* mlayout; + QScrollBar* mrightscroll; + QScrollBar* mbottomscroll; + + /** + * apparently, QScrollBar also emits its signals when you update it + * manually, so we ignore them while we're in \ref updateScrollBars()... + */ + bool mupdatingscrollbars; + + KigWidget* mrealwidget; + KigPart* mpart; + +public: + KigView( KigPart* part, + bool fullscreen = false, + QWidget* parent = 0, + const char* name = 0 + ); + ~KigView(); + + void setupActions(); + + const ScreenInfo& screenInfo() const; + + KigWidget* realWidget() const; + void scrollHorizontal( int delta ); + void scrollVertical( int delta ); + +public slots: + void updateScrollBars(); + void slotZoomIn(); + void slotZoomOut(); + void zoomRect(); + void zoomArea(); + void slotInternalRecenterScreen(); + void slotRecenterScreen(); + void toggleFullScreen(); + +private slots: + void slotRightScrollValueChanged( int ); + void slotBottomScrollValueChanged( int ); +}; +#endif diff --git a/kig/kig/kigpartui.rc b/kig/kig/kigpartui.rc new file mode 100644 index 00000000..7e594416 --- /dev/null +++ b/kig/kig/kigpartui.rc @@ -0,0 +1,288 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="kig_part" version="10"> + <MenuBar> + <Menu name="file"> + <text>&File</text> + <Action name="file_save" /> + <Action name="file_save_as" /> + <Separator /> + <Action name="file_print" /> + <Action name="file_print_preview" /> + <Separator /> + <Action name="file_export" /> + <Separator /> + </Menu> + <Menu name="edit"> + <text>&Edit</text> + <Action name="edit_undo" /> + <Action name="edit_redo" /> + <Separator /> + <Action name="edit_select_all" /> + <Action name="edit_deselect" /> + <Action name="edit_invert_selection" /> + <Separator/> + <Action name="edit_unhide_all" /> + </Menu> + <Menu name="view"> + <text>&View</text> + <Action name="view_zoom_in" /> + <Action name="view_zoom_out" /> + <Action name="view_fit_to_page" /> + <Action name="view_select_shown_rect" /> + <Action name="view_zoom_area" /> + </Menu> + <Menu name="objects"> + <text>&Objects</text> + <Menu name="new_point" icon="point"> + <text>&Points</text> + <Action name="objects_new_normalpoint" /> + <Action name="objects_new_midpoint" /> + <Action name="objects_new_intersection" /> + <Action name="objects_new_point_xy" /> + <Action name="objects_new_translatedpoint" /> + <Action name="objects_new_rotatedpoint" /> + <Action name="objects_new_mirrorpoint" /> + <ActionList name="user_point_types" /> + </Menu> + <Menu name="new_line" icon="line"> + <text>&Lines</text> + <Action name="objects_new_linettp" /> + <Action name="objects_new_lineperpend" /> + <Action name="objects_new_lineparallel" /> + <Action name="objects_new_ray" /> + <Action name="objects_new_linebyvector" /> + <Action name="objects_new_halflinebyvector" /> + <ActionList name="user_line_types" /> + </Menu> + <Menu name="new_circle" icon="circlebps"> + <text>&Circles && Arcs</text> + <Action name="objects_new_circlebcp" /> + <Action name="objects_new_circlebtp" /> + <Action name="objects_new_circlebps" /> + <Action name="objects_new_circlebpd" /> + <Action name="objects_new_circlebcl" /> + <Action name="objects_new_arcbtp" /> + <Action name="objects_new_arcbcpa" /> + <ActionList name="user_circle_types" /> + </Menu> + <Menu name="new_poligon" icon="hexagonbcv"> + <text>Poly&gons</text> + <Action name="objects_new_trianglebtp" /> + <Action name="objects_new_polygonbnp" /> + <Action name="objects_new_polygonbcv" /> + <Action name="objects_new_equitriangle" /> + <Action name="objects_new_square" /> + <Action name="objects_new_polygonvertices" /> + <Action name="objects_new_polygonsides" /> + <Action name="objects_new_convexhull" /> + </Menu> + <Menu name="new_vector" icon="vector"> + <text>&Vectors && Segments</text> + <Action name="objects_new_segment" /> + <Action name="objects_new_segment_axis" /> + <Action name="objects_new_vector" /> + <Action name="objects_new_vectorsum" /> + <Action name="objects_new_vectordifference" /> + <ActionList name="user_segment_types" /> + </Menu> + <Menu name="new_conic" icon="conicb5p"> + <text>Co&nics && Cubics</text> + <Action name="objects_new_ellipsebffp" /> + <Action name="objects_new_hyperbolabffp" /> + <Action name="objects_new_conicb5p" /> + <Action name="objects_new_parabolabtp" /> + <ActionList name="user_conic_types" /> + <Menu name="new_moreconics" icon="conicsradicalline"> + <text>More Conics</text> + <Action name="objects_new_parabolabdp" /> + <Action name="objects_new_equilateralhyperbolab4p" /> + <Action name="objects_new_conicbdfp" /> + <Action name="objects_new_conicbaap" /> + <Action name="objects_new_linepolar" /> + <Action name="objects_new_lineconicasymptotes" /> + <Action name="objects_new_pointpolar" /> + <Action name="objects_new_linedirectrix" /> + <Action name="objects_new_lineconicradical" /> + </Menu> + <Separator /> + <Menu name="new_cubic"> + <text>Cu&bics</text> + <Action name="objects_new_cubicb9p" /> + <Action name="objects_new_cubicnodeb6p" /> + <Action name="objects_new_cubiccuspb4p" /> + </Menu> + </Menu> + <Menu name="new_angle" icon="angle"> + <text>&Angles</text> + <Action name="objects_new_angle" /> + <Action name="objects_new_angle_bisector" /> + </Menu> + <Menu name="new_transformation" icon="centralsymmetry"> + <text>&Transformations</text> + <Action name="objects_new_translation" /> + <Action name="objects_new_pointreflection" /> + <Action name="objects_new_linereflection" /> + <Action name="objects_new_rotation" /> + <Action name="objects_new_scalingovercenter" /> + <Action name="objects_new_scalingovercenter2" /> + <Action name="objects_new_scalingoverline" /> + <Action name="objects_new_scalingoverline2" /> + <Action name="objects_new_similitude" /> + <Action name="objects_new_inversion" /> + <Action name="objects_new_harmonichomology" /> + <Action name="objects_new_genericaffinity" /> + <Action name="objects_new_genericprojectivity" /> + <Action name="objects_new_castshadow" /> + <Action name="objects_new_projectiverotation" /> + </Menu> + <Menu name="new_differentialgeometry" icon="tangent"> + <text>&Differential geometry</text> + <Action name="objects_new_tangent" /> + <Action name="objects_new_centerofcurvature" /> + <Action name="objects_new_osculatingcircle" /> + <Action name="objects_new_evolute" /> + </Menu> + <Menu name="new_test" icon="test"> + <text>T&ests</text> + <Action name="objects_new_areparallel" /> + <Action name="objects_new_areorthogonal" /> + <Action name="objects_new_arecollinear" /> + <Action name="objects_new_containstest" /> + <Action name="objects_new_distancetest" /> + <Action name="objects_new_vectorequalitytest" /> + <Action name="objects_new_inpolygontest" /> + <Action name="objects_new_convexpolygontest" /> + </Menu> + <Menu name="new_other"> + <text>&Other</text> + <Action name="objects_new_locus" /> + <Action name="objects_new_textlabel" /> + <Action name="objects_new_measuretransport" /> + <Action name="objects_new_script_python" /> + <ActionList name="user_other_types" /> + </Menu> + <Separator /> + <Action name="delete_objects" /> + <Action name="cancel_construction" /> + </Menu> + <Menu name="types"> + <text>&Types</text> + <Action name="macro_action" /> + <Action name="types_edit" /> + </Menu> + <Menu name="settings"> + <Action name="fullscreen" /> + <Action name="settings_set_coordinate_system" /> + <Action name="settings_show_grid" /> + <Action name="settings_show_axes" /> + <Action name="settings_toggle_nightvision" /> + </Menu> + </MenuBar> + <ToolBar name="mainToolBar"> + <text>Main Toolbar</text> + <Action name="file_save" /> + <Action name="file_save_as" /> + <Separator /> + <Action name="file_print" /> + <Separator /> + <Action name="edit_undo"/> + <Action name="edit_redo"/> + <Separator /> + <Action name="delete_objects"/> + <Action name="cancel_construction"/> + <Action name="macro_action"/> + </ToolBar> + <ToolBar name="points_toolbar" position="left"> + <text>Points</text> + <Action name="objects_new_normalpoint" /> + <Action name="objects_new_point_xy" /> + <Action name="objects_new_intersection" /> + <Action name="objects_new_midpoint" /> + <Action name="objects_new_mirrorpoint" /> + <Action name="objects_new_rotatedpoint" /> + <Action name="objects_new_translatedpoint" /> + <ActionList name="user_point_types" /> + </ToolBar> + <ToolBar name="line_toolbar" position="left"> + <text>Lines</text> + <Action name="objects_new_linettp" /> + <Action name="objects_new_ray" /> + <Action name="objects_new_lineperpend" /> + <Action name="objects_new_lineparallel" /> + <Action name="objects_new_linebyvector" /> + <Action name="objects_new_halflinebyvector" /> + <ActionList name="user_line_types" /> + </ToolBar> + <ToolBar name="vectseg_toolbar" position="left"> + <text>Vectors && Segments</text> + <Action name="objects_new_segment" /> + <Action name="objects_new_segment_axis" /> + <Action name="objects_new_vector" /> + <Action name="objects_new_vectorsum" /> + <ActionList name="user_segment_types" /> + </ToolBar> + <ToolBar name="circle_toolbar" position="left"> + <text>Circles && Arcs</text> + <Action name="objects_new_circlebcp" /> + <Action name="objects_new_circlebtp" /> + <Action name="objects_new_circlebps" /> + <Action name="objects_new_circlebpd" /> + <Action name="objects_new_arcbtp" /> + <ActionList name="user_circle_types" /> + </ToolBar> + <ToolBar name="conic_toolbar" position="left"> + <text>Conics</text> + <Action name="objects_new_ellipsebffp" /> + <Action name="objects_new_hyperbolabffp" /> + <Action name="objects_new_parabolabtp" /> + <Action name="objects_new_conicb5p" /> + <ActionList name="user_conic_types" /> + <Action name="objects_new_lineconicradical" /> + </ToolBar> + <ToolBar name="angles_toolbar" position="left"> + <text>Angles</text> + <Action name="objects_new_angle" /> + <Action name="objects_new_angle_bisector" /> + </ToolBar> + <ToolBar name="transformation_toolbar" position="right"> + <text>Transformations</text> + <Action name="objects_new_translation" /> + <Action name="objects_new_pointreflection" /> + <Action name="objects_new_linereflection" /> + <Action name="objects_new_rotation" /> + <Action name="objects_new_scalingovercenter" /> + <Action name="objects_new_scalingoverline" /> + <Action name="objects_new_similitude" /> + <Action name="objects_new_inversion" /> + <Action name="objects_new_harmonichomology" /> + <Action name="objects_new_genericaffinity" /> + <Action name="objects_new_genericprojectivity" /> + </ToolBar> + <ToolBar name="tests_toolbar" position="right"> + <text>Tests</text> + <Action name="objects_new_areparallel" /> + <Action name="objects_new_areorthogonal" /> + <Action name="objects_new_arecollinear" /> + <Action name="objects_new_containstest" /> + <Action name="objects_new_distancetest" /> + <Action name="objects_new_vectorequalitytest" /> + <Action name="objects_new_inpolygontest" /> + <Action name="objects_new_convexpolygontest" /> + </ToolBar> + <ToolBar name="rest_toolbar" position="left"> + <text>Other Objects</text> + <Action name="objects_new_locus" /> + <Action name="objects_new_textlabel" /> + <Action name="objects_new_script_python" /> + <ActionList name="user_other_types" /> + </ToolBar> + <ToolBar name="view_toolbar"> + <text>View</text> + <Action name="view_zoom_in" /> + <Action name="view_zoom_out" /> + <Action name="fullscreen" /> + <Action name="view_fit_to_page" /> + <Action name="view_select_shown_rect" /> + <Action name="view_zoom_area" /> + </ToolBar> +</kpartgui> diff --git a/kig/kig/kigui.rc b/kig/kig/kigui.rc new file mode 100644 index 00000000..db90b0d2 --- /dev/null +++ b/kig/kig/kigui.rc @@ -0,0 +1,40 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="kig_shell" version="3"> +<MenuBar> + <Menu name="file" noMerge="1"><text>&File</text> + <Action name="file_new"/> + <Action name="file_open"/> + <Action name="file_open_recent"/> + <Separator /> + <Merge /> + <Action name="file_quit"/> + </Menu> + <Merge /> + <Menu name="settings" noMerge="1"><text>&Settings</text> + <Action name="options_show_toolbar"/> + <Merge name="StandardToolBarMenuHandler" /> + <Action name="options_show_statusbar"/> + <Merge /> + <Separator/> + <Action name="options_configure_keybinding"/> + <Action name="options_configure_toolbars"/> + </Menu> + <Menu name="help" noMerge="1"><text>&Help</text> + <Action name="help_contents"/> + <Action name="help_whats_this"/> + <Separator/> + <Action name="help_tipofday"/> + <Separator/> + <Action name="help_report_bug"/> + <Separator/> + <Action name="help_about_app"/> + <Action name="help_about_kde"/> + </Menu> +</MenuBar> +<ToolBar name="mainToolBar" noMerge="1"><text>Main Toolbar</text> + <Action name="file_new"/> + <Action name="file_open"/> + <Separator/> + <Merge /> +</ToolBar> +</kpartgui> diff --git a/kig/kig/main.cpp b/kig/kig/main.cpp new file mode 100644 index 00000000..d0a70404 --- /dev/null +++ b/kig/kig/main.cpp @@ -0,0 +1,144 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#include "kig.h" + +#include <kuniqueapplication.h> +#include <kaboutdata.h> +#include <kcmdlineargs.h> +#include <klocale.h> +#include <klibloader.h> +#include <kdebug.h> + +#include "aboutdata.h" + +static KCmdLineOptions options[] = + { + { "c", 0, 0 }, + { "convert-to-native", I18N_NOOP( "Do not show a GUI. Convert the specified file to the native Kig format. Output goes to stdout unless --outfile is specified." ), 0 }, + { "o", 0, 0 }, + { "outfile <file>", I18N_NOOP( "File to output the created native file to. '-' means output to stdout. Default is stdout as well." ), 0 }, + { "+[URL]", I18N_NOOP( "Document to open" ), 0 }, + KCmdLineLastOption + }; + +class KigApplication + : public KUniqueApplication +{ +public: + KigApplication( bool gui = true ); + int newInstance(); + void handleArgs( KCmdLineArgs* args ); +}; + +KigApplication::KigApplication( bool gui ) + : KUniqueApplication( gui, gui ) +{ +} + +int KigApplication::newInstance() +{ + static bool first = true; + if (isRestored() && first) + { + first = false; + return 0; + } + first = false; + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + handleArgs(args); + args->clear(); + return 0; +} + +void KigApplication::handleArgs( KCmdLineArgs* args ) +{ + if ( args->count() == 0 ) + { + Kig *widget = new Kig; + widget->show(); + } + else + { + for (int i = 0; i < args->count(); i++ ) + { + Kig *widget = new Kig; + widget->show(); + widget->load( args->url( i ) ); + } + } +} + +static int convertToNative( const KURL& file, const QCString& outfile ) +{ + KigApplication app( false ); + KLibrary* library = KLibLoader::self()->globalLibrary( "libkigpart" ); + int ( *converterfunction )( const KURL&, const QCString& ); + converterfunction = ( int ( * )( const KURL&, const QCString& ) ) library->symbol( "convertToNative" ); + if ( !converterfunction ) + { + kdError() << "Error: broken Kig installation: different library and application version !" << endl; + return -1; + } + return (*converterfunction)( file, outfile ); +} + +int main(int argc, char **argv) +{ + KAboutData *about = kigAboutData( "kig", I18N_NOOP("Kig") ); + + KCmdLineArgs::init(argc, argv, about); + KCmdLineArgs::addCmdLineOptions( options ); + KigApplication::addCmdLineOptions(); + + KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); + if ( args->isSet( "convert-to-native" ) ) + { + QCString outfile = args->getOption( "outfile" ); + if ( outfile.isNull() ) + outfile = "-"; + + if ( args->count() == 0 ) + { + kdError() << "Error: --convert-to-native specified without a file to convert." << endl; + return -1; + } + if ( args->count() > 1 ) + { + kdError() << "Error: --convert-to-native specified with more than one file to convert." << endl; + return -1; + } + return convertToNative( args->url( 0 ), outfile ); + } + else + { + if ( args->isSet( "outfile" ) ) + { + kdError() << "Error: --outfile specified without convert-to-native." << endl; + return -1; + } + KigApplication app; + + // see if we are starting with session management + if (app.isRestored()) RESTORE(Kig) + return app.exec(); + } +} diff --git a/kig/macros/Makefile.am b/kig/macros/Makefile.am new file mode 100644 index 00000000..f03caaa0 --- /dev/null +++ b/kig/macros/Makefile.am @@ -0,0 +1,11 @@ +builtinmacrodir = $(kde_datadir)/kig/builtin-macros +builtinmacro_DATA = \ + circle_by_center_and_line.kigt \ + circle_by_point_and_diameter.kigt \ + circle_by_point_and_segment.kigt \ + equitriangle.kigt \ + evolute.kigt \ + osculating_circle.kigt \ + segment_axis.kigt \ + square.kigt \ + vector_difference.kigt diff --git a/kig/macros/circle_by_center_and_line.kigt b/kig/macros/circle_by_center_and_line.kigt new file mode 100644 index 00000000..2a02210a --- /dev/null +++ b/kig/macros/circle_by_center_and_line.kigt @@ -0,0 +1,31 @@ +<!DOCTYPE KigMacroFile> +<KigMacroFile Number="1" Version="0.7.1" > + <Macro> + <Name>Circle by Center && Line</Name> + <Description>A circle constructed by its center and tangent to a given line</Description> + <ActionName>objects_new_circlebcl</ActionName> + <IconFileName>circlebcl</IconFileName> + <Construction> + <input requirement="line" id="1"> + <UseText>Construct a circle tangent to this line</UseText> + <SelectStatement>Select the line that the new circle should be tangent to...</SelectStatement> + </input> + <input requirement="point" id="2"> + <UseText>Construct a circle with this center</UseText> + <SelectStatement>Select the center of the new circle...</SelectStatement> + </input> + <intermediate action="calc" type="LinePerpend" id="3" > + <arg>1</arg> + <arg>2</arg> + </intermediate> + <intermediate action="calc" type="LineLineIntersection" id="4" > + <arg>1</arg> + <arg>3</arg> + </intermediate> + <result action="calc" type="CircleBCP" id="5" > + <arg>2</arg> + <arg>4</arg> + </result> + </Construction> + </Macro> +</KigMacroFile> diff --git a/kig/macros/circle_by_point_and_diameter.kigt b/kig/macros/circle_by_point_and_diameter.kigt new file mode 100644 index 00000000..afe73a61 --- /dev/null +++ b/kig/macros/circle_by_point_and_diameter.kigt @@ -0,0 +1,36 @@ +<!DOCTYPE KigMacroFile> +<KigMacroFile Number="1" Version="0.9.0" > + <Macro> + <Name>Circle by Point && Segment (as the Diameter)</Name> + <Description>A circle defined by its center and the length of a segment as the diameter</Description> + <ActionName>objects_new_circlebpd</ActionName> + <IconFileName>circlebpd</IconFileName> + <Construction> + <input requirement="point" id="1"> + <UseText>Construct a circle with this center</UseText> + <SelectStatement>Select the center of the new circle...</SelectStatement> + </input> + <input requirement="segment" id="2"> + <UseText>Construct a circle with the diameter given by the length of this segment</UseText> + <SelectStatement>Select the segment whose length gives the diameter of the new circle...</SelectStatement> + </input> + <intermediate action="fetch-property" property="end-point-A" id="3" > + <arg>2</arg> + </intermediate> + <intermediate action="fetch-property" property="mid-point" id="4" > + <arg>2</arg> + </intermediate> + <intermediate action="calc" type="SegmentAB" id="5" > + <arg>3</arg> + <arg>4</arg> + </intermediate> + <intermediate action="fetch-property" property="length" id="6" > + <arg>5</arg> + </intermediate> + <result action="calc" type="CircleBPR" id="7" > + <arg>1</arg> + <arg>6</arg> + </result> + </Construction> + </Macro> +</KigMacroFile> diff --git a/kig/macros/circle_by_point_and_segment.kigt b/kig/macros/circle_by_point_and_segment.kigt new file mode 100644 index 00000000..2f72eeb6 --- /dev/null +++ b/kig/macros/circle_by_point_and_segment.kigt @@ -0,0 +1,26 @@ +<!DOCTYPE KigMacroFile> +<KigMacroFile Number="1" Version="0.4.0" > + <Macro> + <Name>Circle by Point && Segment (as the Radius)</Name> + <Description>A circle defined by its center and the length of a segment as the radius</Description> + <ActionName>objects_new_circlebps</ActionName> + <IconFileName>circlebps</IconFileName> + <Construction> + <input requirement="point" id="1"> + <UseText>Construct a circle with this center</UseText> + <SelectStatement>Select the center of the new circle...</SelectStatement> + </input> + <input requirement="segment" id="2"> + <UseText>Construct a circle with the radius given by the length of this segment</UseText> + <SelectStatement>Select the segment whose length gives the radius of the new circle...</SelectStatement> + </input> + <intermediate action="fetch-property" property="length" id="3" > + <arg>2</arg> + </intermediate> + <result action="calc" type="CircleBPR" id="4" > + <arg>1</arg> + <arg>3</arg> + </result> + </Construction> + </Macro> +</KigMacroFile> diff --git a/kig/macros/equitriangle.kigt b/kig/macros/equitriangle.kigt new file mode 100644 index 00000000..efbb702b --- /dev/null +++ b/kig/macros/equitriangle.kigt @@ -0,0 +1,32 @@ +<!DOCTYPE KigMacroFile> +<KigMacroFile Number="1" Version="0.9.0" > + <Macro> + <Name>Equilateral Triangle</Name> + <Description>Equilateral triangle with given two vertices</Description> + <ActionName>objects_new_equitriangle</ActionName> + <IconFileName>equitriangle.png</IconFileName> + <Construction> + <input requirement="point" id="1" /> + <input requirement="point" id="2" /> + <intermediate action="calc" type="CircleBCP" id="3" > + <arg>1</arg> + <arg>2</arg> + </intermediate> + <intermediate action="calc" type="CircleBCP" id="4" > + <arg>2</arg> + <arg>1</arg> + </intermediate> + <intermediate action="push" type="int" id="5" >-1</intermediate> + <intermediate action="calc" type="CircleCircleIntersection" id="6" > + <arg>3</arg> + <arg>4</arg> + <arg>5</arg> + </intermediate> + <result action="calc" type="TriangleB3P" id="7" > + <arg>1</arg> + <arg>2</arg> + <arg>6</arg> + </result> + </Construction> + </Macro> +</KigMacroFile> diff --git a/kig/macros/evolute.kigt b/kig/macros/evolute.kigt new file mode 100644 index 00000000..aa15296e --- /dev/null +++ b/kig/macros/evolute.kigt @@ -0,0 +1,28 @@ +<!DOCTYPE KigMacroFile> +<KigMacroFile Number="1" Version="0.9.0" > + <Macro> + <Name>Evolute</Name> + <Description>Evolute of a curve</Description> + <ActionName>objects_new_evolute</ActionName> + <IconFileName>evolute</IconFileName> + <Construction> + <input requirement="curve" id="1"> + <UseText>Evolute of this curve</UseText> + <SelectStatement>Select the curve...</SelectStatement> + </input> + <intermediate action="push" type="hierarchy" id="2" > + <input requirement="point" id="1" /> + <input requirement="curve" id="2" /> + <result action="calc" type="CocCurve" id="3" > + <arg>2</arg> + <arg>1</arg> + </result> + </intermediate> + <result action="calc" type="Locus" id="3" > + <arg>2</arg> + <arg>1</arg> + <arg>1</arg> + </result> + </Construction> + </Macro> +</KigMacroFile> diff --git a/kig/macros/osculating_circle.kigt b/kig/macros/osculating_circle.kigt new file mode 100644 index 00000000..59ba450a --- /dev/null +++ b/kig/macros/osculating_circle.kigt @@ -0,0 +1,27 @@ +<!DOCTYPE KigMacroFile> +<KigMacroFile Number="1" Version="0.9.0" > + <Macro> + <Name>Osculating Circle</Name> + <Description>Osculating circle of a curve at a point</Description> + <ActionName>objects_new_osculatingcircle</ActionName> + <IconFileName>osculatingcircle</IconFileName> + <Construction> + <input requirement="curve" id="1"> + <UseText>Osculating circle of this curve</UseText> + <SelectStatement>Select the curve...</SelectStatement> + </input> + <input requirement="point" id="2"> + <UseText>Osculating circle at this point</UseText> + <SelectStatement>Select the point...</SelectStatement> + </input> + <intermediate action="calc" type="CocCurve" id="3" > + <arg>1</arg> + <arg>2</arg> + </intermediate> + <result action="calc" type="CircleBCP" id="4" > + <arg>3</arg> + <arg>2</arg> + </result> + </Construction> + </Macro> +</KigMacroFile> diff --git a/kig/macros/segment_axis.kigt b/kig/macros/segment_axis.kigt new file mode 100644 index 00000000..e86b90cf --- /dev/null +++ b/kig/macros/segment_axis.kigt @@ -0,0 +1,25 @@ +<!DOCTYPE KigMacroFile> +<KigMacroFile Number="1" Version="0.6.1" > + <Macro> + <Name>Segment Axis</Name> + <Description>The perpendicular line through a given segment's mid point.</Description> + <ActionName>objects_new_segment_axis</ActionName> + <IconFileName>segmentaxis</IconFileName> + <Construction> + <input requirement="segment" id="1"> + <UseText>Construct the axis of this segment</UseText> + <SelectStatement>Select the segment of which you want to draw the axis...</SelectStatement> + </input> + <intermediate action="fetch-property" property="mid-point" id="2" > + <arg>1</arg> + </intermediate> + <intermediate action="calc" type="Copy" id="3" > + <arg>2</arg> + </intermediate> + <result action="calc" type="LinePerpend" id="4" > + <arg>1</arg> + <arg>3</arg> + </result> + </Construction> + </Macro> +</KigMacroFile> diff --git a/kig/macros/square.kigt b/kig/macros/square.kigt new file mode 100644 index 00000000..09c500e0 --- /dev/null +++ b/kig/macros/square.kigt @@ -0,0 +1,44 @@ +<!DOCTYPE KigMacroFile> +<KigMacroFile Number="1" Version="0.9.0" > + <Macro> + <Name>Square</Name> + <Description>Square with two given adjacent vertices</Description> + <ActionName>objects_new_square</ActionName> + <IconFileName>square.png</IconFileName> + <Construction> + <input requirement="point" id="1" /> + <input requirement="point" id="2" /> + <intermediate action="calc" type="SegmentAB" id="3" > + <arg>1</arg> + <arg>2</arg> + </intermediate> + <intermediate action="fetch-property" property="mid-point" id="4" > + <arg>3</arg> + </intermediate> + <intermediate action="calc" type="CircleBCP" id="5" > + <arg>4</arg> + <arg>1</arg> + </intermediate> + <intermediate action="calc" type="SegmentAB" id="6" > + <arg>1</arg> + <arg>2</arg> + </intermediate> + <intermediate action="calc" type="LinePerpend" id="7" > + <arg>6</arg> + <arg>4</arg> + </intermediate> + <intermediate action="push" type="int" id="8" >-1</intermediate> + <intermediate action="calc" type="ConicLineIntersection" id="9" > + <arg>5</arg> + <arg>7</arg> + <arg>8</arg> + </intermediate> + <intermediate action="push" type="int" id="10" >4</intermediate> + <result action="calc" type="PoligonBCV" id="11" > + <arg>9</arg> + <arg>1</arg> + <arg>10</arg> + </result> + </Construction> + </Macro> +</KigMacroFile> diff --git a/kig/macros/vector_difference.kigt b/kig/macros/vector_difference.kigt new file mode 100644 index 00000000..c1b3be4b --- /dev/null +++ b/kig/macros/vector_difference.kigt @@ -0,0 +1,31 @@ +<!DOCTYPE KigMacroFile> +<KigMacroFile Number="1" Version="0.9.0" > + <Macro> + <Name>Vector Difference</Name> + <Description>Construct the vector difference of two vectors.</Description> + <ActionName>objects_new_vectordifference</ActionName> + <IconFileName>vectordifference</IconFileName> + <Construction> + <input requirement="vector" id="1"> + <UseText>Construct the vector difference of this vector and another one.</UseText> + <SelectStatement>Select the first of the two vectors of which you want to construct the difference...</SelectStatement> + </input> + <input requirement="vector" id="2"> + <UseText>Construct the vector difference of the other vector and this one.</UseText> + <SelectStatement>Select the other of the two vectors of which you want to construct the difference...</SelectStatement> + </input> + <input requirement="point" id="3"> + <UseText>Construct the vector difference starting at this point.</UseText> + <SelectStatement>Select the point to construct the difference vector in...</SelectStatement> + </input> + <intermediate action="fetch-property" property="vector-opposite" id="4" > + <arg>2</arg> + </intermediate> + <result action="calc" type="VectorSum" id="5" > + <arg>1</arg> + <arg>4</arg> + <arg>3</arg> + </result> + </Construction> + </Macro> +</KigMacroFile> diff --git a/kig/mimetypes/Makefile.am b/kig/mimetypes/Makefile.am new file mode 100644 index 00000000..38387bfc --- /dev/null +++ b/kig/mimetypes/Makefile.am @@ -0,0 +1,8 @@ +# mime types... +kdemime_DATA = x-kig.desktop x-kgeo.desktop x-kseg.desktop x-cabri.desktop x-drgeo.desktop +kdemimedir=$(kde_mimedir)/application + +magicdir = $(kde_confdir)/magic +magic_DATA = cabri.magic drgeo.magic + +KDE_ICON = AUTO diff --git a/kig/mimetypes/cabri.magic b/kig/mimetypes/cabri.magic new file mode 100644 index 00000000..fc2e0d5c --- /dev/null +++ b/kig/mimetypes/cabri.magic @@ -0,0 +1 @@ +0 string FIGURE\ CabriII\ vers\. application/x-cabri diff --git a/kig/mimetypes/cr128-mime-kig_doc.png b/kig/mimetypes/cr128-mime-kig_doc.png Binary files differnew file mode 100644 index 00000000..0e1feb83 --- /dev/null +++ b/kig/mimetypes/cr128-mime-kig_doc.png diff --git a/kig/mimetypes/cr16-mime-kig_doc.png b/kig/mimetypes/cr16-mime-kig_doc.png Binary files differnew file mode 100644 index 00000000..f3f2d75b --- /dev/null +++ b/kig/mimetypes/cr16-mime-kig_doc.png diff --git a/kig/mimetypes/cr22-mime-kig_doc.png b/kig/mimetypes/cr22-mime-kig_doc.png Binary files differnew file mode 100644 index 00000000..ce7b65cf --- /dev/null +++ b/kig/mimetypes/cr22-mime-kig_doc.png diff --git a/kig/mimetypes/cr32-mime-kig_doc.png b/kig/mimetypes/cr32-mime-kig_doc.png Binary files differnew file mode 100644 index 00000000..f80ef2dd --- /dev/null +++ b/kig/mimetypes/cr32-mime-kig_doc.png diff --git a/kig/mimetypes/cr48-mime-kig_doc.png b/kig/mimetypes/cr48-mime-kig_doc.png Binary files differnew file mode 100644 index 00000000..b6e039af --- /dev/null +++ b/kig/mimetypes/cr48-mime-kig_doc.png diff --git a/kig/mimetypes/cr64-mime-kig_doc.png b/kig/mimetypes/cr64-mime-kig_doc.png Binary files differnew file mode 100644 index 00000000..c2836c88 --- /dev/null +++ b/kig/mimetypes/cr64-mime-kig_doc.png diff --git a/kig/mimetypes/crsc-mime-kig_doc.svgz b/kig/mimetypes/crsc-mime-kig_doc.svgz Binary files differnew file mode 100644 index 00000000..1a56d017 --- /dev/null +++ b/kig/mimetypes/crsc-mime-kig_doc.svgz diff --git a/kig/mimetypes/drgeo.magic b/kig/mimetypes/drgeo.magic new file mode 100644 index 00000000..f5fc2a63 --- /dev/null +++ b/kig/mimetypes/drgeo.magic @@ -0,0 +1 @@ +0 string \<?xml\ version="1.0"?\>\n\<drgenius\> application/x-drgeo diff --git a/kig/mimetypes/x-cabri.desktop b/kig/mimetypes/x-cabri.desktop new file mode 100644 index 00000000..b02d7ac9 --- /dev/null +++ b/kig/mimetypes/x-cabri.desktop @@ -0,0 +1,67 @@ +# KDE Config File +[Desktop Entry] +MimeType=application/x-cabri +Icon=kig_doc +Comment=Cabri Figure +Comment[af]=Cabri-figuur +Comment[ar]=شكل كابري +Comment[be]=Фігура Cabri +Comment[bg]=Фигура Cabri +Comment[bn]=কà§à¦¯à¦¾à¦¬à¦°à§€ ছবি +Comment[bs]=Cabri slika +Comment[ca]=Figura de cabri +Comment[cs]=Cabri vzor +Comment[csb]=Céchùnk Cabri +Comment[cy]=Ffigur Cabri +Comment[da]=Cabri figur +Comment[de]=Cabri-Zeichnung +Comment[el]=ΣχÎδιο του Cabri +Comment[eo]=Cabri figuro +Comment[es]=Figura de Cabri +Comment[et]=Cabri kujund +Comment[eu]=Cabri irudia +Comment[fa]=Ø´Ú©Ù„ Cabri +Comment[fi]=Cabri-kaavio +Comment[fr]=Figure Cabri +Comment[ga]=Léaráid Cabri +Comment[gl]=Figura de Cabri +Comment[hi]=केबरी रूप +Comment[hr]=Cabri oblik +Comment[hu]=Cabri ábra +Comment[is]=Cabri mynd +Comment[it]=Figura di Cabri +Comment[ja]=Cabri 図 +Comment[ka]=Cabri ფიგურრ+Comment[km]=រូប​ពន្យល់ Cabri +Comment[lt]=Cabri figÅ«ra +Comment[lv]=Cabri FigÅ«ra +Comment[mk]=Cabri фигура +Comment[mn]=Cabri Ð¥ÑлбÑÑ€ +Comment[nb]=Cabri-figur +Comment[nds]="Cabri"-Figuur +Comment[ne]=काबà¥à¤°à¥€ आकृति +Comment[nl]=Cabri-figuur +Comment[nn]=Cabri-figur +Comment[pl]=Rysunek Cabri +Comment[pt]=Figura do Cabri +Comment[pt_BR]=Figura do Cabri +Comment[ru]=Фигура Cabri +Comment[sk]=Cabri vzor +Comment[sl]=Lik Cabri +Comment[sr]=Cabri фигура +Comment[sr@Latn]=Cabri figura +Comment[sv]=Cabri-figur +Comment[ta]=கபிரி படம௠+Comment[tg]=Фигураи КÑбри +Comment[tr]=Cabri Figürü +Comment[uk]=Фігура Cabri +Comment[ven]=Tshiimo tsha Cabri +Comment[vi]=Hình Cabri +Comment[zh_CN]=Cabri 图形 +Comment[zh_TW]=Cabri 圖形 +Type=MimeType +Patterns=*.fig;*.FIG +X-KDE-AutoEmbed=false +[Property::X-KDE-NativeExtension] +Type=QString +Value=.FIG diff --git a/kig/mimetypes/x-drgeo.desktop b/kig/mimetypes/x-drgeo.desktop new file mode 100644 index 00000000..7a19faf7 --- /dev/null +++ b/kig/mimetypes/x-drgeo.desktop @@ -0,0 +1,62 @@ +# KDE Config File +[Desktop Entry] +MimeType=application/x-drgeo +Icon=kig_doc +Comment=Dr. Geo Figure +Comment[af]=Dr. Geo-figuur +Comment[be]=Фігура Dr. Geo +Comment[bg]=Фигура Dr. Geo +Comment[bn]=ড. জিও ছবি +Comment[bs]=Dr. Geo slika +Comment[ca]=Figura de Dr. Geo +Comment[cs]=Dr. Geo +Comment[csb]=Céchùnk Dr Geo +Comment[cy]=Ffigur Dr. Geo +Comment[da]=Dr. Geo-figur +Comment[de]=Dr. Geo-Zeichnung +Comment[el]=ΣχÎδιο του Dr. Geo +Comment[eo]=Dr. Geo figuro +Comment[es]=Figura de Dr. Geo +Comment[et]=Dr. Geo kujund +Comment[eu]=Dr. Geo irudia +Comment[fa]=Ø´Ú©Ù„ دکتر جیو +Comment[fi]=Dr. Geo -kaavio +Comment[fr]=Figure Dr. Geo +Comment[ga]=Léaráid Dr. Geo +Comment[gl]=Figura de Dr. Geo +Comment[hi]=डॉ. जिओ आकार +Comment[hu]=Dr. Geo-ábra +Comment[is]=Dr. Geo mynd +Comment[it]=Figura di Dr. Geo +Comment[ja]=Dr. Geo 図 +Comment[ka]=Dr. Geo ფიგურრ+Comment[km]=រូប​ពន្យល់ Dr. Geo +Comment[lt]=Dr. Geo figÅ«ra +Comment[ms]=Figure Dr. Geo +Comment[nb]=Dr. Geo-figur +Comment[nds]="Dr. Geo"-Figuur +Comment[ne]=डा. जिव फिगर +Comment[nl]=Dr. Geo-figuur +Comment[nn]=Dr. Geo-figur +Comment[pl]=Rysunek Dr. Geo +Comment[pt]=Figura do Dr. Geo +Comment[pt_BR]=Figura do Dr.Geo +Comment[ru]=Фигура Dr. Geo +Comment[sk]=Obrázok Dr. Geo +Comment[sl]=Lik Dr. Geo +Comment[sr]=Dr. Geo Ñлика +Comment[sr@Latn]=Dr. Geo slika +Comment[sv]=Dr. Geo-figur +Comment[ta]= டா. ஜியோ படம௠+Comment[tg]=Фигураи геометрӣ +Comment[tr]=Dr. Geo Figürü +Comment[uk]=Фігура Dr. Geo +Comment[vi]=Tiến sÄ© Hình Hình há»c +Comment[zh_CN]=Dr. Geo 图形 +Comment[zh_TW]=Dr. Geo 圖形 +Type=MimeType +Patterns=*.fgeo +X-KDE-AutoEmbed=false +[Property::X-KDE-NativeExtension] +Type=QString +Value=.fgeo diff --git a/kig/mimetypes/x-kgeo.desktop b/kig/mimetypes/x-kgeo.desktop new file mode 100644 index 00000000..5fb0b12d --- /dev/null +++ b/kig/mimetypes/x-kgeo.desktop @@ -0,0 +1,69 @@ +# KDE Config File +[Desktop Entry] +MimeType=application/x-kgeo +Icon=kig_doc +Comment=KGeo Figure +Comment[af]=KGeo-figuur +Comment[ar]=شكل هندسي Ùƒ +Comment[be]=Фігура KGeo +Comment[bg]=Фигура KGeo +Comment[bn]=কে-জিও ছবি +Comment[br]=Skeudenn KGeo +Comment[bs]=KGeo slika +Comment[ca]=Figura de KGeo +Comment[cs]=KGeo vzor +Comment[csb]=Céchùnk KGeo +Comment[cy]=Ffigur KGeo +Comment[da]=KGeo figur +Comment[de]=KGeo-Zeichnung +Comment[el]=ΣχÎδιο του KGeo +Comment[eo]=KGeo figuro +Comment[es]=Figura de KGeo +Comment[et]=KGeo kujund +Comment[eu]=KGeo irudia +Comment[fi]=KGeo-kaavio +Comment[fr]=Figure KGeo +Comment[ga]=Léaráid KGeo +Comment[gl]=Figura de KGeo +Comment[hi]=के-जिओ रूप +Comment[hr]=KGeo oblik +Comment[hu]=KGeo ábra +Comment[is]=KGeo mynd +Comment[it]=Figura di KGeo +Comment[ja]=KGeo 図 +Comment[ka]=KGeo ფიგურრ+Comment[km]=រូប​ពន្យល់ KGeo +Comment[lt]=KGeo figÅ«ra +Comment[lv]=KGeo FigÅ«ra +Comment[mk]=KGeo фигура +Comment[mn]=КГео Ð¥ÑлбÑÑ€ +Comment[nb]=KGeo-figur +Comment[nds]="KGeo"-Figuur +Comment[ne]=केडीई जिव फिगर +Comment[nl]=KGeo-figuur +Comment[nn]=KGeo-figur +Comment[pa]=KGeo ਸ਼ਕਲਾਂ +Comment[pl]=Rysunek KGeo +Comment[pt]=Figura do KGeo +Comment[pt_BR]=Figura do KGeo +Comment[ru]=Фигура KGeo +Comment[sk]=KGeo vzor +Comment[sl]=Lik KGeo +Comment[sr]=KGeo фигура +Comment[sr@Latn]=KGeo figura +Comment[sv]=Kgeo-figur +Comment[ta]=கேஜியோ படம௠+Comment[tg]=Фигураи KGeo +Comment[tr]=KGeo Figürü +Comment[uk]=Фігура KGeo +Comment[ven]=Tshiimo tsha KGeo +Comment[vi]=Hình KGeo +Comment[xh]=Umfanekiso we KGeo +Comment[zh_CN]=KGeo 图形 +Comment[zh_TW]=KGeo 圖形 +Type=MimeType +Patterns=*.kgeo; +X-KDE-AutoEmbed=false +[Property::X-KDE-NativeExtension] +Type=QString +Value=.kgeo diff --git a/kig/mimetypes/x-kig.desktop b/kig/mimetypes/x-kig.desktop new file mode 100644 index 00000000..95fcce4c --- /dev/null +++ b/kig/mimetypes/x-kig.desktop @@ -0,0 +1,69 @@ +# KDE Config File +[Desktop Entry] +MimeType=application/x-kig +Icon=kig_doc +Comment=Kig Figure +Comment[af]=Kig-figuur +Comment[ar]=شكل لكيج +Comment[be]=Фігура Kig +Comment[bg]=Фигура Kig +Comment[bn]=কিগ ছবি +Comment[br]=Skeudenn Kig +Comment[bs]=Kig datoteka +Comment[ca]=Figura de Kig +Comment[cs]=Kig vzor +Comment[csb]=Céchùnk Kig +Comment[cy]=Ffigur Kig +Comment[da]=Kig figur +Comment[de]=Kig-Zeichnung +Comment[el]=ΣχÎδιο του Kig +Comment[eo]=Kig figuro +Comment[es]=Figura de Kig +Comment[et]=Kigi kujund +Comment[eu]=Kig irudia +Comment[fa]=Ø´Ú©Ù„ Kig +Comment[fi]=Kig-kaavio +Comment[fr]=Figure Kig +Comment[ga]=Léaráid Kig +Comment[gl]=Figura de Kig +Comment[hi]=के-इग रूप +Comment[hr]=Kig oblik +Comment[hu]=Kig ábra +Comment[is]=Kig mynd +Comment[it]=Figura di Kig +Comment[ja]=Kig 図 +Comment[ka]=Kig ფიგურრ+Comment[km]=រូប​ពន្យល់ Kig +Comment[lt]=Kig figÅ«ra +Comment[lv]=Kig FigÅ«ra +Comment[mk]=Kig фигура +Comment[mn]=Kig Ð¥ÑлбÑÑ€ +Comment[nb]=Kig-figur +Comment[nds]="Kig"-Figuur +Comment[ne]=किग फिगर +Comment[nl]=Kig-figuur +Comment[nn]=Kig-figur +Comment[pl]=Rysunek Kig +Comment[pt]=Figura do Kig +Comment[pt_BR]=Figura do Kig +Comment[ru]=Фигура Kig +Comment[sk]=Kig vzor +Comment[sl]=Lik Kig +Comment[sr]=Kig фигура +Comment[sr@Latn]=Kig figura +Comment[sv]=Kig-figur +Comment[ta]=கிக௠படம௠+Comment[tg]=Фигураи Kig +Comment[tr]=Kig Figürü +Comment[uk]=Фігура Kig +Comment[ven]=Tshiimo tsha Kig +Comment[vi]=Hình Kig +Comment[xh]=Kig Inani +Comment[zh_CN]=Kig 图形 +Comment[zh_TW]=Kig 圖形 +Type=MimeType +Patterns=*.kig;*.kigz; +X-KDE-AutoEmbed=false +[Property::X-KDE-NativeExtension] +Type=QString +Value=.kig;*.kigz diff --git a/kig/mimetypes/x-kseg.desktop b/kig/mimetypes/x-kseg.desktop new file mode 100644 index 00000000..daac2747 --- /dev/null +++ b/kig/mimetypes/x-kseg.desktop @@ -0,0 +1,68 @@ +# KDE Config File +[Desktop Entry] +MimeType=application/x-kseg +Icon=kig_doc +Comment=KSeg Document +Comment[af]=KSeg Dokument +Comment[ar]=مستند قسم Ùƒ +Comment[be]=Дакумент KSeg +Comment[bg]=Документ на KSeg +Comment[bn]=কে-সেগ ডকà§à¦®à§‡à¦¨à§à¦Ÿ +Comment[br]=Teul KSeg +Comment[bs]=KSeg dokument +Comment[ca]=Document de KSeg +Comment[cs]=Dokument KSeg +Comment[csb]=Dokùment KSeg +Comment[cy]=Dogfen KSeg +Comment[da]=KSeg-dokument +Comment[de]=KSeg-Dokument +Comment[el]=ΈγγÏαφο KSeg +Comment[eo]=KSeg dokumento +Comment[es]=Documento de KSeg +Comment[et]=KSegi dokument +Comment[eu]=KSeg dokumentua +Comment[fa]=سند KSeg +Comment[fi]=KSeg-asiakirja +Comment[fr]=Document KSeg +Comment[ga]=Cáipéis KSeg +Comment[gl]=Documento de KSeg +Comment[he]=מסמך KSeg +Comment[hi]=के-सेग दसà¥à¤¤à¤¾à¤µà¥‡à¤œà¤¼ +Comment[hr]=KSeg dokument +Comment[hu]=KSeg dokumentum +Comment[is]=KSeg skjal +Comment[it]=Documento KSeg +Comment[ja]=KSeg ドã‚ュメント +Comment[ka]=KSeg დáƒáƒ™áƒ£áƒ›áƒ”ნტი +Comment[km]=ឯកសារ KSeg +Comment[lt]=KSeg dokumentas +Comment[lv]=KSeg Dokuments +Comment[mk]=KSeg документ +Comment[ms]=Dokumen KSeg +Comment[nb]=KSeg-dokument +Comment[nds]=KSeg-Dokment +Comment[ne]=केडीई सेग कागजात +Comment[nl]=KSeg-document +Comment[nn]=KSeg-dokument +Comment[pl]=Dokument KSeg +Comment[pt]=Documento do KSeg +Comment[pt_BR]=Documento do KSeg +Comment[ru]=Документ KSeg +Comment[sk]=Dokument KSeg +Comment[sl]=Dokument KSeg +Comment[sr]=Kseg документ +Comment[sr@Latn]=Kseg dokument +Comment[sv]=Kseg-dokument +Comment[ta]=கேசக௠ஆவணம௠+Comment[tg]=Ҳуҷҷати KSeg +Comment[tr]=KSeg Belgesi +Comment[uk]=Документ KSeg +Comment[vi]=Tà i liệu KSeg +Comment[zh_CN]=KSeg 文档 +Comment[zh_TW]=KSeg 文件 +Type=MimeType +Patterns=*.seg; +X-KDE-AutoEmbed=false +[Property::X-KDE-NativeExtension] +Type=QString +Value=.seg diff --git a/kig/misc/Makefile.am b/kig/misc/Makefile.am new file mode 100644 index 00000000..d97d7989 --- /dev/null +++ b/kig/misc/Makefile.am @@ -0,0 +1,50 @@ +INCLUDES=$(all_includes) +noinst_LTLIBRARIES=libmisc.la +noinst_HEADERS = \ + argsparser.h \ + builtin_stuff.h \ + calcpaths.h \ + common.h \ + conic-common.h \ + coordinate.h \ + coordinate_system.h \ + cubic-common.h \ + goniometry.h \ + guiaction.h \ + kigfiledialog.h \ + kiginputdialog.h \ + kignumerics.h \ + kigpainter.h \ + kigtransform.h \ + lists.h \ + object_constructor.h \ + object_hierarchy.h \ + rect.h \ + screeninfo.h \ + special_constructors.h + +libmisc_la_SOURCES = \ + argsparser.cpp \ + builtin_stuff.cc \ + calcpaths.cc \ + common.cpp \ + conic-common.cpp \ + coordinate.cpp \ + coordinate_system.cpp \ + cubic-common.cc \ + goniometry.cc \ + guiaction.cc \ + kigfiledialog.cc \ + kiginputdialog.cc \ + kignumerics.cpp \ + kigpainter.cpp \ + kigtransform.cpp \ + lists.cc \ + object_constructor.cc \ + object_hierarchy.cc \ + rect.cc \ + screeninfo.cc \ + special_constructors.cc + +libmisc_la_LIBADD=-lm +METASOURCES=AUTO diff --git a/kig/misc/argsparser.cpp b/kig/misc/argsparser.cpp new file mode 100644 index 00000000..c2387970 --- /dev/null +++ b/kig/misc/argsparser.cpp @@ -0,0 +1,267 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "argsparser.h" + +#include "../objects/object_imp.h" +#include "../objects/object_holder.h" + +#include <cassert> +#include <algorithm> +#include <kdebug.h> + +void ArgsParser::initialize( const struct spec* args, int n ) +{ + std::vector<spec> vect( args, args + n ); + initialize( vect ); +} + +ArgsParser::ArgsParser() +{ +} + +ArgsParser::ArgsParser( const std::vector<spec>& args ) +{ + initialize( args ); +} + +void ArgsParser::initialize( const std::vector<spec>& args ) +{ + margs = args; +} + +ArgsParser::ArgsParser( const spec* args, int n ) +{ + initialize( args, n ); +} + +static bool hasimp( const ObjectCalcer& o, const ObjectImpType* imptype ) +{ + return o.imp()->inherits( imptype ); +} + +static bool hasimp( const ObjectImp& o, const ObjectImpType* imptype ) +{ + return o.inherits( imptype ); +} + +static bool isvalid( const ObjectImp& o ) +{ + return o.valid(); +} + +static bool isvalid( const ObjectCalcer& o ) +{ + return o.imp()->valid(); +} + +// we use a template method that is used for both Objects and Args to +// not have to write the same thing twice.. +template <class Collection> +static int check( const Collection& c, const std::vector<ArgsParser::spec>& margs ) +{ + std::vector<bool> found( margs.size() ); + + for ( typename Collection::const_iterator o = c.begin(); o != c.end(); ++o ) + { + for ( uint i = 0; i < margs.size(); ++i ) + { + if ( hasimp( **o, margs[i].type ) && !found[i] ) + { + // object o is of a type that we're looking for + found[i] = true; + goto matched; + }; + }; + return ArgsParser::Invalid; + matched: + ; + }; + for( uint i = 0; i < margs.size(); ++i ) + if ( !found[i] ) return ArgsParser::Valid; + return ArgsParser::Complete; +} + +int ArgsParser::check( const Args& os ) const +{ + return ::check( os, margs ); +} + +int ArgsParser::check( const std::vector<ObjectCalcer*>& os ) const +{ + return ::check( os, margs ); +} + +template <typename Collection> +static Collection parse( const Collection& os, + const std::vector<ArgsParser::spec> margs ) +{ + Collection ret( margs.size(), static_cast<typename Collection::value_type>( 0 ) ); + + for ( typename Collection::const_iterator o = os.begin(); o != os.end(); ++o ) + { + for( uint i = 0; i < margs.size(); ++i ) + if ( hasimp( **o, margs[i].type ) && ret[i] == 0 ) + { + // object o is of a type that we're looking for + ret[i] = *o; + goto added; + } + added: + ; + }; + // remove 0's from the output.. + ret.erase( + std::remove( ret.begin(), ret.end(), + static_cast<typename Collection::value_type>( 0 ) ), + ret.end() ); + return ret; +} + +Args ArgsParser::parse( const Args& os ) const +{ + return ::parse( os, margs ); +} + +std::vector<ObjectCalcer*> ArgsParser::parse( const std::vector<ObjectCalcer*>& os ) const +{ + return ::parse( os, margs ); +} + +ArgsParser ArgsParser::without( const ObjectImpType* type ) const +{ + std::vector<spec> ret; + ret.reserve( margs.size() - 1 ); + for ( uint i = 0; i < margs.size(); ++i ) + if ( margs[i].type != type ) + ret.push_back( margs[i] ); + return ArgsParser( ret ); +} + +ArgsParser::spec ArgsParser::findSpec( const ObjectImp* obj, const Args& parents ) const +{ + spec ret; + ret.type = 0; + + std::vector<bool> found( margs.size(), false ); + + for ( Args::const_iterator o = parents.begin(); + o != parents.end(); ++o ) + { + for ( uint i = 0; i < margs.size(); ++i ) + { + if ( (*o)->inherits( margs[i].type ) && !found[i] ) + { + // object o is of a type that we're looking for + found[i] = true; + if ( *o == obj ) return margs[i]; + // i know that "goto's are *evil*", but they're very useful + // and completely harmless if you use them as better "break;" + // statements.. trust me ;) + goto matched; + }; + }; + matched: + ; + }; + kdDebug() << k_funcinfo << "no proper spec found :(" << endl; + return ret; +} + +const ObjectImpType* ArgsParser::impRequirement( + const ObjectImp* o, const Args& parents ) const +{ + spec s = findSpec( o, parents ); + return s.type; +} + +std::string ArgsParser::usetext( const ObjectImp* obj, const Args& sel ) const +{ + spec s = findSpec( obj, sel ); + return s.usetext; +} + +template<typename Collection> +static bool checkArgs( const Collection& os, uint min, const std::vector<ArgsParser::spec>& argsspec ) +{ + assert( os.size() <= argsspec.size() ); + if( os.size() < min ) return false; + uint checknum = os.size(); + for ( uint i = 0; i < checknum; ++i ) + { + if( !isvalid( *os[i] ) ) return false; + if( !hasimp( *os[i], argsspec[i].type ) ) return false; + } + return true; +} + +bool ArgsParser::checkArgs( const Args& os ) const +{ + return checkArgs( os, margs.size() ); +} + +bool ArgsParser::checkArgs( const Args& os, uint min ) const +{ + return ::checkArgs( os, min, margs ); +} + +bool ArgsParser::checkArgs( const std::vector<ObjectCalcer*>& os ) const +{ + return checkArgs( os, margs.size() ); +} + +bool ArgsParser::checkArgs( const std::vector<ObjectCalcer*>& os, uint minobjects ) const +{ + return ::checkArgs( os, minobjects, margs ); +} + +ArgsParser::~ArgsParser() +{ +} + +bool ArgsParser::isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const +{ + spec s = findSpec( o, parents ); + return s.onOrThrough; +} + +std::string ArgsParser::selectStatement( const Args& selection ) const +{ + std::vector<bool> found( margs.size(), false ); + + for ( Args::const_iterator o = selection.begin(); + o != selection.end(); ++o ) + { + for ( uint i = 0; i < margs.size(); ++i ) + { + if ( (*o)->inherits( margs[i].type ) && !found[i] ) + { + // object o is of a type that we're looking for + found[i] = true; + break; + } + } + } + for ( uint i = 0; i < margs.size(); ++i ) + { + if ( !found[i] ) + return margs[i].selectstat; + } + kdDebug() << k_funcinfo << "no proper select statement found :(" << endl; + return 0; +} + diff --git a/kig/misc/argsparser.h b/kig/misc/argsparser.h new file mode 100644 index 00000000..001d9359 --- /dev/null +++ b/kig/misc/argsparser.h @@ -0,0 +1,187 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_MISC_ARGSPARSER_H +#define KIG_MISC_ARGSPARSER_H + +#include "../objects/common.h" + +#include <string> + +class ObjectImpType; + +/** + * This class is meant to take care of checking the types of the + * parents to ObjectCalcer's, and to put them in the correct order. + * An ObjectType should construct an ArgsParser with a specification + * of the arguments it wants. This specification is given as an array + * of ArgsParser::spec structs. This struct contains a pointer to an + * ObjectImpType ( which is the type you want the argument to have ), + * a string ( which is an I18N_NOOP'd string describing what you will + * be using the argument for ) and a boolean ( which says whether the + * constructed object is by construction on the curve argument ( if + * the constructed object is a point ), or whether the constructed + * object is by construction through the point argument ( if the + * constructed object is a curve ) ). + * + * An ObjectType using an ArgsParser to take care of the various + * things that it can handle ( impRequirement, the sortArgs functions + * and the isDefinedOnOrThrough stuff ), should inherit from + * ArgsParserObjectType, which takes care of calling the ArgsParser + * for these things... It also allows you to use a convenient + * ObjectConstructor for your type. + * + * E.g., let's see what CircleBCPType has for its arguments spec: + * here's some code: + * \code + * static const ArgsParser::spec argsspecTranslation[] = + * { + * { ObjectImp::stype(), I18N_NOOP("Translate this object"), false }, + * { VectorImp::stype(), I18N_NOOP("Translate by this vector"), false } + * }; + * + * TranslatedType::TranslatedType() + * : ArgsParserObjectType( "Translation", argsspecTranslation, 2 ) + * { + * } + * + * ObjectImp* TranslatedType::calc( const Args& args, const KigDocument& ) const + * { + * if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + * + * Coordinate dir = static_cast<const VectorImp*>( args[1] )->dir(); + * Transformation t = Transformation::translation( dir ); + * + * return args[0]->transform( t ); + * } + * \endcode + * + * As you can see above, the argsspec can be declared right in the + * cpp-file. The usetexts explain to the user what the argument in + * question will be used for. The boolean says that in this case, the + * constructed object is not by construction on or through one of its + * arguments. In the constructor, you simply call the + * ArgsParserObjectType with the argsspec struct you defined, and the + * number of arguments in the argsspec ( in this case 2 ). + * + * In the calc function, you can rely on the arguments already being + * in the correct order ( the same order as you put them in in the + * arguments spec. You should use the checkArgs function to check if + * all the arguments are valid, and if they aren't return a + * InvalidImp. All objects can always become invalid ( e.g. an + * intersection point of two non-intersecting conics can become valid + * again when the conics move ), and you should always check for this. + * + * An interesting to note here is that the first argument is of a more + * general type than the second. A VectorImp is *also* an ObjectImp. + * In general, when this happens, you should put the more general type + * first, as in general this produces the results that the user + * expects. I have no formal proof for this, just talking from + * experience. It might be that you experience different things, but + * unless you're sure of the results, put the more general type first. + * + * This class uses a pretty basic algorithm for doing the parsing ( + * e.g. if a match fails in one order, it does not try a different + * order, which could perhaps be necessary in the case of having more + * general argument types in the same argument spec ). However, the + * current algorithm works in all the situation where I've tested it, + * and I don't feel the need to change it. Feel free to do so if you + * like, but even if you do, I'm not sure if I will include it in + * mainline Kig. + */ +class ArgsParser +{ +public: + /** + * this are some enum values that we return from some functions. + */ + enum { Invalid = 0, Valid = 1, Complete = 2 }; + struct spec { const ObjectImpType* type; std::string usetext; std::string selectstat; bool onOrThrough;}; +private: + /** + * the args spec.. + */ + std::vector<spec> margs; + + spec findSpec( const ObjectImp* o, const Args& parents ) const; +public: + ArgsParser( const struct spec* args, int n ); + ArgsParser( const std::vector<spec>& args ); + ArgsParser(); + ~ArgsParser(); + + void initialize( const std::vector<spec>& args ); + void initialize( const struct spec* args, int n ); + + /** + * returns a new ArgsParser that wants the same args, except for the + * ones of the given type.. + */ + ArgsParser without( const ObjectImpType* type ) const; + // checks if os matches the argument list this parser should parse.. + int check( const Args& os ) const; + int check( const std::vector<ObjectCalcer*>& os ) const; + /** + * returns the usetext for the argument that o would be used for, + * if sel were used as parents.. + * \p o should be in \p sel ... + */ + std::string usetext( const ObjectImp* o, const Args& sel ) const; + + /** + * returns the select statement for the next selectable argument + * when the given args are selected. + */ + std::string selectStatement( const Args& sel ) const; + + // this reorders the objects or args so that they are in the same + // order as the requested arguments.. + Args parse( const Args& os ) const; + std::vector<ObjectCalcer*> parse( const std::vector<ObjectCalcer*>& os ) const; + + /** + * returns the minimal ObjectImp ID that \p o needs to inherit in order + * to be useful.. \p o should be part of \p parents . + */ + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + + /** + * Supposing that \p parents would be given as parents, this function + * returns whether the returned ObjectImp will be, by construction, + * on \p o ( if \p o is a curve ), or through \p o ( if \p o is a point ). + */ + bool isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const; + + // Checks the args according to this args specification. If the + // objects should never have occurred, then an assertion failure + // will happen, if one of the args is invalid, then false will be + // returned, if all is fine, then true is returned.. + // assert that the objects are of the right types, and in the right + // order as what would be returned by parse( os ).. If minobjects + // is provided, then not all objects are needed, and it is enough if + // at least minobjects are available.. Use this for object types + // that can calc a temporary example object using less than the + // required args. These args need to be at the end of argsspec + + // anyobjsspec. If minobjects is not provided, then it is assumed + // that all args are necessary. + bool checkArgs( const std::vector<ObjectCalcer*>& os ) const; + bool checkArgs( const std::vector<ObjectCalcer*>& os, uint minobjects ) const; + bool checkArgs( const Args& os ) const; + bool checkArgs( const Args& os, uint minobjects ) const; +}; + +#endif diff --git a/kig/misc/boost_intrusive_pointer.hpp b/kig/misc/boost_intrusive_pointer.hpp new file mode 100644 index 00000000..a278e736 --- /dev/null +++ b/kig/misc/boost_intrusive_pointer.hpp @@ -0,0 +1,256 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + + + +// This code comes from the boost::intrusive_ptr. I adapted it to +// suit my needs ( no dependencies on other boost libs, change the +// namespace to avoid conflicts, + +#ifndef MYBOOST_INTRUSIVE_PTR_HPP_INCLUDED +#define MYBOOST_INTRUSIVE_PTR_HPP_INCLUDED + +// +// intrusive_ptr.hpp +// +// Copyright (c) 2001, 2002 Peter Dimov +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +// See http://www.boost.org/libs/smart_ptr/intrusive_ptr.html for documentation. +// + +#include <functional> // for std::less +#include <iosfwd> // for std::basic_ostream + + +namespace myboost +{ + +// +// intrusive_ptr +// +// A smart pointer that uses intrusive reference counting. +// +// Relies on unqualified calls to +// +// void intrusive_ptr_add_ref(T * p); +// void intrusive_ptr_release(T * p); +// +// (p != 0) +// +// The object is responsible for destroying itself. +// + +template<class T> class intrusive_ptr +{ +private: + + typedef intrusive_ptr this_type; + +public: + + typedef T element_type; + + intrusive_ptr(): p_(0) + { + } + + intrusive_ptr(T * p, bool add_ref = true): p_(p) + { + if(p_ != 0 && add_ref) intrusive_ptr_add_ref(p_); + } + +#if !defined(BOOST_NO_MEMBER_TEMPLATES) || defined(BOOST_MSVC6_MEMBER_TEMPLATES) + + template<class U> intrusive_ptr(intrusive_ptr<U> const & rhs): p_(rhs.get()) + { + if(p_ != 0) intrusive_ptr_add_ref(p_); + } + +#endif + + intrusive_ptr(intrusive_ptr const & rhs): p_(rhs.p_) + { + if(p_ != 0) intrusive_ptr_add_ref(p_); + } + + ~intrusive_ptr() + { + if(p_ != 0) intrusive_ptr_release(p_); + } + +#if !defined(BOOST_NO_MEMBER_TEMPLATES) || defined(BOOST_MSVC6_MEMBER_TEMPLATES) + + template<class U> intrusive_ptr & operator=(intrusive_ptr<U> const & rhs) + { + this_type(rhs).swap(*this); + return *this; + } + +#endif + + intrusive_ptr & operator=(intrusive_ptr const & rhs) + { + this_type(rhs).swap(*this); + return *this; + } + + intrusive_ptr & operator=(T * rhs) + { + this_type(rhs).swap(*this); + return *this; + } + + T * get() const + { + return p_; + } + + T & operator*() const + { + return *p_; + } + + T * operator->() const + { + return p_; + } + + typedef T * (intrusive_ptr::*unspecified_bool_type) () const; + + operator unspecified_bool_type () const + { + return p_ == 0? 0: &intrusive_ptr::get; + } + + // operator! is a Borland-specific workaround + bool operator! () const + { + return p_ == 0; + } + + void swap(intrusive_ptr & rhs) + { + T * tmp = p_; + p_ = rhs.p_; + rhs.p_ = tmp; + } + +private: + + T * p_; +}; + +template<class T, class U> inline bool operator==(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b) +{ + return a.get() == b.get(); +} + +template<class T, class U> inline bool operator!=(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b) +{ + return a.get() != b.get(); +} + +template<class T> inline bool operator==(intrusive_ptr<T> const & a, T * b) +{ + return a.get() == b; +} + +template<class T> inline bool operator!=(intrusive_ptr<T> const & a, T * b) +{ + return a.get() != b; +} + +template<class T> inline bool operator==(T * a, intrusive_ptr<T> const & b) +{ + return a == b.get(); +} + +template<class T> inline bool operator!=(T * a, intrusive_ptr<T> const & b) +{ + return a != b.get(); +} + +#if __GNUC__ == 2 && __GNUC_MINOR__ <= 96 + +// Resolve the ambiguity between our op!= and the one in rel_ops + +template<class T> inline bool operator!=(intrusive_ptr<T> const & a, intrusive_ptr<T> const & b) +{ + return a.get() != b.get(); +} + +#endif + +template<class T> inline bool operator<(intrusive_ptr<T> const & a, intrusive_ptr<T> const & b) +{ + return std::less<T *>()(a.get(), b.get()); +} + +template<class T> void swap(intrusive_ptr<T> & lhs, intrusive_ptr<T> & rhs) +{ + lhs.swap(rhs); +} + +// mem_fn support + +template<class T> T * get_pointer(intrusive_ptr<T> const & p) +{ + return p.get(); +} + +template<class T, class U> intrusive_ptr<T> static_pointer_cast(intrusive_ptr<U> const & p) +{ + return static_cast<T *>(p.get()); +} + +template<class T, class U> intrusive_ptr<T> dynamic_pointer_cast(intrusive_ptr<U> const & p) +{ + return dynamic_cast<T *>(p.get()); +} + +// operator<< + +#if defined(__GNUC__) && (__GNUC__ < 3) + +template<class Y> std::ostream & operator<< (std::ostream & os, intrusive_ptr<Y> const & p) +{ + os << p.get(); + return os; +} + +#else + +template<class E, class T, class Y> std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os, intrusive_ptr<Y> const & p) +{ + os << p.get(); + return os; +} + +#endif + +} // namespace myboost + +#ifdef BOOST_MSVC +# pragma warning(pop) +#endif + +#endif // #ifndef MYBOOST_INTRUSIVE_PTR_HPP_INCLUDED diff --git a/kig/misc/builtin_stuff.cc b/kig/misc/builtin_stuff.cc new file mode 100644 index 00000000..e162e7ac --- /dev/null +++ b/kig/misc/builtin_stuff.cc @@ -0,0 +1,596 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "builtin_stuff.h" + +#include <config.h> + +#include "object_constructor.h" +#include "lists.h" +#include "special_constructors.h" +#include "guiaction.h" + +#include "../objects/angle_type.h" +#include "../objects/arc_type.h" +#include "../objects/circle_type.h" +#include "../objects/conic_types.h" +#include "../objects/cubic_type.h" +#include "../objects/intersection_types.h" +#include "../objects/inversion_type.h" +#include "../objects/line_imp.h" +#include "../objects/line_type.h" +#include "../objects/object_imp.h" +#include "../objects/other_imp.h" +#include "../objects/other_type.h" +#include "../objects/point_type.h" +#include "../objects/tests_type.h" +#include "../objects/transform_types.h" +#include "../objects/vector_type.h" +#include "../objects/polygon_type.h" + +#include <klocale.h> + +void setupBuiltinStuff() +{ + static bool done = false; + if ( ! done ) + { + ObjectConstructorList* ctors = ObjectConstructorList::instance(); + GUIActionList* actions = GUIActionList::instance(); + ObjectConstructor* c = 0; + + // segment... + c = new SimpleObjectTypeConstructor( + SegmentABType::instance(), I18N_NOOP( "Segment" ), + I18N_NOOP( "A segment constructed from its start and end point" ), + "segment" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_segment", Qt::Key_S ) ); + + // line by two points.. + c = new SimpleObjectTypeConstructor( + LineABType::instance(), I18N_NOOP( "Line by Two Points" ), + I18N_NOOP( "A line constructed through two points"), "line" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_linettp", Qt::Key_L ) ); + + // ray by two points.. + c = new SimpleObjectTypeConstructor( + RayABType::instance(), I18N_NOOP( "Half-Line" ), + I18N_NOOP( "A half-line by its start point, and another point somewhere on it." ), + "ray" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_ray", Qt::Key_R ) ); + + // perpendicular line + c = new SimpleObjectTypeConstructor( + LinePerpendLPType::instance(), I18N_NOOP( "Perpendicular" ), + I18N_NOOP( "A line constructed through a point, perpendicular to another line or segment." ), + "perpendicular" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_lineperpend" ) ); + + // parallel line + c = new SimpleObjectTypeConstructor( + LineParallelLPType::instance(), I18N_NOOP( "Parallel" ), + I18N_NOOP( "A line constructed through a point, and parallel to another line or segment" ), + "parallel" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_lineparallel" ) ); + + // circle + c = new SimpleObjectTypeConstructor( + CircleBCPType::instance(), I18N_NOOP( "Circle by Center && Point" ), + I18N_NOOP( "A circle constructed by its center and a point that pertains to it" ), + "circlebcp" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_circlebcp", Qt::Key_C ) ); + + c = new SimpleObjectTypeConstructor( + CircleBTPType::instance(), I18N_NOOP( "Circle by Three Points" ), + I18N_NOOP( "A circle constructed through three points" ), + "circlebtp" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_circlebtp" ) ); + + // declare this object static to this function, so it gets deleted + // at the end of the program, without us having to wonder about + // deleting it.. We don't want to register this + // object-constructor, because that way, "construct the bisector" + // would appear twice in the angle popup menu: once as the generic + // construct a property stuff, and once because of this ctor.. + // we only register the guiaction, cause it makes sense to have a + // toolbar icon for this.. + static PropertyObjectConstructor anglebisectionctor( + AngleImp::stype(), + I18N_NOOP( "Construct Bisector of This Angle" ), + I18N_NOOP( "Select the angle you want to construct the bisector of..." ), + I18N_NOOP( "Angle Bisector" ), + I18N_NOOP( "The bisector of an angle" ), + "angle_bisector", + "angle-bisector" ); + actions->add( new ConstructibleAction( &anglebisectionctor, "objects_new_angle_bisector" ) ); + + // conic stuff + c = new SimpleObjectTypeConstructor( + ConicB5PType::instance(), I18N_NOOP( "Conic by Five Points" ), + I18N_NOOP( "A conic constructed through five points" ), + "conicb5p" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_conicb5p" ) ); + + c = new SimpleObjectTypeConstructor( + ConicBAAPType::instance(), + I18N_NOOP( "Hyperbola by Asymptotes && Point" ), + I18N_NOOP( "A hyperbola with given asymptotes through a point" ), + "conicbaap" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_conicbaap" ) ); + + c = new SimpleObjectTypeConstructor( + EllipseBFFPType::instance(), + I18N_NOOP( "Ellipse by Focuses && Point" ), // focuses is used in preference to foci + I18N_NOOP( "An ellipse constructed by its focuses and a point that pertains to it" ), + "ellipsebffp" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_ellipsebffp" ) ); + + c = new SimpleObjectTypeConstructor( + HyperbolaBFFPType::instance(), + I18N_NOOP( "Hyperbola by Focuses && Point" ), // focuses is used in preference to foci + I18N_NOOP( "A hyperbola constructed by its focuses and a point that pertains to it" ), + "hyperbolabffp" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_hyperbolabffp" ) ); + + c = new SimpleObjectTypeConstructor( + ConicBDFPType::instance(), + I18N_NOOP( "Conic by Directrix, Focus && Point" ), + I18N_NOOP( "A conic with given directrix and focus, through a point" ), + "conicbdfp" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_conicbdfp" ) ); + + c = new SimpleObjectTypeConstructor( + ParabolaBTPType::instance(), + I18N_NOOP( "Vertical Parabola by Three Points" ), + I18N_NOOP( "A vertical parabola constructed through three points" ), + "parabolabtp" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_parabolabtp" ) ); + + c = new SimpleObjectTypeConstructor( + CubicB9PType::instance(), + I18N_NOOP( "Cubic Curve by Nine Points" ), + I18N_NOOP( "A cubic curve constructed through nine points" ), + "cubicb9p" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_cubicb9p" ) ); + + c = new SimpleObjectTypeConstructor( + ConicPolarPointType::instance(), + I18N_NOOP( "Polar Point of a Line" ), + I18N_NOOP( "The polar point of a line with respect to a conic." ), + "polarpoint" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_pointpolar" ) ); + + c = new SimpleObjectTypeConstructor( + ConicPolarLineType::instance(), + I18N_NOOP( "Polar Line of a Point" ), + I18N_NOOP( "The polar line of a point with respect to a conic." ), + "polarline" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_linepolar" ) ); + + c = new SimpleObjectTypeConstructor( + CubicNodeB6PType::instance(), + I18N_NOOP( "Cubic Curve with Node by Six Points" ), + I18N_NOOP( "A cubic curve with a nodal point at the origin through six points" ), + "cubicnodeb6p" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_cubicnodeb6p" ) ); + + c = new SimpleObjectTypeConstructor( + CubicCuspB4PType::instance(), + I18N_NOOP( "Cubic Curve with Cusp by Four Points" ), + I18N_NOOP( "A cubic curve with a horizontal cusp at the origin through four points" ), + "cubiccuspb4p" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_cubiccuspb4p" ) ); + + c = new SimpleObjectTypeConstructor( + ConicDirectrixType::instance(), + I18N_NOOP( "Directrix of a Conic" ), + I18N_NOOP( "The directrix line of a conic." ), + "directrix" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_linedirectrix" ) ); + + c = new SimpleObjectTypeConstructor( + AngleType::instance(), + I18N_NOOP( "Angle by Three Points" ), + I18N_NOOP( "An angle defined by three points" ), + "angle" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_angle", Qt::Key_A ) ); + + c = new SimpleObjectTypeConstructor( + EquilateralHyperbolaB4PType::instance(), + I18N_NOOP( "Equilateral Hyperbola by Four Points" ), + I18N_NOOP( "An equilateral hyperbola constructed through four points" ), + "equilateralhyperbolab4p" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_equilateralhyperbolab4p" ) ); + + { + // now for the Mid Point action. It does both the mid point of + // a segment, and the mid point of two points. The midpoint of + // two segments just shows the mid point property, and therefore + // doesn't need to be added to the ctors, because there are + // already facilities to construct an object's properties.. + // therefore, we add only an mpotp to the ctors, and add the + // merged constructor only to the actions.. + ctors->add( new MidPointOfTwoPointsConstructor() ); + + ObjectConstructor* mpotp = new MidPointOfTwoPointsConstructor(); + ObjectConstructor* mpos = new PropertyObjectConstructor( + SegmentImp::stype(), I18N_NOOP( "Construct the midpoint of this segment" ), + "", "", "", "", "mid-point" ); + + // make this a static object, so it gets deleted at the end of + // the program. + static MergeObjectConstructor m( + I18N_NOOP( "Mid Point" ), + I18N_NOOP( "The midpoint of a segment or two other points" ), + "bisection" ); + m.merge( mpotp ); + m.merge( mpos ); + actions->add( new ConstructibleAction( &m, "objects_new_midpoint", Qt::Key_M ) ); + }; + + c = new SimpleObjectTypeConstructor( + VectorType::instance(), + I18N_NOOP( "Vector" ), + I18N_NOOP( "Construct a vector from two given points." ), + "vector" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_vector", Qt::Key_V ) ); + + c = new SimpleObjectTypeConstructor( + VectorSumType::instance(), + I18N_NOOP( "Vector Sum" ), + I18N_NOOP( "Construct the vector sum of two vectors." ), + "vectorsum" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_vectorsum", 0 ) ); + + c = new SimpleObjectTypeConstructor( + LineByVectorType::instance(), + I18N_NOOP( "Line by Vector" ), + I18N_NOOP( "Construct the line by a given vector though a given point." ), + "linebyvector" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_linebyvector", 0 ) ); + + c = new SimpleObjectTypeConstructor( + HalflineByVectorType::instance(), + I18N_NOOP( "Half-Line by Vector" ), + I18N_NOOP( "Construct the half-line by a given vector starting at given point." ), + "halflinebyvector" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_halflinebyvector", 0 ) ); + + c = new SimpleObjectTypeConstructor( + ArcBTPType::instance(), + I18N_NOOP( "Arc by Three Points" ), + I18N_NOOP( "Construct an arc through three points." ), + "arc" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_arcbtp" ) ); + + c = new SimpleObjectTypeConstructor( + ArcBCPAType::instance(), + I18N_NOOP( "Arc by Center, Angle && Point" ), + I18N_NOOP( "Construct an arc by its center and a given angle, " + "starting at a given point" ), + "arcbcpa" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_arcbcpa" ) ); + + c = new SimpleObjectTypeConstructor( + ParabolaBDPType::instance(), + I18N_NOOP( "Parabola by Directrix && Focus" ), + I18N_NOOP( "A parabola defined by its directrix and focus" ), + "parabolabdp" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_parabolabdp" ) ); + + // Transformation stuff.. + c = new InversionConstructor(); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_inversion" ) ); + + c = new SimpleObjectTypeConstructor( + TranslatedType::instance(), + I18N_NOOP( "Translate" ), + I18N_NOOP( "The translation of an object by a vector" ), + "translation" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_translation" ) ); + + c = new SimpleObjectTypeConstructor( + PointReflectionType::instance(), + I18N_NOOP( "Reflect in Point" ), + I18N_NOOP( "An object reflected in a point" ), + "centralsymmetry" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_pointreflection" ) ); + + c = new SimpleObjectTypeConstructor( + LineReflectionType::instance(), + I18N_NOOP( "Reflect in Line" ), + I18N_NOOP( "An object reflected in a line" ), + "mirrorpoint" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_linereflection" ) ); + + c = new SimpleObjectTypeConstructor( + RotationType::instance(), + I18N_NOOP( "Rotate" ), + I18N_NOOP( "An object rotated by an angle around a point" ), + "rotation" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_rotation" ) ); + + c = new SimpleObjectTypeConstructor( + ScalingOverCenterType::instance(), + I18N_NOOP( "Scale" ), + I18N_NOOP( "Scale an object over a point, by the ratio given by the length of a segment" ), + "scale" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_scalingovercenter" ) ); + + c = new SimpleObjectTypeConstructor( + ScalingOverLineType::instance(), + I18N_NOOP( "Scale over Line" ), + I18N_NOOP( "An object scaled over a line, by the ratio given by the length of a segment" ), + "stretch" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_scalingoverline" ) ); + + c = new SimpleObjectTypeConstructor( + ScalingOverCenter2Type::instance(), + I18N_NOOP( "Scale (ratio given by two segments)" ), + I18N_NOOP( "Scale an object over a point, by the ratio given by the length of two segments" ), + "scale" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_scalingovercenter2" ) ); + + c = new SimpleObjectTypeConstructor( + ScalingOverLine2Type::instance(), + I18N_NOOP( "Scale over Line (ratio given by two segments)" ), + I18N_NOOP( "An object scaled over a line, by the ratio given by the length of two segments" ), + "stretch" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_scalingoverline2" ) ); + + c = new SimpleObjectTypeConstructor( + SimilitudeType::instance(), + I18N_NOOP( "Apply Similitude" ), + I18N_NOOP( "Apply a similitude to an object ( the sequence of a scaling and rotation around a center )" ), + "similitude" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_similitude" ) ); + + c = new SimpleObjectTypeConstructor( + HarmonicHomologyType::instance(), + I18N_NOOP( "Harmonic Homology" ), + I18N_NOOP( "The harmonic homology with a given center and a given axis (this is a projective transformation)" ), + "harmonichomology" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_harmonichomology" ) ); + + c = new GenericAffinityConstructor(); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_genericaffinity" ) ); + + c = new GenericProjectivityConstructor(); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_genericprojectivity" ) ); + + c = new SimpleObjectTypeConstructor( + CastShadowType::instance(), + I18N_NOOP( "Draw Projective Shadow" ), + I18N_NOOP( "The shadow of an object with a given light source and projection plane (indicated by a line)" ), + "castshadow" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_castshadow" ) ); + +// c = new SimpleObjectTypeConstructor( +// ProjectiveRotationType::instance(), +// I18N_NOOP( "Rotate Projectively" ), +// I18N_NOOP( "An object projectively rotated by an angle and a half-line" ), +// "projectiverotation" ); +// ctors->add( c ); +// actions->add( new ConstructibleAction( c, "objects_new_projectiverotation" ) ); + + c = new MultiObjectTypeConstructor( + ConicAsymptoteType::instance(), + I18N_NOOP( "Asymptotes of a Hyperbola" ), + I18N_NOOP( "The two asymptotes of a hyperbola." ), + "conicasymptotes", -1, 1 ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_lineconicasymptotes" ) ); + + c = new ConicRadicalConstructor(); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_lineconicradical") ); + + /* ----------- start polygons --------- */ + + c = new SimpleObjectTypeConstructor( + TriangleB3PType::instance(), + I18N_NOOP( "Triangle by Its Vertices" ), + I18N_NOOP( "Construct a triangle given its three vertices." ), + "triangle" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_trianglebtp" ) ); + + c = new PolygonBNPTypeConstructor(); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_polygonbnp" )); + + c = new PolygonBCVConstructor(); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_polygonbcv" ) ); + + c = new PolygonVertexTypeConstructor(); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_polygonvertices" )); + + c = new PolygonSideTypeConstructor(); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_polygonsides" )); + + c = new SimpleObjectTypeConstructor( + ConvexHullType::instance(), I18N_NOOP( "Convex Hull" ), + I18N_NOOP( "A polygon that corresponds to the convex hull of another polygon" ), + "convexhull" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_convexhull" ) ); + + /* ----------- end polygons --------- */ + + c = new LocusConstructor(); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_locus" ) ); + + // tests + c = new TestConstructor( + AreParallelType::instance(), + I18N_NOOP( "Parallel Test" ), + I18N_NOOP( "Test whether two given lines are parallel" ), + "testparallel" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_areparallel" ) ); + + c = new TestConstructor( + AreOrthogonalType::instance(), + I18N_NOOP( "Orthogonal Test" ), + I18N_NOOP( "Test whether two given lines are orthogonal" ), + "testorthogonal" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_areorthogonal" ) ); + + c = new TestConstructor( + AreCollinearType::instance(), + I18N_NOOP( "Collinear Test" ), + I18N_NOOP( "Test whether three given points are collinear" ), + "testcollinear" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_arecollinear" ) ); + + c = new TestConstructor( + ContainsTestType::instance(), + I18N_NOOP( "Contains Test" ), + I18N_NOOP( "Test whether a given curve contains a given point" ), + "testcontains" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_containstest" ) ); + + c = new TestConstructor( + InPolygonTestType::instance(), + I18N_NOOP( "In Polygon Test" ), + I18N_NOOP( "Test whether a given polygon contains a given point" ), + "test" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_inpolygontest" ) ); + + c = new TestConstructor( + ConvexPolygonTestType::instance(), + I18N_NOOP( "Convex Polygon Test" ), + I18N_NOOP( "Test whether a given polygon is convex" ), + "test" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_convexpolygontest" ) ); + + c = new TestConstructor( + SameDistanceType::instance(), + I18N_NOOP( "Distance Test" ), + I18N_NOOP( "Test whether a given point have the same distance from a given point " + "and from another given point" ), + "testdistance" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_distancetest" ) ); + + c = new TestConstructor( + VectorEqualityTestType::instance(), + I18N_NOOP( "Vector Equality Test" ), + I18N_NOOP( "Test whether two vectors are equal" ), + "test" ); +// "testequal" ); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_vectorequalitytest" ) ); + + c = new MeasureTransportConstructor(); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_measuretransport" )); + +// c = new SimpleObjectTypeConstructor( +// MeasureTransportType::instance(), +// I18N_NOOP( "Measure Transport" ), +// I18N_NOOP( "Transport the measure of a segment or arc over a line or circle." ), +// "measuretransport" ); +// ctors->add( c ); +// actions->add( new ConstructibleAction( c, "objects_new_measuretransport" ) ); + + // the generic intersection constructor.. + c = new GenericIntersectionConstructor(); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_intersection", Qt::Key_I ) ); + + // the generic tangent constructor + c = new TangentConstructor(); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_tangent", Qt::Key_T ) ); + + // the generic center of curvature constructor + c = new CocConstructor(); + ctors->add( c ); + actions->add( new ConstructibleAction( c, "objects_new_centerofcurvature" ) ); + + actions->add( new ConstructPointAction( "objects_new_normalpoint" ) ); + actions->add( new ConstructTextLabelAction( "objects_new_textlabel" ) ); + actions->add( new AddFixedPointAction( "objects_new_point_xy" ) ); + +#ifdef KIG_ENABLE_PYTHON_SCRIPTING +#include "../scripting/script-common.h" + actions->add( new NewScriptAction( + I18N_NOOP( "Python Script" ), + I18N_NOOP( "Construct a new Python script." ), + "objects_new_script_python", + ScriptType::Python ) ); +#endif + +#if 0 + actions->add( new TestAction( "test_stuff" ) ); +#endif + }; + + done = true; +} diff --git a/kig/misc/builtin_stuff.h b/kig/misc/builtin_stuff.h new file mode 100644 index 00000000..198886fe --- /dev/null +++ b/kig/misc/builtin_stuff.h @@ -0,0 +1,23 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_MISC_BUILTIN_STUFF_H +#define KIG_MISC_BUILTIN_STUFF_H + +void setupBuiltinStuff(); + +#endif diff --git a/kig/misc/calcpaths.cc b/kig/misc/calcpaths.cc new file mode 100644 index 00000000..1532715b --- /dev/null +++ b/kig/misc/calcpaths.cc @@ -0,0 +1,303 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "calcpaths.h" + +#include "../objects/object_calcer.h" +#include "../objects/object_imp.h" + +#include <algorithm> + +// mp: +// The previous algorithm by Dominique had an exponential complexity +// for some constructions (e.g. a sequence of "n" triangles each inscribed +// into the previous). +// The new version is directly taken from a book of Alan Bertossi +// "Algoritmi e strutture dati" + +// temporarily disabling the new algorithm due to the freeze: +// I previously misunderstood the semantics of this function +// and thought that the os vector had to be completed with all +// the subtree generated by it. On the contrary, the os vector +// contains *all* the objects that we want, we only have to +// reorder them. Now it *should* work, however we postpone +// activating this to a more proper moment + +// to deactivate the new algorithm change "define" into "undef" + +#define NEWCALCPATH +#ifdef NEWCALCPATH +void localdfs( ObjectCalcer* obj, + std::vector<ObjectCalcer*>& visited, + std::vector<ObjectCalcer*>& all); + +std::vector<ObjectCalcer*> calcPath( const std::vector<ObjectCalcer*>& os ) +{ + // "all" is the Objects var we're building, in reverse ordering + std::vector<ObjectCalcer*> visited; + std::vector<ObjectCalcer*> all; + + for ( std::vector<ObjectCalcer*>::const_iterator i = os.begin(); i != os.end(); ++i ) + { + if ( std::find( visited.begin(), visited.end(), *i ) == visited.end() ) + { + localdfs( *i, visited, all ); + } + } + + // now, we need to remove all objects that are not in os + // (forgot to do this in previous fix :-( ) + std::vector<ObjectCalcer*> ret; + for ( std::vector<ObjectCalcer*>::reverse_iterator i = all.rbegin(); i != all.rend(); ++i ) + { + // we only add objects that appear in os + if ( std::find( os.begin(), os.end(), *i ) != os.end() ) ret.push_back( *i ); + }; + return ret; +} + +void localdfs( ObjectCalcer* obj, + std::vector<ObjectCalcer*>& visited, + std::vector<ObjectCalcer*>& all) +{ + visited.push_back( obj ); + const std::vector<ObjectCalcer*> o = obj->children(); + for ( std::vector<ObjectCalcer*>::const_iterator i = o.begin(); i != o.end(); ++i ) + { + if ( std::find( visited.begin(), visited.end(), *i ) == visited.end() ) + localdfs( *i, visited, all ); + } + all.push_back( obj ); +} + +// old calcPath commented out... + +#else +// these first two functions were written before i read stuff about +// graph theory and algorithms, so i'm sure they're far from optimal. +// However, they seem to work fine, and i don't think there's a real +// need for optimisation here.. +std::vector<ObjectCalcer*> calcPath( const std::vector<ObjectCalcer*>& os ) +{ + // this is a little experiment of mine, i don't know if it is the + // fastest way to do it, but it seems logical to me... + + // the general idea here: + // first we build a new Objects variable. For every object in os, + // we put all of its children at the end of it, and we do the same + // for the ones we add.. + + // "all" is the Objects var we're building... + std::vector<ObjectCalcer*> all = os; + // tmp is the var containing the objects we're iterating over. The + // first time around this is the os variable, the next time, this + // contains the variables we added in the first round... + std::vector<ObjectCalcer*> tmp = os; + // tmp2 is a temporary var. During a round, it receives all the + // variables we add ( to "all" ) in that round, and at the end of + // the round, it is assigned to tmp. + std::vector<ObjectCalcer*> tmp2; + while ( ! tmp.empty() ) + { + for ( std::vector<ObjectCalcer*>::const_iterator i = tmp.begin(); i != tmp.end(); ++i ) + { + const std::vector<ObjectCalcer*> o = (*i)->children(); + std::copy( o.begin(), o.end(), std::back_inserter( all ) ); + std::copy( o.begin(), o.end(), std::back_inserter( tmp2 ) ); + }; + tmp = tmp2; + tmp2.clear(); + }; + + // now we know that if all objects appear at least once after all of + // their parents. So, we take all, and of every object, we remove + // every reference except the last one... + std::vector<ObjectCalcer*> ret; + ret.reserve( os.size() ); + for ( std::vector<ObjectCalcer*>::reverse_iterator i = all.rbegin(); i != all.rend(); ++i ) + { + // we only add objects that appear in os and only if they are not + // already in ret.. + if ( std::find( ret.begin(), ret.end(), *i ) == ret.end() && + std::find( os.begin(), os.end(), *i ) != os.end() ) ret.push_back( *i ); + }; + std::reverse( ret.begin(), ret.end() ); + return ret; +} +#endif + +bool addBranch( const std::vector<ObjectCalcer*>& o, const ObjectCalcer* to, std::vector<ObjectCalcer*>& ret ) +{ + bool rb = false; + for ( std::vector<ObjectCalcer*>::const_iterator i = o.begin(); i != o.end(); ++i ) + { + if ( *i == to ) + rb = true; + else + if ( addBranch( (*i)->children(), to, ret ) ) + { + rb = true; + ret.push_back( *i ); + }; + }; + return rb; +} + +std::vector<ObjectCalcer*> calcPath( const std::vector<ObjectCalcer*>& from, const ObjectCalcer* to ) +{ + std::vector<ObjectCalcer*> all; + + for ( std::vector<ObjectCalcer*>::const_iterator i = from.begin(); i != from.end(); ++i ) + { + (void) addBranch( (*i)->children(), to, all ); + }; + + std::vector<ObjectCalcer*> ret; + for ( std::vector<ObjectCalcer*>::iterator i = all.begin(); i != all.end(); ++i ) + { + if ( std::find( ret.begin(), ret.end(), *i ) == ret.end() ) + ret.push_back( *i ); + }; + return std::vector<ObjectCalcer*>( ret.rbegin(), ret.rend() ); +} + +static void addNonCache( ObjectCalcer* o, std::vector<ObjectCalcer*>& ret ) +{ + if ( ! o->imp()->isCache() ) + if ( std::find( ret.begin(), ret.end(), o ) == ret.end() ) + ret.push_back( o ); + else + { + std::vector<ObjectCalcer*> parents = o->parents(); + for ( uint i = 0; i < parents.size(); ++i ) + addNonCache( parents[i], ret ); + }; +} + +static bool visit( const ObjectCalcer* o, const std::vector<ObjectCalcer*>& from, std::vector<ObjectCalcer*>& ret ) +{ + // this function returns true if the visited object depends on one + // of the objects in from. If we encounter objects that are on the + // side of the tree path ( they do not depend on from themselves, + // but their direct children do ), then we add them to ret. + if ( std::find( from.begin(), from.end(), o ) != from.end() ) return true; + + std::vector<bool> deps( o->parents().size(), false ); + bool somedepend = false; + bool alldepend = true; + std::vector<ObjectCalcer*> parents = o->parents(); + for ( uint i = 0; i < parents.size(); ++i ) + { + bool v = visit( parents[i], from, ret ); + somedepend |= v; + alldepend &= v; + deps[i] = v; + }; + if ( somedepend && ! alldepend ) + { + for ( uint i = 0; i < deps.size(); ++i ) + if ( ! deps[i] ) + addNonCache( parents[i], ret ); + }; + + return somedepend; +} + +std::vector<ObjectCalcer*> sideOfTreePath( const std::vector<ObjectCalcer*>& from, const ObjectCalcer* to ) +{ + std::vector<ObjectCalcer*> ret; + visit( to, from, ret ); + return ret; +} + +std::vector<ObjectCalcer*> getAllParents( const std::vector<ObjectCalcer*>& objs ) +{ + using namespace std; + std::set<ObjectCalcer*> ret( objs.begin(),objs.end() ); + std::set<ObjectCalcer*> cur = ret; + while ( ! cur.empty() ) + { + std::set<ObjectCalcer*> next; + for ( std::set<ObjectCalcer*>::const_iterator i = cur.begin(); i != cur.end(); ++i ) + { + std::vector<ObjectCalcer*> parents = (*i)->parents(); + next.insert( parents.begin(), parents.end() ); + }; + + ret.insert( next.begin(), next.end() ); + cur = next; + }; + return std::vector<ObjectCalcer*>( ret.begin(), ret.end() ); +} + +std::vector<ObjectCalcer*> getAllParents( ObjectCalcer* obj ) +{ + std::vector<ObjectCalcer*> objs; + objs.push_back( obj ); + return getAllParents( objs ); +} + +bool isChild( const ObjectCalcer* o, const std::vector<ObjectCalcer*>& os ) +{ + std::vector<ObjectCalcer*> parents = o->parents(); + std::set<ObjectCalcer*> cur( parents.begin(), parents.end() ); + while ( ! cur.empty() ) + { + std::set<ObjectCalcer*> next; + for ( std::set<ObjectCalcer*>::const_iterator i = cur.begin(); i != cur.end(); ++i ) + { + if ( std::find( os.begin(), os.end(), *i ) != os.end() ) return true; + std::vector<ObjectCalcer*> parents = (*i)->parents(); + next.insert( parents.begin(), parents.end() ); + }; + cur = next; + }; + return false; +} + +std::set<ObjectCalcer*> getAllChildren( ObjectCalcer* obj ) +{ + std::vector<ObjectCalcer*> objs; + objs.push_back( obj ); + return getAllChildren( objs ); +} + +std::set<ObjectCalcer*> getAllChildren( const std::vector<ObjectCalcer*> objs ) +{ + std::set<ObjectCalcer*> ret; + // objects to iterate over... + std::set<ObjectCalcer*> cur( objs.begin(), objs.end() ); + while( !cur.empty() ) + { + // contains the objects to iterate over the next time around... + std::set<ObjectCalcer*> next; + for( std::set<ObjectCalcer*>::iterator i = cur.begin(); + i != cur.end(); ++i ) + { + ret.insert( *i ); + std::vector<ObjectCalcer*> children = (*i)->children(); + next.insert( children.begin(), children.end() ); + }; + cur = next; + }; + return ret; +} + +bool isPointOnCurve( const ObjectCalcer* point, const ObjectCalcer* curve ) +{ + return point->isDefinedOnOrThrough( curve ) || curve->isDefinedOnOrThrough( point ); +} diff --git a/kig/misc/calcpaths.h b/kig/misc/calcpaths.h new file mode 100644 index 00000000..620558a3 --- /dev/null +++ b/kig/misc/calcpaths.h @@ -0,0 +1,88 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_MISC_CALCPATHS_H +#define KIG_MISC_CALCPATHS_H + +#include "../objects/common.h" + +/** + * This function sorts \p os such that they're in the right order for + * calc()-ing. This means that child objects must appear after their + * parents ( for you graph people, this is just a topological sort.. ) + */ +std::vector<ObjectCalcer*> calcPath( const std::vector<ObjectCalcer*>& os ); + +/** + * This is a different function for more or less the same purpose. It + * takes a few Objects, which are considered to have been calced + * already. Then, it puts the necessary part of their children in the + * right order, so that calc()-ing correctly updates all of their data + * ( they're calc'ed in the right order, i mean... ). The objects in + * from are normally not included in the output, unless they appear + * somewhere in the middle of the calc-path towards to... + */ +std::vector<ObjectCalcer*> calcPath( const std::vector<ObjectCalcer*>& from, const ObjectCalcer* to ); + +/** + * This function returns all objects on the side of the path through + * the dependency tree from from down to to. This means that we look + * for any objects that don't depend on any of the objects in from + * themselves, but of which one of the direct children does. We need + * this function for Locus stuff... + */ +std::vector<ObjectCalcer*> sideOfTreePath( const std::vector<ObjectCalcer*>& from, const ObjectCalcer* to ); + +/** + * This function returns all objects above the \p given in the + * dependency graph. The \p given objects are also included + * themselves.. + */ +std::vector<ObjectCalcer*> getAllParents( const std::vector<ObjectCalcer*>& objs ); +/** + * \overload + */ +std::vector<ObjectCalcer*> getAllParents( ObjectCalcer* obj ); + +/** + * This function returns all objects below the objects in \p objs in the + * dependency graphy. The objects in \p objs are also included + * themselves.. + */ +std::set<ObjectCalcer*> getAllChildren( const std::vector<ObjectCalcer*> objs ); + +/** + * \overload + */ +std::set<ObjectCalcer*> getAllChildren( ObjectCalcer* obj ); + +/** + * Returns true if \p o is a descendant of any of the objects in \p os.. + */ +bool isChild( const ObjectCalcer* o, const std::vector<ObjectCalcer*>& os ); + +/** + * Return true if the given \p point is ( by construction ) on the given + * \p curve. This means that it is either a constrained point on the + * curve, or the curve is constructed through the point, or the point + * is an intersection point of the curve with another curve. + * Note that it is assumed that the given point is in fact a point and the + * given curve is in fact a curve. + */ +bool isPointOnCurve( const ObjectCalcer* point, const ObjectCalcer* curve ); + +#endif diff --git a/kig/misc/common.cpp b/kig/misc/common.cpp new file mode 100644 index 00000000..fccd384f --- /dev/null +++ b/kig/misc/common.cpp @@ -0,0 +1,520 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#include "common.h" + +#include "../kig/kig_view.h" +#include "../objects/object_imp.h" + +#include <cmath> + +#include <kdebug.h> +#include <knumvalidator.h> +#include <klocale.h> +#if KDE_IS_VERSION( 3, 1, 90 ) +#include <kinputdialog.h> +#else +#include <klineeditdlg.h> +#endif + +Coordinate calcPointOnPerpend( const LineData& l, const Coordinate& t ) +{ + return calcPointOnPerpend( l.b - l.a, t ); +} + +Coordinate calcPointOnPerpend( const Coordinate& dir, const Coordinate& t ) +{ + return t + ( dir ).orthogonal(); +} + +Coordinate calcPointOnParallel( const LineData& l, const Coordinate& t ) +{ + return calcPointOnParallel( l.b - l.a, t ); +} + +Coordinate calcPointOnParallel( const Coordinate& dir, const Coordinate& t ) +{ + return t + dir*5; +} + +Coordinate calcIntersectionPoint( const LineData& l1, const LineData& l2 ) +{ + const Coordinate& pa = l1.a; + const Coordinate& pb = l1.b; + const Coordinate& pc = l2.a; + const Coordinate& pd = l2.b; + + double + xab = pb.x - pa.x, + xdc = pd.x - pc.x, + xac = pc.x - pa.x, + yab = pb.y - pa.y, + ydc = pd.y - pc.y, + yac = pc.y - pa.y; + + double det = xab*ydc - xdc*yab; + double detn = xac*ydc - xdc*yac; + + // test for parallelism + if ( fabs (det) < 1e-6 ) return Coordinate::invalidCoord(); + double t = detn/det; + + return pa + t*(pb - pa); +} + +void calcBorderPoints( Coordinate& p1, Coordinate& p2, const Rect& r ) +{ + calcBorderPoints( p1.x, p1.y, p2.x, p2.y, r ); +} + +const LineData calcBorderPoints( const LineData& l, const Rect& r ) +{ + LineData ret( l ); + calcBorderPoints( ret.a.x, ret.a.y, ret.b.x, ret.b.y, r ); + return ret; +} + +void calcBorderPoints( double& xa, double& ya, double& xb, double& yb, const Rect& r ) +{ + // we calc where the line through a(xa,ya) and b(xb,yb) intersects with r: + double left = (r.left()-xa)*(yb-ya)/(xb-xa)+ya; + double right = (r.right()-xa)*(yb-ya)/(xb-xa)+ya; + double top = (r.top()-ya)*(xb-xa)/(yb-ya)+xa; + double bottom = (r.bottom()-ya)*(xb-xa)/(yb-ya)+xa; + + // now we go looking for valid points + int novp = 0; // number of valid points we have already found + + if (!(top < r.left() || top > r.right())) { + // the line intersects with the top side of the rect. + ++novp; + xa = top; ya = r.top(); + }; + if (!(left < r.bottom() || left > r.top())) { + // the line intersects with the left side of the rect. + if (novp++) { xb = r.left(); yb=left; } + else { xa = r.left(); ya=left; }; + }; + if (!(right < r.bottom() || right > r.top())) { + // the line intersects with the right side of the rect. + if (novp++) { xb = r.right(); yb=right; } + else { xa = r.right(); ya=right; }; + }; + if (!(bottom < r.left() || bottom > r.right())) { + // the line intersects with the bottom side of the rect. + ++novp; + xb = bottom; yb = r.bottom(); + }; + if (novp < 2) { + // line is completely outside of the window... + xa = ya = xb = yb = 0; + }; +} + +void calcRayBorderPoints( const Coordinate& a, Coordinate& b, const Rect& r ) +{ + calcRayBorderPoints( a.x, a.y, b.x, b.y, r ); +} + +void calcRayBorderPoints( const double xa, const double ya, double& xb, + double& yb, const Rect& r ) +{ + // we calc where the line through a(xa,ya) and b(xb,yb) intersects with r: + double left = (r.left()-xa)*(yb-ya)/(xb-xa)+ya; + double right = (r.right()-xa)*(yb-ya)/(xb-xa)+ya; + double top = (r.top()-ya)*(xb-xa)/(yb-ya)+xa; + double bottom = (r.bottom()-ya)*(xb-xa)/(yb-ya)+xa; + + // now we see which we can use... + if( + // the ray intersects with the top side of the rect.. + top >= r.left() && top <= r.right() + // and b is above a + && yb > ya ) + { + xb = top; + yb = r.top(); + return; + }; + if( + // the ray intersects with the left side of the rect... + left >= r.bottom() && left <= r.top() + // and b is on the left of a.. + && xb < xa ) + { + xb = r.left(); + yb=left; + return; + }; + if ( + // the ray intersects with the right side of the rect... + right >= r.bottom() && right <= r.top() + // and b is to the right of a.. + && xb > xa ) + { + xb = r.right(); + yb=right; + return; + }; + if( + // the ray intersects with the bottom side of the rect... + bottom >= r.left() && bottom <= r.right() + // and b is under a.. + && yb < ya ) { + xb = bottom; + yb = r.bottom(); + return; + }; + kdError() << k_funcinfo << "damn" << endl; +} + +bool isOnLine( const Coordinate& o, const Coordinate& a, + const Coordinate& b, const double fault ) +{ + double x1 = a.x; + double y1 = a.y; + double x2 = b.x; + double y2 = b.y; + + // check your math theory ( homogeneous coördinates ) for this + double tmp = fabs( o.x * (y1-y2) + o.y*(x2-x1) + (x1*y2-y1*x2) ); + return tmp < ( fault * (b-a).length()); + // if o is on the line ( if the determinant of the matrix + // |---|---|---| + // | x | y | z | + // |---|---|---| + // | x1| y1| z1| + // |---|---|---| + // | x2| y2| z2| + // |---|---|---| + // equals 0, then p(x,y,z) is on the line containing points + // p1(x1,y1,z1) and p2 here, we're working with normal coords, no + // homogeneous ones, so all z's equal 1 +} + +bool isOnSegment( const Coordinate& o, const Coordinate& a, + const Coordinate& b, const double fault ) +{ + return isOnLine( o, a, b, fault ) + // not too far to the right + && (o.x - kigMax(a.x,b.x) < fault ) + // not too far to the left + && ( kigMin (a.x, b.x) - o.x < fault ) + // not too high + && ( kigMin (a.y, b.y) - o.y < fault ) + // not too low + && ( o.y - kigMax (a.y, b.y) < fault ); +} + +bool isOnRay( const Coordinate& o, const Coordinate& a, + const Coordinate& b, const double fault ) +{ + return isOnLine( o, a, b, fault ) + // not too far in front of a horizontally.. +// && ( a.x - b.x < fault ) == ( a.x - o.x < fault ) + && ( ( a.x < b.x ) ? ( a.x - o.x < fault ) : ( a.x - o.x > -fault ) ) + // not too far in front of a vertically.. +// && ( a.y - b.y < fault ) == ( a.y - o.y < fault ); + && ( ( a.y < b.y ) ? ( a.y - o.y < fault ) : ( a.y - o.y > -fault ) ); +} + +bool isOnArc( const Coordinate& o, const Coordinate& c, const double r, + const double sa, const double a, const double fault ) +{ + if ( fabs( ( c - o ).length() - r ) > fault ) + return false; + Coordinate d = o - c; + double angle = atan2( d.y, d.x ); + + if ( angle < sa ) angle += 2 * M_PI; + return angle - sa - a < 1e-4; +} + +const Coordinate calcMirrorPoint( const LineData& l, + const Coordinate& p ) +{ + Coordinate m = + calcIntersectionPoint( l, + LineData( p, + calcPointOnPerpend( l, p ) + ) + ); + return 2*m - p; +} + +const Coordinate calcCircleLineIntersect( const Coordinate& c, + const double sqr, + const LineData& l, + int side ) +{ + Coordinate proj = calcPointProjection( c, l ); + Coordinate hvec = proj - c; + Coordinate lvec = -l.dir(); + + double sqdist = hvec.squareLength(); + double sql = sqr - sqdist; + if ( sql < 0.0 ) + return Coordinate::invalidCoord(); + else + { + double l = sqrt( sql ); + lvec = lvec.normalize( l ); + lvec *= side; + + return proj + lvec; + }; +} + +const Coordinate calcArcLineIntersect( const Coordinate& c, const double sqr, + const double sa, const double angle, + const LineData& l, int side ) +{ + const Coordinate possiblepoint = calcCircleLineIntersect( c, sqr, l, side ); + if ( isOnArc( possiblepoint, c, sqrt( sqr ), sa, angle, test_threshold ) ) + return possiblepoint; + else return Coordinate::invalidCoord(); +} + +const Coordinate calcPointProjection( const Coordinate& p, + const LineData& l ) +{ + Coordinate orth = l.dir().orthogonal(); + return p + orth.normalize( calcDistancePointLine( p, l ) ); +} + +double calcDistancePointLine( const Coordinate& p, + const LineData& l ) +{ + double xa = l.a.x; + double ya = l.a.y; + double xb = l.b.x; + double yb = l.b.y; + double x = p.x; + double y = p.y; + double norm = l.dir().length(); + return ( yb * x - ya * x - xb * y + xa * y + xb * ya - yb * xa ) / norm; +} + +Coordinate calcRotatedPoint( const Coordinate& a, const Coordinate& c, const double arc ) +{ + // we take a point p on a line through c and parallel with the + // X-axis.. + Coordinate p( c.x + 5, c.y ); + // we then calc the arc that ac forms with cp... + Coordinate d = a - c; + d = d.normalize(); + double aarc = std::acos( d.x ); + if ( d.y < 0 ) aarc = 2*M_PI - aarc; + + // we now take the sum of the two arcs to find the arc between + // pc and ca + double asum = aarc + arc; + + Coordinate ret( std::cos( asum ), std::sin( asum ) ); + ret = ret.normalize( ( a -c ).length() ); + return ret + c; +} + +Coordinate calcCircleRadicalStartPoint( const Coordinate& ca, const Coordinate& cb, + double sqra, double sqrb ) +{ + Coordinate direc = cb - ca; + Coordinate m = (ca + cb)/2; + + double dsq = direc.squareLength(); + double lambda = dsq == 0.0 ? 0.0 + : (sqra - sqrb) / (2*dsq); + + direc *= lambda; + return m + direc; +} + +double getDoubleFromUser( const QString& caption, const QString& label, double value, + QWidget* parent, bool* ok, double min, double max, int decimals ) +{ +#ifdef KIG_USE_KDOUBLEVALIDATOR + KDoubleValidator vtor( min, max, decimals, 0, 0 ); +#else + KFloatValidator vtor( min, max, (QWidget*) 0, 0 ); +#endif +#if KDE_IS_VERSION( 3, 1, 90 ) + QString input = KInputDialog::getText( + caption, label, KGlobal::locale()->formatNumber( value, decimals ), + ok, parent, "getDoubleFromUserDialog", &vtor ); +#else + QString input = + KLineEditDlg::getText( caption, label, + KGlobal::locale()->formatNumber( value, decimals ), + ok, parent, &vtor ); +#endif + + bool myok = true; + double ret = KGlobal::locale()->readNumber( input, &myok ); + if ( ! myok ) + ret = input.toDouble( & myok ); + if ( ok ) *ok = myok; + return ret; +} + +const Coordinate calcCenter( + const Coordinate& a, const Coordinate& b, const Coordinate& c ) +{ + // this algorithm is written by my brother, Christophe Devriese + // <oelewapperke@ulyssis.org> ... + // I don't get it myself :) + + double xdo = b.x-a.x; + double ydo = b.y-a.y; + + double xao = c.x-a.x; + double yao = c.y-a.y; + + double a2 = xdo*xdo + ydo*ydo; + double b2 = xao*xao + yao*yao; + + double numerator = (xdo * yao - xao * ydo); + if ( numerator == 0 ) + { + // problem: xdo * yao == xao * ydo <=> xdo/ydo == xao / yao + // this means that the lines ac and ab have the same direction, + // which means they're the same line.. + // FIXME: i would normally throw an error here, but KDE doesn't + // use exceptions, so i'm returning a bogus point :( + return a.invalidCoord(); + /* return (a+c)/2; */ + }; + double denominator = 0.5 / numerator; + + double centerx = a.x - (ydo * b2 - yao * a2) * denominator; + double centery = a.y + (xdo * b2 - xao * a2) * denominator; + + return Coordinate(centerx, centery); +} + +bool lineInRect( const Rect& r, const Coordinate& a, const Coordinate& b, + const int width, const ObjectImp* imp, const KigWidget& w ) +{ + double miss = w.screenInfo().normalMiss( width ); + +//mp: the following test didn't work for vertical segments; +// fortunately the ieee floating point standard allows us to avoid +// the test altogether, since it would produce an infinity value that +// makes the final r.contains to fail +// in any case the corresponding test for a.y - b.y was missing. + +// if ( fabs( a.x - b.x ) <= 1e-7 ) +// { +// // too small to be useful.. +// return r.contains( Coordinate( a.x, r.center().y ), miss ); +// } + +// in case we have a segment we need also to check for the case when +// the segment is entirely contained in the rect, in which case the +// final tests all fail. +// it is ok to just check for the midpoint in the rect since: +// - if we have a segment completely contained in the rect this is true +// - if the midpoint is in the rect than returning true is safe (also +// in the cases where we have a ray or a line) + + if ( r.contains( 0.5*( a + b ), miss ) ) return true; + + Coordinate dir = b - a; + double m = dir.y / dir.x; + double lefty = a.y + m * ( r.left() - a.x ); + double righty = a.y + m * ( r.right() - a.x ); + double minv = dir.x / dir.y; + double bottomx = a.x + minv * ( r.bottom() - a.y ); + double topx = a.x + minv * ( r.top() - a.y ); + + // these are the intersections between the line, and the lines + // defined by the sides of the rectangle. + Coordinate leftint( r.left(), lefty ); + Coordinate rightint( r.right(), righty ); + Coordinate bottomint( bottomx, r.bottom() ); + Coordinate topint( topx, r.top() ); + + // For each intersection, we now check if we contain the + // intersection ( this might not be the case for a segment, when the + // intersection is not between the begin and end point.. ) and if + // the rect contains the intersection.. If it does, we have a winner.. + return + ( imp->contains( leftint, width, w ) && r.contains( leftint, miss ) ) || + ( imp->contains( rightint, width, w ) && r.contains( rightint, miss ) ) || + ( imp->contains( bottomint, width, w ) && r.contains( bottomint, miss ) ) || + ( imp->contains( topint, width, w ) && r.contains( topint, miss ) ); +} + +bool operator==( const LineData& l, const LineData& r ) +{ + return l.a == r.a && l.b == r.b; +} + +bool LineData::isParallelTo( const LineData& l ) const +{ + const Coordinate& p1 = a; + const Coordinate& p2 = b; + const Coordinate& p3 = l.a; + const Coordinate& p4 = l.b; + + double dx1 = p2.x - p1.x; + double dy1 = p2.y - p1.y; + double dx2 = p4.x - p3.x; + double dy2 = p4.y - p3.y; + + return isSingular( dx1, dy1, dx2, dy2 ); +} + +bool LineData::isOrthogonalTo( const LineData& l ) const +{ + const Coordinate& p1 = a; + const Coordinate& p2 = b; + const Coordinate& p3 = l.a; + const Coordinate& p4 = l.b; + + double dx1 = p2.x - p1.x; + double dy1 = p2.y - p1.y; + double dx2 = p4.x - p3.x; + double dy2 = p4.y - p3.y; + + return isSingular( dx1, dy1, -dy2, dx2 ); +} + +bool areCollinear( const Coordinate& p1, + const Coordinate& p2, const Coordinate& p3 ) +{ + return isSingular( p2.x - p1.x, p2.y - p1.y, p3.x - p1.x, p3.y - p1.y ); +} + +bool isSingular( const double& a, const double& b, + const double& c, const double& d ) +{ + double det = a*d - b*c; + double norm1 = std::fabs(a) + std::fabs(b); + double norm2 = std::fabs(c) + std::fabs(d); + +/* + * test must be done relative to the magnitude of the two + * row (or column) vectors! + */ + return ( std::fabs(det) < test_threshold*norm1*norm2 ); +} + +const double double_inf = HUGE_VAL; +const double test_threshold = 1e-6; diff --git a/kig/misc/common.h b/kig/misc/common.h new file mode 100644 index 00000000..77a1faa2 --- /dev/null +++ b/kig/misc/common.h @@ -0,0 +1,291 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + + +#ifndef KIG_MISC_COMMON_H +#define KIG_MISC_COMMON_H + +#include "coordinate.h" +#include "rect.h" + +#include <qrect.h> +#include <kdeversion.h> + +#include <vector> +#include <assert.h> + +#ifdef KDE_IS_VERSION +#if KDE_IS_VERSION( 3, 1, 0 ) +#define KIG_USE_KDOUBLEVALIDATOR +#else +#undef KIG_USE_KDOUBLEVALIDATOR +#endif +#else +#undef KIG_USE_KDOUBLEVALIDATOR +#endif + +class ObjectImp; +class KigWidget; + +extern const double double_inf; + +/** + * Here, we define some algorithms which we need in + * various places... + */ + +double getDoubleFromUser( const QString& caption, const QString& label, double value, + QWidget* parent, bool* ok, double min, double max, int decimals ); + +/** + * Simple class representing a line. Used by various functions in Kig. + */ +class LineData { +public: + /** + * \ifnot creating-python-scripting-doc + * Default constructor. Sets a and b to the origin. + * \endif + */ + LineData() : a(), b() {} + /** + * Constructor. Sets a and b to the given Coordinates. + */ + LineData( const Coordinate& na, const Coordinate& nb ) : a( na ), b( nb ) {} + /** + * One point on the line. + */ + Coordinate a; + /** + * Another point on the line. + */ + Coordinate b; + /** + * The direction of the line. Equivalent to b - a. + */ + const Coordinate dir() const { return b - a; } + /** + * The length from a to b. + */ + double length() const { return ( b - a ).length(); } + + /** + * Return true if this line is parallel to l. + */ + bool isParallelTo( const LineData& l ) const; + + /** + * Return true if this line is orthogonal to l. + */ + bool isOrthogonalTo( const LineData& l ) const; +}; + +/** + * Equality. Tests two LineData's for equality. + */ +bool operator==( const LineData& l, const LineData& r ); + +/** + * This calcs the rotation of point a around point c by arc arc. Arc + * is in radians, in the range 0 < arc < 2*pi ... + */ +Coordinate calcRotatedPoint( const Coordinate& a, const Coordinate& c, const double arc ); + +/** + * this returns a point, so that the line through point t + * and the point returned is perpendicular to the line l. + */ +Coordinate calcPointOnPerpend( const LineData& l, const Coordinate& t ); + +/** + * this returns a point, so that the line through point t and the + * point returned is perpendicular to the direction given in dir... + */ +Coordinate calcPointOnPerpend( const Coordinate& dir, const Coordinate& t ); + +/** + * this returns a point, so that the line through point t + * and the point returned is parallel with the line l + */ +Coordinate calcPointOnParallel( const LineData& l, const Coordinate& t ); + +/** + * this returns a point, so that the line through point t + * and the point returned is parallel with the direction given in dir... + */ +Coordinate calcPointOnParallel( const Coordinate& dir, const Coordinate& t ); + + +/** + * this calcs the point where the lines l and m intersect... + */ +Coordinate calcIntersectionPoint( const LineData& l, const LineData& m ); + +/** + * this calcs the intersection points of the circle with center c and + * radius sqrt( r ), and the line l. As a circle and a + * line have two intersection points, side tells us which one we + * need... It should be 1 or -1. If the line and the circle have no + * intersection, valid is set to false, otherwise to true... + * Note that sqr is the _square_ of the radius. We do this to avoid + * rounding errors... + */ +const Coordinate calcCircleLineIntersect( const Coordinate& c, + const double sqr, + const LineData& l, + int side ); + +/** + * this calcs the intersection points of the arc with center c, + * radius sqrt( r ), start angle sa and angle angle, and the line l. + * As a arc and a line can have max two intersection points, side + * tells us which one we need... It should be 1 or -1. If the line + * and the arc have no intersection, valid is set to false, otherwise + * to true... Note that sqr is the _square_ of the radius. We do + * this to avoid rounding errors... + */ +const Coordinate calcArcLineIntersect( const Coordinate& c, const double sqr, + const double sa, const double angle, + const LineData& l, int side ); + +/** + * this calculates the perpendicular projection of point p on line + * ab... + */ +const Coordinate calcPointProjection( const Coordinate& p, + const LineData& l ); + +/** + * calc the distance of point p to the line through a and b... + */ +double calcDistancePointLine( const Coordinate& p, + const LineData& l ); + +/** + * this sets p1 and p2 to p1' and p2' so that p1'p2' is the same line + * as p1p2, and so that p1' and p2' are on the border of the Rect... + */ +void calcBorderPoints( Coordinate& p1, Coordinate& p2, const Rect& r ); +/** + * overload... + */ +void calcBorderPoints( double& xa, double& xb, double& ya, double& yb, const Rect& r); +/** + * cleaner overload, intended to replace the above two... + */ +const LineData calcBorderPoints( const LineData& l, const Rect& r ); + +/** + * this does the same as the above function, but only for b.. + */ +void calcRayBorderPoints( const Coordinate& a, Coordinate& b, const Rect& r ); + +/** + * This function calculates the center of the circle going through the + * three given points.. + */ +const Coordinate calcCenter( + const Coordinate& a, const Coordinate& b, const Coordinate& c ); + +/** + * overload... + */ +void calcRayBorderPoints( const double xa, const double xb, double& ya, + double& yb, const Rect& r ); + +/** + * calc the mirror point of p over the line l + */ +const Coordinate calcMirrorPoint( const LineData& l, + const Coordinate& p ); + +/** + * test collinearity of three points + */ +bool areCollinear( const Coordinate& p1, const Coordinate& p2, + const Coordinate& p3 ); + +/** + * test if a 2x2 matrix is singular (relatively to the + * norm of the two row vectors) + */ +bool isSingular( const double& a, const double& b, + const double& c, const double& d ); + +/** + * is o on the line defined by point a and point b ? + * fault is the allowed difference... + */ +bool isOnLine( const Coordinate& o, const Coordinate& a, + const Coordinate& b, const double fault ); + +/** + * is o on the segment defined by point a and point b ? + * this calls isOnLine(), but also checks if o is "between" a and b... + * fault is the allowed difference... + */ +bool isOnSegment( const Coordinate& o, const Coordinate& a, + const Coordinate& b, const double fault ); + +bool isOnRay( const Coordinate& o, const Coordinate& a, + const Coordinate& b, const double fault ); + +bool isOnArc( const Coordinate& o, const Coordinate& c, const double r, + const double sa, const double a, const double fault ); + +Coordinate calcCircleRadicalStartPoint( const Coordinate& ca, + const Coordinate& cb, + double sqra, double sqrb ); + +/** + * Is the line, segment, ray or vector inside r ? We need the imp to + * distinguish between rays, lines, segments or whatever.. ( we use + * their contains functions actually.. ) + */ +bool lineInRect( const Rect& r, const Coordinate& a, const Coordinate& b, + const int width, const ObjectImp* imp, const KigWidget& w ); + +template <typename T> +T kigMin( const T& a, const T& b ) +{ + return a < b ? a : b; +} + +template <typename T> +T kigMax( const T& a, const T& b ) +{ + return a > b ? a : b; +} + +template <typename T> +T kigAbs( const T& a ) +{ + return a >= 0 ? a : -a; +} + +template <typename T> +int kigSgn( const T& a ) +{ + return a == 0 ? 0 : a > 0 ? +1 : -1; +} + +extern const double test_threshold; + +#endif diff --git a/kig/misc/conic-common.cpp b/kig/misc/conic-common.cpp new file mode 100644 index 00000000..3dde7449 --- /dev/null +++ b/kig/misc/conic-common.cpp @@ -0,0 +1,888 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Maurizio Paolini <paolini@dmf.unicatt.it> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#include <config.h> + +#include "conic-common.h" + +#include "common.h" +#include "kigtransform.h" + +#include <cmath> +#include <algorithm> + +#ifdef HAVE_IEEEFP_H +#include <ieeefp.h> +#endif + +ConicCartesianData::ConicCartesianData( + const ConicPolarData& polardata + ) +{ + double ec = polardata.ecostheta0; + double es = polardata.esintheta0; + double p = polardata.pdimen; + double fx = polardata.focus1.x; + double fy = polardata.focus1.y; + + double a = 1 - ec*ec; + double b = 1 - es*es; + double c = - 2*ec*es; + double d = - 2*p*ec; + double e = - 2*p*es; + double f = - p*p; + + f += a*fx*fx + b*fy*fy + c*fx*fy - d*fx - e*fy; + d -= 2*a*fx + c*fy; + e -= 2*b*fy + c*fx; + + coeffs[0] = a; + coeffs[1] = b; + coeffs[2] = c; + coeffs[3] = d; + coeffs[4] = e; + coeffs[5] = f; +} + +ConicPolarData::ConicPolarData( const ConicCartesianData& cartdata ) +{ + double a = cartdata.coeffs[0]; + double b = cartdata.coeffs[1]; + double c = cartdata.coeffs[2]; + double d = cartdata.coeffs[3]; + double e = cartdata.coeffs[4]; + double f = cartdata.coeffs[5]; + + // 1. Compute theta (tilt of conic) + double theta = std::atan2(c, b - a)/2; + + // now I should possibly add pi/2... + double costheta = std::cos(theta); + double sintheta = std::sin(theta); + // compute new coefficients (c should now be zero) + double aa = a*costheta*costheta + b*sintheta*sintheta - c*sintheta*costheta; + double bb = a*sintheta*sintheta + b*costheta*costheta + c*sintheta*costheta; + if (aa*bb < 0) + { // we have a hyperbola we need to check the correct orientation + double dd = d*costheta - e*sintheta; + double ee = d*sintheta + e*costheta; + double xc = - dd / ( 2*aa ); + double yc = - ee / ( 2*bb ); + double ff = f + aa*xc*xc + bb*yc*yc + dd*xc + ee*yc; + if (ff*aa > 0) // wrong orientation + { + if (theta > 0) theta -= M_PI/2; + else theta += M_PI/2; + costheta = cos(theta); + sintheta = sin(theta); + aa = a*costheta*costheta + b*sintheta*sintheta - c*sintheta*costheta; + bb = a*sintheta*sintheta + b*costheta*costheta + c*sintheta*costheta; + } + } + else + { + if ( std::fabs (bb) < std::fabs (aa) ) + { + if (theta > 0) theta -= M_PI/2; + else theta += M_PI/2; + costheta = cos(theta); + sintheta = sin(theta); + aa = a*costheta*costheta + b*sintheta*sintheta - c*sintheta*costheta; + bb = a*sintheta*sintheta + b*costheta*costheta + c*sintheta*costheta; + } + } + + double cc = 2*(a - b)*sintheta*costheta + + c*(costheta*costheta - sintheta*sintheta); + // cc should be zero!!! cout << "cc = " << cc << "\n"; + double dd = d*costheta - e*sintheta; + double ee = d*sintheta + e*costheta; + + a = aa; + b = bb; + c = cc; + d = dd; + e = ee; + + // now b cannot be zero (otherwise conic is degenerate) + a /= b; + c /= b; + d /= b; + e /= b; + f /= b; + b = 1.0; + + // 2. compute y coordinate of focuses + + double yf = - e/2; + + // new values: + f += yf*yf + e*yf; + e += 2*yf; // this should be zero! + + // now: a > 0 -> ellipse + // a = 0 -> parabula + // a < 0 -> hyperbola + + double eccentricity = sqrt(1.0 - a); + + double sqrtdiscrim = sqrt(d*d - 4*a*f); + if (d < 0.0) sqrtdiscrim = -sqrtdiscrim; + double xf = (4*a*f - 4*f - d*d)/(d + eccentricity*sqrtdiscrim) / 2; + + // 3. the focus needs to be rotated back into position + focus1.x = xf*costheta + yf*sintheta; + focus1.y = -xf*sintheta + yf*costheta; + + // 4. final touch: the pdimen + pdimen = -sqrtdiscrim/2; + + ecostheta0 = eccentricity*costheta; + esintheta0 = -eccentricity*sintheta; + if ( pdimen < 0) + { + pdimen = -pdimen; + ecostheta0 = -ecostheta0; + esintheta0 = -esintheta0; + } +} + +const ConicCartesianData calcConicThroughPoints ( + const std::vector<Coordinate>& points, + const LinearConstraints c1, + const LinearConstraints c2, + const LinearConstraints c3, + const LinearConstraints c4, + const LinearConstraints c5 ) +{ + assert( 0 < points.size() && points.size() <= 5 ); + // points is a vector of up to 5 points through which the conic is + // constrained. + // this routine should compute the coefficients in the cartesian equation + // a x^2 + b y^2 + c xy + d x + e y + f = 0 + // they are defined up to a multiplicative factor. + // since we don't know (in advance) which one of them is nonzero, we + // simply keep all 6 parameters, obtaining a 5x6 linear system which + // we solve using gaussian elimination with complete pivoting + // If there are too few, then we choose some cool way to fill in the + // empty parts in the matrix according to the LinearConstraints + // given.. + + // 5 rows, 6 columns.. + double row0[6]; + double row1[6]; + double row2[6]; + double row3[6]; + double row4[6]; + double *matrix[5] = {row0, row1, row2, row3, row4}; + double solution[6]; + int scambio[6]; + LinearConstraints constraints[] = {c1, c2, c3, c4, c5}; + + int numpoints = points.size(); + int numconstraints = 5; + + // fill in the matrix elements + for ( int i = 0; i < numpoints; ++i ) + { + double xi = points[i].x; + double yi = points[i].y; + matrix[i][0] = xi*xi; + matrix[i][1] = yi*yi; + matrix[i][2] = xi*yi; + matrix[i][3] = xi; + matrix[i][4] = yi; + matrix[i][5] = 1.0; + } + + for ( int i = 0; i < numconstraints; i++ ) + { + if (numpoints >= 5) break; // don't add constraints if we have enough + for (int j = 0; j < 6; ++j) matrix[numpoints][j] = 0.0; + // force the symmetry axes to be + // parallel to the coordinate system (zero tilt): c = 0 + if (constraints[i] == zerotilt) matrix[numpoints][2] = 1.0; + // force a parabula (if zerotilt): b = 0 + if (constraints[i] == parabolaifzt) matrix[numpoints][1] = 1.0; + // force a circle (if zerotilt): a = b + if (constraints[i] == circleifzt) { + matrix[numpoints][0] = 1.0; + matrix[numpoints][1] = -1.0; } + // force an equilateral hyperbola: a + b = 0 + if (constraints[i] == equilateral) { + matrix[numpoints][0] = 1.0; + matrix[numpoints][1] = 1.0; } + // force symmetry about y-axis: d = 0 + if (constraints[i] == ysymmetry) matrix[numpoints][3] = 1.0; + // force symmetry about x-axis: e = 0 + if (constraints[i] == xsymmetry) matrix[numpoints][4] = 1.0; + + if (constraints[i] != noconstraint) ++numpoints; + } + + if ( ! GaussianElimination( matrix, numpoints, 6, scambio ) ) + return ConicCartesianData::invalidData(); + // fine della fase di eliminazione + BackwardSubstitution( matrix, numpoints, 6, scambio, solution ); + + // now solution should contain the correct coefficients.. + return ConicCartesianData( solution ); +} + +const ConicPolarData calcConicBFFP( + const std::vector<Coordinate>& args, + int type ) +{ + assert( args.size() >= 2 && args.size() <= 3 ); + assert( type == 1 || type == -1 ); + + ConicPolarData ret; + + Coordinate f1 = args[0]; + Coordinate f2 = args[1]; + Coordinate d; + double eccentricity, d1, d2, dl; + + Coordinate f2f1 = f2 - f1; + double f2f1l = f2f1.length(); + ret.ecostheta0 = f2f1.x / f2f1l; + ret.esintheta0 = f2f1.y / f2f1l; + + if ( args.size() == 3 ) + { + d = args[2]; + d1 = ( d - f1 ).length(); + d2 = ( d - f2 ).length(); + dl = fabs( d1 + type * d2 ); + eccentricity = f2f1l/dl; + } + else + { + if ( type > 0 ) eccentricity = 0.7; else eccentricity = 2.0; + dl = f2f1l/eccentricity; + } + + double rhomax = (dl + f2f1l) /2.0; + + ret.ecostheta0 *= eccentricity; + ret.esintheta0 *= eccentricity; + ret.pdimen = type*(1 - eccentricity)*rhomax; + ret.focus1 = type == 1 ? f1 : f2; + return ret; +} + +const LineData calcConicPolarLine ( + const ConicCartesianData& data, + const Coordinate& cpole, + bool& valid ) +{ + double x = cpole.x; + double y = cpole.y; + double a = data.coeffs[0]; + double b = data.coeffs[1]; + double c = data.coeffs[2]; + double d = data.coeffs[3]; + double e = data.coeffs[4]; + double f = data.coeffs[5]; + + double alpha = 2*a*x + c*y + d; + double beta = c*x + 2*b*y + e; + double gamma = d*x + e*y + 2*f; + + double normsq = alpha*alpha + beta*beta; + + if (normsq < 1e-10) // line at infinity + { + valid = false; + return LineData(); + } + valid = true; + + Coordinate reta = -gamma/normsq * Coordinate (alpha, beta); + Coordinate retb = reta + Coordinate (-beta, alpha); + return LineData( reta, retb ); +} + +const Coordinate calcConicPolarPoint ( + const ConicCartesianData& data, + const LineData& polar ) +{ + Coordinate p1 = polar.a; + Coordinate p2 = polar.b; + + double alpha = p2.y - p1.y; + double beta = p1.x - p2.x; + double gamma = p1.y*p2.x - p1.x*p2.y; + + double a11 = data.coeffs[0]; + double a22 = data.coeffs[1]; + double a12 = data.coeffs[2]/2.0; + double a13 = data.coeffs[3]/2.0; + double a23 = data.coeffs[4]/2.0; + double a33 = data.coeffs[5]; + +// double detA = a11*a22*a33 - a11*a23*a23 - a22*a13*a13 - a33*a12*a12 + +// 2*a12*a23*a13; + + double a11inv = a22*a33 - a23*a23; + double a22inv = a11*a33 - a13*a13; + double a33inv = a11*a22 - a12*a12; + double a12inv = a23*a13 - a12*a33; + double a23inv = a12*a13 - a23*a11; + double a13inv = a12*a23 - a13*a22; + + double x = a11inv*alpha + a12inv*beta + a13inv*gamma; + double y = a12inv*alpha + a22inv*beta + a23inv*gamma; + double z = a13inv*alpha + a23inv*beta + a33inv*gamma; + + if (fabs(z) < 1e-10) // point at infinity + { + return Coordinate::invalidCoord(); + } + + x /= z; + y /= z; + return Coordinate (x, y); +} + +const Coordinate calcConicLineIntersect( const ConicCartesianData& c, + const LineData& l, + double knownparam, + int which ) +{ + assert( which == 1 || which == -1 || which == 0 ); + + double aa = c.coeffs[0]; + double bb = c.coeffs[1]; + double cc = c.coeffs[2]; + double dd = c.coeffs[3]; + double ee = c.coeffs[4]; + double ff = c.coeffs[5]; + + double x = l.a.x; + double y = l.a.y; + double dx = l.b.x - l.a.x; + double dy = l.b.y - l.a.y; + + double aaa = aa*dx*dx + bb*dy*dy + cc*dx*dy; + double bbb = 2*aa*x*dx + 2*bb*y*dy + cc*x*dy + cc*y*dx + dd*dx + ee*dy; + double ccc = aa*x*x + bb*y*y + cc*x*y + dd*x + ee*y + ff; + + double t; + if ( which == 0 ) /* i.e. we have a known intersection */ + { + t = - bbb/aaa - knownparam; + return l.a + t*(l.b - l.a); + } + + double discrim = bbb*bbb - 4*aaa*ccc; + if (discrim < 0.0) + { + return Coordinate::invalidCoord(); + } + else + { + if ( which*bbb > 0 ) + { + t = bbb + which*sqrt(discrim); + t = - 2*ccc/t; + } else { + t = -bbb + which*sqrt(discrim); + t /= 2*aaa; + } + + return l.a + t*(l.b - l.a); + } +} + +ConicPolarData::ConicPolarData( + const Coordinate& f, double d, + double ec, double es ) + : focus1( f ), pdimen( d ), ecostheta0( ec ), esintheta0( es ) +{ +} + +ConicPolarData::ConicPolarData() + : focus1(), pdimen( 0 ), ecostheta0( 0 ), esintheta0( 0 ) +{ +} + +const ConicPolarData calcConicBDFP( + const LineData& directrix, + const Coordinate& cfocus, + const Coordinate& cpoint ) +{ + ConicPolarData ret; + + Coordinate ba = directrix.dir(); + double bal = ba.length(); + ret.ecostheta0 = -ba.y / bal; + ret.esintheta0 = ba.x / bal; + + Coordinate pa = cpoint - directrix.a; + + double distpf = (cpoint - cfocus).length(); + double distpd = ( pa.y*ba.x - pa.x*ba.y)/bal; + + double eccentricity = distpf/distpd; + ret.ecostheta0 *= eccentricity; + ret.esintheta0 *= eccentricity; + + Coordinate fa = cfocus - directrix.a; + ret.pdimen = ( fa.y*ba.x - fa.x*ba.y )/bal; + ret.pdimen *= eccentricity; + ret.focus1 = cfocus; + + return ret; +} + +ConicCartesianData::ConicCartesianData( const double incoeffs[6] ) +{ + std::copy( incoeffs, incoeffs + 6, coeffs ); +} + +const LineData calcConicAsymptote( + const ConicCartesianData data, + int which, bool &valid ) +{ + assert( which == -1 || which == 1 ); + + LineData ret; + double a=data.coeffs[0]; + double b=data.coeffs[1]; + double c=data.coeffs[2]; + double d=data.coeffs[3]; + double e=data.coeffs[4]; + + double normabc = a*a + b*b + c*c; + double delta = c*c - 4*a*b; + if (fabs(delta) < 1e-6*normabc) { valid = false; return ret; } + + double yc = (2*a*e - c*d)/delta; + double xc = (2*b*d - c*e)/delta; + // let c be nonnegative; we no longer need d, e, f. + if (c < 0) + { + c *= -1; + a *= -1; + b *= -1; + } + + if ( delta < 0 ) + { + valid = false; + return ret; + } + + double sqrtdelta = sqrt(delta); + Coordinate displacement; + if (which > 0) + displacement = Coordinate(-2*b, c + sqrtdelta); + else + displacement = Coordinate(c + sqrtdelta, -2*a); + ret.a = Coordinate(xc, yc); + ret.b = ret.a + displacement; + return ret; +} + +const ConicCartesianData calcConicByAsymptotes( + const LineData& line1, + const LineData& line2, + const Coordinate& p ) +{ + Coordinate p1 = line1.a; + Coordinate p2 = line1.b; + double x = p.x; + double y = p.y; + + double c1 = p1.x*p2.y - p2.x*p1.y; + double b1 = p2.x - p1.x; + double a1 = p1.y - p2.y; + + p1 = line2.a; + p2 = line2.b; + + double c2 = p1.x*p2.y - p2.x*p1.y; + double b2 = p2.x - p1.x; + double a2 = p1.y - p2.y; + + double a = a1*a2; + double b = b1*b2; + double c = a1*b2 + a2*b1; + double d = a1*c2 + a2*c1; + double e = b1*c2 + c1*b2; + + double f = a*x*x + b*y*y + c*x*y + d*x + e*y; + f = -f; + + return ConicCartesianData( a, b, c, d, e, f ); +} + +const LineData calcConicRadical( const ConicCartesianData& cequation1, + const ConicCartesianData& cequation2, + int which, int zeroindex, bool& valid ) +{ + assert( which == 1 || which == -1 ); + assert( 0 < zeroindex && zeroindex < 4 ); + LineData ret; + valid = true; + + double a = cequation1.coeffs[0]; + double b = cequation1.coeffs[1]; + double c = cequation1.coeffs[2]; + double d = cequation1.coeffs[3]; + double e = cequation1.coeffs[4]; + double f = cequation1.coeffs[5]; + + double a2 = cequation2.coeffs[0]; + double b2 = cequation2.coeffs[1]; + double c2 = cequation2.coeffs[2]; + double d2 = cequation2.coeffs[3]; + double e2 = cequation2.coeffs[4]; + double f2 = cequation2.coeffs[5]; + +// background: the family of conics c + lambda*c2 has members that +// degenerate into a union of two lines. The values of lambda giving +// such degenerate conics is the solution of a third degree equation. +// The coefficients of such equation are given by: +// (Thanks to Dominique Devriese for the suggestion of this approach) +// domi: (And thanks to Maurizio for implementing it :) + + double df = 4*a*b*f - a*e*e - b*d*d - c*c*f + c*d*e; + double cf = 4*a2*b*f + 4*a*b2*f + 4*a*b*f2 + - 2*a*e*e2 - 2*b*d*d2 - 2*f*c*c2 + - a2*e*e - b2*d*d - f2*c*c + + c2*d*e + c*d2*e + c*d*e2; + double bf = 4*a*b2*f2 + 4*a2*b*f2 + 4*a2*b2*f + - 2*a2*e2*e - 2*b2*d2*d - 2*f2*c2*c + - a*e2*e2 - b*d2*d2 - f*c2*c2 + + c*d2*e2 + c2*d*e2 + c2*d2*e; + double af = 4*a2*b2*f2 - a2*e2*e2 - b2*d2*d2 - c2*c2*f2 + c2*d2*e2; + +// assume both conics are nondegenerate, renormalize so that af = 1 + + df /= af; + cf /= af; + bf /= af; + af = 1.0; // not needed, just for consistency + +// computing the coefficients of the Sturm sequence + + double p1a = 2*bf*bf - 6*cf; + double p1b = bf*cf - 9*df; + double p0a = cf*p1a*p1a + p1b*(3*p1b - 2*bf*p1a); + double fval, fpval, lambda; + + if (p0a < 0 && p1a < 0) + { + // -+-- ---+ + valid = false; + return ret; + } + + lambda = -bf/3.0; //inflection point + double displace = 1.0; + if (p1a > 0) // with two stationary points + { + displace += sqrt(p1a); // should be enough. The important + // thing is that it is larger than the + // semidistance between the stationary points + } + // compute the value at the inflection point using Horner scheme + fval = bf + lambda; // b + x + fval = cf + lambda*fval; // c + xb + xx + fval = df + lambda*fval; // d + xc + xxb + xxx + + if (fabs(p0a) < 1e-7) + { // this is the case if we intersect two vertical parabulas! + p0a = 1e-7; // fall back to the one zero case + } + if (p0a < 0) + { + // we have three roots.. + // we select the one we want ( defined by mzeroindex.. ) + lambda += ( 2 - zeroindex )* displace; + } + else + { + // we have just one root + if( zeroindex > 1 ) // cannot find second and third root + { + valid = false; + return ret; + } + + if (fval > 0) // zero on the left + { + lambda -= displace; + } else { // zero on the right + lambda += displace; + } + + // p0a = 0 means we have a root with multiplicity two + } + +// +// find a root of af*lambda^3 + bf*lambda^2 + cf*lambda + df = 0 +// (use a Newton method starting from lambda = 0. Hope...) +// + + double delta; + + int iterations = 0; + const int maxiterations = 30; + while (iterations++ < maxiterations) // using Newton, iterations should be very few + { + // compute value of function and polinomial + fval = fpval = af; + fval = bf + lambda*fval; // b + xa + fpval = fval + lambda*fpval; // b + 2xa + fval = cf + lambda*fval; // c + xb + xxa + fpval = fval + lambda*fpval; // c + 2xb + 3xxa + fval = df + lambda*fval; // d + xc + xxb + xxxa + + delta = fval/fpval; + lambda -= delta; + if (fabs(delta) < 1e-6) break; + } + if (iterations >= maxiterations) { valid = false; return ret; } + + // now we have the degenerate conic: a, b, c, d, e, f + + a += lambda*a2; + b += lambda*b2; + c += lambda*c2; + d += lambda*d2; + e += lambda*e2; + f += lambda*f2; + + // domi: + // this is the determinant of the matrix of the new conic. It + // should be zero, for the new conic to be degenerate. + df = 4*a*b*f - a*e*e - b*d*d - c*c*f + c*d*e; + + //lets work in homogeneous coordinates... + + double dis1 = e*e - 4*b*f; + double maxval = fabs(dis1); + int maxind = 1; + double dis2 = d*d - 4*a*f; + if (fabs(dis2) > maxval) + { + maxval = fabs(dis2); + maxind = 2; + } + double dis3 = c*c - 4*a*b; + if (fabs(dis3) > maxval) + { + maxval = fabs(dis3); + maxind = 3; + } + // one of these must be nonzero (otherwise the matrix is ...) + // exchange elements so that the largest is the determinant of the + // first 2x2 minor + double temp; + switch (maxind) + { + case 1: // exchange 1 <-> 3 + temp = a; a = f; f = temp; + temp = c; c = e; e = temp; + temp = dis1; dis1 = dis3; dis3 = temp; + break; + + case 2: // exchange 2 <-> 3 + temp = b; b = f; f = temp; + temp = c; c = d; d = temp; + temp = dis2; dis2 = dis3; dis3 = temp; + break; + } + + // domi: + // this is the negative of the determinant of the top left of the + // matrix. If it is 0, then the conic is a parabola, if it is < 0, + // then the conic is an ellipse, if positive, the conic is a + // hyperbola. In this case, it should be positive, since we have a + // degenerate conic, which is a degenerate case of a hyperbola.. + // note that it is negative if there is no valid conic to be + // found ( e.g. not enough intersections.. ) + // double discrim = c*c - 4*a*b; + + if (dis3 < 0) + { + // domi: + // i would put an assertion here, but well, i guess it doesn't + // really matter, and this prevents crashes if the math i still + // recall from high school happens to be wrong :) + valid = false; + return ret; + }; + + double r[3]; // direction of the null space + r[0] = 2*b*d - c*e; + r[1] = 2*a*e - c*d; + r[2] = dis3; + + // now remember the switch: + switch (maxind) + { + case 1: // exchange 1 <-> 3 + temp = a; a = f; f = temp; + temp = c; c = e; e = temp; + temp = dis1; dis1 = dis3; dis3 = temp; + temp = r[0]; r[0] = r[2]; r[2] = temp; + break; + + case 2: // exchange 2 <-> 3 + temp = b; b = f; f = temp; + temp = c; c = d; d = temp; + temp = dis2; dis2 = dis3; dis3 = temp; + temp = r[1]; r[1] = r[2]; r[2] = temp; + break; + } + + // Computing a Householder reflection transformation that + // maps r onto [0, 0, k] + + double w[3]; + double rnormsq = r[0]*r[0] + r[1]*r[1] + r[2]*r[2]; + double k = sqrt( rnormsq ); + if ( k*r[2] < 0) k = -k; + double wnorm = sqrt( 2*rnormsq + 2*k*r[2] ); + w[0] = r[0]/wnorm; + w[1] = r[1]/wnorm; + w[2] = (r[2] + k)/wnorm; + + // matrix transformation using Householder matrix, the resulting + // matrix is zero on third row and column + // [q0,q1,q2]^t = A w + // alpha = w^t A w + double q0 = a*w[0] + c*w[1]/2 + d*w[2]/2; + double q1 = b*w[1] + c*w[0]/2 + e*w[2]/2; + double alpha = a*w[0]*w[0] + b*w[1]*w[1] + c*w[0]*w[1] + + d*w[0]*w[2] + e*w[1]*w[2] + f*w[2]*w[2]; + double a00 = a - 4*w[0]*q0 + 4*w[0]*w[0]*alpha; + double a11 = b - 4*w[1]*q1 + 4*w[1]*w[1]*alpha; + double a01 = c/2 - 2*w[1]*q0 - 2*w[0]*q1 + 4*w[0]*w[1]*alpha; + + double dis = a01*a01 - a00*a11; + assert ( dis >= 0 ); + double sqrtdis = sqrt( dis ); + double px, py; + if ( which*a01 > 0 ) + { + px = a01 + which*sqrtdis; + py = a11; + } else { + px = a00; + py = a01 - which*sqrtdis; + } + double p[3]; // vector orthogonal to one of the two planes + double pscalw = w[0]*px + w[1]*py; + p[0] = px - 2*pscalw*w[0]; + p[1] = py - 2*pscalw*w[1]; + p[2] = - 2*pscalw*w[2]; + + // "r" is the solution of the equation A*(x,y,z) = (0,0,0) where + // A is the matrix of the degenerate conic. This is what we + // called in the conic theory we saw in high school a "double + // point". It has the unique property that any line going through + // it is a "polar line" of the conic at hand. It only exists for + // degenerate conics. It has another unique property that if you + // take any other point on the conic, then the line between it and + // the double point is part of the conic. + // this is what we use here: we find the double point ( ret.a + // ), and then find another points on the conic. + + ret.a = -p[2]/(p[0]*p[0] + p[1]*p[1]) * Coordinate (p[0],p[1]); + ret.b = ret.a + Coordinate (-p[1],p[0]); + valid = true; + + return ret; +} + +const ConicCartesianData calcConicTransformation ( + const ConicCartesianData& data, const Transformation& t, bool& valid ) +{ + double a[3][3]; + double b[3][3]; + + a[1][1] = data.coeffs[0]; + a[2][2] = data.coeffs[1]; + a[1][2] = a[2][1] = data.coeffs[2]/2; + a[0][1] = a[1][0] = data.coeffs[3]/2; + a[0][2] = a[2][0] = data.coeffs[4]/2; + a[0][0] = data.coeffs[5]; + + Transformation ti = t.inverse( valid ); + if ( ! valid ) return ConicCartesianData(); + + double supnorm = 0.0; + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + { + b[i][j] = 0.; + for (int ii = 0; ii < 3; ii++) + { + for (int jj = 0; jj < 3; jj++) + { + b[i][j] += a[ii][jj]*ti.data( ii, i )*ti.data( jj, j ); + } + } + if ( std::fabs( b[i][j] ) > supnorm ) supnorm = std::fabs( b[i][j] ); + } + } + + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + { + b[i][j] /= supnorm; + } + } + + return ConicCartesianData ( b[1][1], b[2][2], b[1][2] + b[2][1], + b[0][1] + b[1][0], b[0][2] + b[2][0], b[0][0] ); +} + +ConicCartesianData::ConicCartesianData() +{ +} + +bool operator==( const ConicPolarData& lhs, const ConicPolarData& rhs ) +{ + return lhs.focus1 == rhs.focus1 && + lhs.pdimen == rhs.pdimen && + lhs.ecostheta0 == rhs.ecostheta0 && + lhs.esintheta0 == rhs.esintheta0; +} + +ConicCartesianData ConicCartesianData::invalidData() +{ + ConicCartesianData ret; + ret.coeffs[0] = double_inf; + return ret; +} + +bool ConicCartesianData::valid() const +{ + return finite( coeffs[0] ); +} + diff --git a/kig/misc/conic-common.h b/kig/misc/conic-common.h new file mode 100644 index 00000000..bcad5b6b --- /dev/null +++ b/kig/misc/conic-common.h @@ -0,0 +1,278 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Maurizio Paolini <paolini@dmf.unicatt.it> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#ifndef KIG_MISC_CONIC_COMMON_H +#define KIG_MISC_CONIC_COMMON_H + +#include "coordinate.h" +#include <vector> +#include "kignumerics.h" + +class ConicPolarData; +class Transformation; +class LineData; + +/** + * Cartesian Conic Data. This class represents an equation of a conic + * in the form "ax^2 + by^2 + cxy + dx + ey + f = 0". + * \internal The coefficients are stored in the order a - f. + */ +class ConicCartesianData +{ +public: + double coeffs[6]; + ConicCartesianData(); + /** + * Construct a ConicCartesianData from a ConicPolarData. + * Construct a ConicCartesianData that is the cartesian + * representation of the conic represented by d. + */ + explicit ConicCartesianData( const ConicPolarData& d ); + /** + * Construct a ConicCartesianData from its coefficients + * Construct a ConicCartesianData using the coefficients a through f + * from the equation "ax^2 + by^2 + cxy + dx + ey + f = 0" + */ + ConicCartesianData( double a, double b, double c, + double d, double e, double f ) + { + coeffs[0] = a; + coeffs[1] = b; + coeffs[2] = c; + coeffs[3] = d; + coeffs[4] = e; + coeffs[5] = f; + } + ConicCartesianData( const double incoeffs[6] ); + + /** + * Invalid conic. + * Return a ConicCartesianData representing an invalid conic. + * \see valid() + */ + static ConicCartesianData invalidData(); + /** + * Test validity. + * Return whether this is a valid conic. + * \see invalidData() + */ + bool valid() const; +}; + +/** + * This class represents an equation of a conic in the form + * \f$ \rho(\theta) = \frac{p}{1 - e \cos\theta}\f$. focus and the + * ecostheta stuff represent the coordinate system in which the + * equation yields the good result.. + */ +class ConicPolarData +{ +public: + /** + * Construct a ConicPolarData from a ConicCartesianData. + * + * Construct a ConicPolarData that is the polar + * representation of the conic represented by d. + */ + explicit ConicPolarData( const ConicCartesianData& data ); + explicit ConicPolarData(); + /** + * Construct a ConicPolarData using the parameters from the equation + * \f$ \rho(\theta) = \frac{p}{1 - e \cos\theta}\f$ + */ + ConicPolarData( const Coordinate& focus1, double dimen, + double ecostheta0, double esintheta0 ); + + /** + * The first focus of this conic. + */ + Coordinate focus1; + /** + * The pdimen value from the polar equation. + */ + double pdimen; + /** + * The ecostheta0 value from the polar equation. + */ + double ecostheta0; + /** + * The esintheta0 value from the polar equation. + */ + double esintheta0; +}; + +bool operator==( const ConicPolarData& lhs, const ConicPolarData& rhs ); + +/** + * These are the constraint values that can be passed to the + * calcConicThroughPoints function. Their meaning is as follows: + * noconstraint: no additional points will be calculated. + * zerotilt: force the symmetry axes to be parallel to the coordinate + * system ( zero tilt ). + * parabolaifzt: the returned conic should be a parabola ( if used in + * combination with zerotilt ) + * circleifzt: the returned conic should be a circle ( if used in + * combination with zerotilt ) + * equilateral: the returned conic should be equilateral + * ysymmetry: the returned conic should be symmetric over the Y-axis. + * xsymmetry: the returned conic should be symmetric over the X-axis. + */ +enum LinearConstraints { + noconstraint, zerotilt, parabolaifzt, circleifzt, + equilateral, ysymmetry, xsymmetry +}; + +/** + * Calculate a conic through a given set of points. points should + * contain at least one, and at most five points. If there are five + * points, then the conic is completely defined. If there are less, + * then additional points will be calculated according to the + * constraints given. See above for the various constraints. + * + * An invalid ConicCartesianData is returned if there is no conic + * through the given set of points, or if not enough constraints are + * given for a conic to be calculated. + */ +const ConicCartesianData calcConicThroughPoints ( + const std::vector<Coordinate>& points, + const LinearConstraints c1 = noconstraint, + const LinearConstraints c2 = noconstraint, + const LinearConstraints c3 = noconstraint, + const LinearConstraints c4 = noconstraint, + const LinearConstraints c5 = noconstraint); + +/** + * This function is used by ConicBFFP. It calcs the polar equation + * for a hyperbola ( type == -1 ) or ellipse ( type == 1 ) with + * focuses args[0] and args[1], and with args[2] on the edge of the + * conic. args.size() should be two or three. If it is two, the two + * points are taken to be the focuses, and a third point is chosen in + * a sensible way.. + */ +const ConicPolarData calcConicBFFP( + const std::vector<Coordinate>& args, + int type ); + +/** + * function used by ConicBDFP. It calcs the conic with directrix d, + * focus f, and point p on the conic.. + */ +const ConicPolarData calcConicBDFP( + const LineData& d, const Coordinate& f, const Coordinate& p ); + +/** + * This calcs the hyperbola defined by its two asymptotes line1 and + * line2, and a point p on the edge. + */ +const ConicCartesianData calcConicByAsymptotes( + const LineData& line1, + const LineData& line2, + const Coordinate& p ); + +/** + * This function calculates the polar line of the point cpole with + * respect to the given conic data. As the last argument, you should + * pass a reference to a boolean. This boolean will be set to true if + * the returned LineData is valid, and to false if the returned line + * is not valid. The latter condition only occurs if a "line at + * infinity" would have had to be returned. + */ +const LineData calcConicPolarLine ( + const ConicCartesianData& data, + const Coordinate& cpole, + bool& valid ); + +/** + * This function calculates the polar point of the line polar with + * respect to the given conic data. As the last argument, you should + * pass a reference to a boolean. This boolean will be set to true if + * the returned LineData is valid, and to false if the returned line + * is not valid. The latter condition only occurs if a "point at + * infinity" would have had to be returned. + */ +const Coordinate calcConicPolarPoint ( + const ConicCartesianData& data, + const LineData& polar ); + +/** + * This function calculates the intersection of a given line ( l ) and + * a given conic ( c ). A line and a conic have two intersections in + * general, and as such, which should be set to -1 or 1 depending on + * which intersection you want. As the last argument, you should pass + * a reference to a boolean. This boolean will be set to true if the + * returned point is valid, and to false if the returned point is not + * valid. The latter condition only occurs if the given conic and + * line do not have the specified intersection. + * + * knownparam is something special: If you already know one + * intersection of the line and the conic, and you want the other one, + * then you should set which to 0, knownparam to the curve parameter + * of the point you already know ( i.e. the value returned by + * conicimp->getParam( otherpoint ) ). + */ +const Coordinate calcConicLineIntersect( const ConicCartesianData& c, + const LineData& l, + double knownparam, + int which ); + +/** + * This function calculates the asymptote of the given conic ( data ). + * A conic has two asymptotes in general, so which should be set to +1 + * or -1 depending on which asymptote you want. As the last argument, + * you should pass a reference to a boolean. This boolean will be set + * to true if the returned line is valid, and to false if the returned + * line is not valid. The latter condition only occurs if the given + * conic does not have the specified asymptote. + */ +const LineData calcConicAsymptote( + const ConicCartesianData data, + int which, bool &valid ); + +/** + * This function calculates the radical line of two conics. A radical + * line is the line that goes through two of the intersections of two + * conics. Since two conics have up to four intersections in general, + * there are three sets of two radical lines. zeroindex specifies + * which set of radical lines you want ( set it to 1, 2 or 3 ), and + * which is set to -1 or +1 depending on which of the two radical + * lines in the set you want. As the last argument, you should pass a + * reference to a boolean. This boolean will be set to true if the + * returned line is valid, and to false if the returned line is not + * valid. The latter condition only occurs if the given conics do not + * have the specified radical line. + */ +const LineData calcConicRadical( const ConicCartesianData& cequation1, + const ConicCartesianData& cequation2, + int which, int zeroindex, bool& valid ); + +/** + * This calculates the image of the given conic ( data ) through the + * given transformation ( t ). As the last argument, you should pass + * a reference to a boolean. This boolean will be set to true if the + * returned line is valid, and to false if the returned line is not + * valid. The latter condition only occurs if the given + * transformation is singular, and as such, the transformation of the + * conic cannot be calculated. + */ +const ConicCartesianData calcConicTransformation ( + const ConicCartesianData& data, + const Transformation& t, bool& valid ); + +#endif // KIG_MISC_CONIC_COMMON_H diff --git a/kig/misc/coordinate.cpp b/kig/misc/coordinate.cpp new file mode 100644 index 00000000..13501bc9 --- /dev/null +++ b/kig/misc/coordinate.cpp @@ -0,0 +1,184 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "coordinate.h" + +#include <qglobal.h> +#include <cmath> +#include <kdebug.h> + +#include "common.h" + +using namespace std; + +Coordinate Coordinate::fromQPoint( const QPoint& p ) +{ + return Coordinate( p.x(), p.y() ); +} + +kdbgstream& operator<<( kdbgstream& s, const Coordinate& t ) +{ + s << "x: " << t.x << " y: " << t.y << endl; + return s; +} + +const Coordinate operator+ ( const Coordinate& a, const Coordinate& b ) +{ + return Coordinate ( a.x + b.x, a.y + b.y ); +} + +const Coordinate operator- ( const Coordinate& a, const Coordinate& b ) +{ + return Coordinate ( a.x - b.x, a.y - b.y ); +} + +const Coordinate operator* ( const Coordinate& a, double r ) +{ + return Coordinate ( r*a.x, r*a.y ); +} + +const Coordinate operator* ( double r, const Coordinate& a ) +{ + return Coordinate ( r*a.x, r*a.y ); +} + +const Coordinate operator/ ( const Coordinate& a, double r ) +{ + return Coordinate ( a.x/r, a.y/r ); +} + +bool operator==( const Coordinate& a, const Coordinate& b ) +{ + return a.x == b.x && a.y == b.y; +} + +bool operator!=( const Coordinate& a, const Coordinate& b ) +{ + return !operator==( a, b ); +} + +Coordinate::Coordinate() + : x(0), + y(0) +{ +} + +Coordinate::Coordinate( double nx, double ny ) + : x( nx ), + y( ny ) +{ +} + +Coordinate::Coordinate( const Coordinate& p ) + : x( p.x ), + y( p.y ) +{ +} + +const Coordinate Coordinate::operator-() const +{ + return Coordinate ( -x, -y ); +} + +Coordinate& Coordinate::operator=( const Coordinate& p ) +{ + x = p.x; + y = p.y; + return *this; +} + +Coordinate& Coordinate::operator+=( const Coordinate& p ) +{ + x += p.x; + y += p.y; + return *this; +} + +Coordinate& Coordinate::operator-=( const Coordinate& p ) +{ + x -= p.x; + y -= p.y; + return *this; +} + +Coordinate& Coordinate::operator*=( double r ) +{ + x *= r; + y *= r; + return *this; +} + +Coordinate& Coordinate::operator*=( int r ) +{ + x *= r; + y *= r; + return *this; +} + +Coordinate& Coordinate::operator/=( double r ) +{ + x /= r; + y /= r; + return *this; +} + +double Coordinate::distance( const Coordinate& p ) const +{ + return (p - *this).length(); +} + +double Coordinate::length() const +{ + return sqrt(x*x+y*y); +} + +const Coordinate Coordinate::orthogonal() const +{ + return Coordinate( -y, x ); +} + +const Coordinate Coordinate::normalize( double l ) const +{ + double oldlength = length(); + return ( *this * l ) / oldlength; +} + +const Coordinate Coordinate::round() const +{ + return Coordinate( qRound( x ), qRound( y ) ); +} + +QPoint Coordinate::toQPoint() const +{ + Coordinate t = round(); + return QPoint( (int) t.x, (int) t.y ); +} + +Coordinate Coordinate::invalidCoord() +{ + return Coordinate( double_inf, double_inf ); +} + +bool Coordinate::valid() const +{ + return abs( x ) != double_inf && abs( y ) != double_inf; +} + +double operator*( const Coordinate& a, const Coordinate& b ) +{ + return a.x * b.x + a.y * b.y; +} diff --git a/kig/misc/coordinate.h b/kig/misc/coordinate.h new file mode 100644 index 00000000..a56edb76 --- /dev/null +++ b/kig/misc/coordinate.h @@ -0,0 +1,169 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + + +#ifndef KIG_MISC_COORDINATE_H +#define KIG_MISC_COORDINATE_H + +class QPoint; +class kdbgstream; + +/** + * The Coordinate class is the basic class representing a 2D location + * by its x and y components. It has all relevant arithmetic + * operators properly defined, and should be straightforward to use.. + */ +class Coordinate +{ +public: + static Coordinate fromQPoint( const QPoint& p ); + + /** Constructor. Construct a new Coordinate, with a given x and y + * value. + */ + Coordinate( double x, double y ); + /** Copy Constructor. Construct a new Coordinate, and give it the + * same value as p. + */ + Coordinate( const Coordinate& p ); + /** + * \ifnot creating-python-scripting-doc + * \brief Default Constructor + * + * Constructs a new Coordinate, with x and y initialized to 0. + * \endif + */ + Coordinate(); + ~Coordinate() {} + + /** Create an invalid Coordinate. This is a special value of a + * Coordinate that signals that something went wrong.. + * + * \see Coordinate::valid + * + * \internal We represent an invalid coordinate by setting x or y to + * positive or negative infinity. This is handy, since it doesn't + * require us to adapt most of the functions, it doesn't need extra + * space, and most of the times that we should get an invalid coord, + * we get one automatically.. + */ + static Coordinate invalidCoord(); + /** Return whether this is a valid Coordinate. + * \see Coordinate::invalidCoord + */ + bool valid() const; + + /** Distance to another Coordinate. + */ + double distance ( const Coordinate& p ) const; + /** Length. Returns the length or norm of this coordinate. + * I.e. return the distance from this Coordinate to the origin. + * \see squareLength + */ + double length () const; + /** Square length. Equivalent to the square of \ref length, but a + * bit more efficient because no square root has to be calculated. + * \see length + */ + inline double squareLength() const; + /** Inverse. Returns the inverse of this Coordinate. + */ + const Coordinate operator- () const; + /** Orthogonal. Returns a vector which is orthogonal on this vector. + * This relation always holds: + * <pre> + * Coordinate a = ...; + * assert( a*a.orthogonal() ) == 0; + * </pre> + */ + const Coordinate orthogonal() const; + /** Round. Returns this coordinate, rounded to the nearest integral + * values. + */ + const Coordinate round() const; + /** Normalize. This sets the length to length, while keeping the + * x/y ratio untouched... + */ + const Coordinate normalize( double length = 1 ) const; + QPoint toQPoint() const; + + Coordinate& operator= ( const Coordinate& c ); + /** Add. Add c to this Coordinate + */ + Coordinate& operator+= ( const Coordinate& c ); + /** Subtract. Subtract c from this Coordinate + */ + Coordinate& operator-= ( const Coordinate& c ); + /** Scale. Scales this Coordinate by a factor r + */ + Coordinate& operator*= ( double r ); + /** Scale. Scales this Coordinate by a factor r + */ + Coordinate& operator*= ( int r ); + /** Scale. Scales this Coordinate by a factor 1/r + */ + Coordinate& operator/= ( double r ); +public: + /** X Component. The X Component of this Coordinate. + */ + double x; + /** Y Component. The Y Component of this Coordinate. + */ + double y; + + friend kdbgstream& operator<<( kdbgstream& s, const Coordinate& t ); + /** Add. Returns the sum of a and b. + */ + friend const Coordinate operator+ ( const Coordinate& a, const Coordinate& b ); + /** Subtract. Returns the difference between a and b. + */ + friend const Coordinate operator- ( const Coordinate& a, const Coordinate& b ); + /** Scale. Returns this a, scaled by a factor of r. + */ + friend const Coordinate operator* ( const Coordinate& a, double r ); + /** Scale. Returns a, scaled by a factor of 1/r. + */ + friend const Coordinate operator/ ( const Coordinate& a, double r ); + /** Scalar Product. Returns the scalar product of a and b. + */ + friend double operator*( const Coordinate& a, const Coordinate& b ); + /** Equal. Tests two Coordinates for equality. + */ + friend bool operator==( const Coordinate&, const Coordinate& ); + /** Not Equal. Tests two Coordinates for inequality. + */ + friend bool operator!=( const Coordinate&, const Coordinate& ); +}; + +const Coordinate operator/ ( const Coordinate& a, double r ); +kdbgstream& operator<<( kdbgstream& s, const Coordinate& t ); +const Coordinate operator+ ( const Coordinate& a, const Coordinate& b ); +const Coordinate operator- ( const Coordinate& a, const Coordinate& b ); +const Coordinate operator* ( const Coordinate& a, double r ); +const Coordinate operator* ( double r, const Coordinate& a ); +double operator*( const Coordinate& a, const Coordinate& b ); + +double Coordinate::squareLength() const +{ + return x*x+y*y; +} + +#endif + diff --git a/kig/misc/coordinate_system.cpp b/kig/misc/coordinate_system.cpp new file mode 100644 index 00000000..dd5181c2 --- /dev/null +++ b/kig/misc/coordinate_system.cpp @@ -0,0 +1,720 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#include "coordinate_system.h" + +#include "../kig/kig_document.h" +#include "../kig/kig_view.h" + +#include "common.h" +#include "coordinate.h" +#include "goniometry.h" +#include "kigpainter.h" + +#include <qpainter.h> +#include <qregexp.h> + +#include <kdebug.h> +#include <kglobal.h> +#include <klocale.h> +#include <knumvalidator.h> + +#include <string> +#include <math.h> + +class CoordinateValidator + : public QValidator +{ + bool mpolar; +#ifdef KIG_USE_KDOUBLEVALIDATOR + KDoubleValidator mdv; +#else + KFloatValidator mdv; +#endif + mutable QRegExp mre; +public: + CoordinateValidator( bool polar ); + ~CoordinateValidator(); + State validate ( QString & input, int & pos ) const; + void fixup ( QString & input ) const; +}; + + +CoordinateValidator::CoordinateValidator( bool polar ) + : QValidator( 0, 0 ), mpolar( polar ), mdv( 0, 0 ), + mre( polar ? "\\(? ?([0-9.,+-]+); ?([0-9.,+-]+) ?°? ?\\)?" + : "\\(? ?([0-9.,+-]+); ?([0-9.,+-]+) ?\\)?" ) +{ +} + +CoordinateValidator::~CoordinateValidator() +{ +} + +QValidator::State CoordinateValidator::validate( QString & input, int & pos ) const +{ + QString tinput = input; + if ( tinput[tinput.length() - 1 ] == ')' ) tinput.truncate( tinput.length() - 1 ); + if ( mpolar ) + { + if ( tinput[tinput.length() - 1 ] == ' ' ) tinput.truncate( tinput.length() - 1 ); + if ( tinput[tinput.length() - 1 ] == '°' ) tinput.truncate( tinput.length() - 1 ); + }; + if( tinput[tinput.length() - 1 ] == ' ' ) tinput.truncate( tinput.length() - 1 ); + if ( tinput[0] == '(' ) tinput = tinput.mid( 1 ); + if( tinput[0] == ' ' ) tinput = tinput.mid( 1 ); + int scp = tinput.find( ';' ); + if ( scp == -1 ) return mdv.validate( tinput, pos ) == Invalid ? Invalid : Valid; + else + { + QString p1 = tinput.left( scp ); + QString p2 = tinput.mid( scp + 1 ); + + State ret = Acceptable; + + int boguspos = 0; + ret = kigMin( ret, mdv.validate( p1, boguspos ) ); + + boguspos = 0; + ret = kigMin( ret, mdv.validate( p2, boguspos ) ); + + return ret; + }; +} + +void CoordinateValidator::fixup( QString & input ) const +{ + int nsc = input.contains( ';' ); + if ( nsc > 1 ) + { + // where is the second ';' + int i = input.find( ';' ); + i = input.find( ';', i ); + input = input.left( i ); + }; + // now the string has at most one semicolon left.. + int sc = input.find( ';' ); + if ( sc == -1 ) + { + sc = input.length(); + KLocale* l = KGlobal::locale(); + if ( mpolar ) + input.append( QString::fromLatin1( ";" ) + l->positiveSign() + + QString::fromLatin1( "0°" ) ); + else + input.append( QString::fromLatin1( ";" ) + l->positiveSign() + + QString::fromLatin1( "0" ) + l->decimalSymbol() + + QString::fromLatin1( "0" ) ); + }; + mre.exactMatch( input ); + QString ds1 = mre.cap( 1 ); + mdv.fixup( ds1 ); + QString ds2 = mre.cap( 2 ); + mdv.fixup( ds2 ); + input = ds1 + QString::fromLatin1( "; " ) + ds2; +} + +EuclideanCoords::EuclideanCoords() +{ +} + +QString EuclideanCoords::fromScreen( const Coordinate& p, const KigDocument& d ) const +{ + // i used to use the widget size here, but that's no good idea, + // since an object isn't asked to recalc every time the widget size + // changes.. might be a good idea to do that, but well, maybe some + // other time :) + Rect sr = d.suggestedRect(); + double m = kigMax( sr.width(), sr.height() ); + int l = kigMax( 0, (int) ( 3 - log10( m ) ) ); + QString xs = KGlobal::locale()->formatNumber( p.x, l ); + QString ys = KGlobal::locale()->formatNumber( p.y, l ); + return QString::fromLatin1( "( %1; %2 )" ).arg( xs ).arg( ys ); +} + +Coordinate EuclideanCoords::toScreen(const QString& s, bool& ok) const +{ + QRegExp r( "\\(? ?([0-9.,+-]+); ?([0-9.,+-]+) ?\\)?" ); + ok = ( r.search(s) == 0 ); + if (ok) + { + QString xs = r.cap(1); + QString ys = r.cap(2); + KLocale* l = KGlobal::locale(); + double x = l->readNumber( xs, &ok ); + if ( ! ok ) x = xs.toDouble( &ok ); + if ( ! ok ) return Coordinate(); + double y = l->readNumber( ys, &ok ); + if ( ! ok ) y = ys.toDouble( &ok ); + if ( ! ok ) return Coordinate(); + return Coordinate( x, y ); + } + return Coordinate(); +} + +/** + * copied and adapted from a ( public domain ) function i found in the + * first Graphics Gems book. Credits to Paul S. Heckbert, who wrote + * the "Nice number for graph labels" gem. + * find a "nice" number approximately equal to x. We look for + * 1, 2 or 5, multiplied by a power of 10. + */ +static double nicenum( double x, bool round ) +{ + int exp = (int) log10( x ); + double f = x/pow( 10., exp ); + double nf; + if ( round ) + { + if ( f < 1.5 ) nf = 1.; + else if ( f < 3. ) nf = 2.; + else if ( f < 7. ) nf = 5.; + else nf = 10.; + } + else + { + if ( f <= 1. ) nf = 1.; + else if ( f <= 2. ) nf = 2.; + else if ( f <= 5. ) nf = 5.; + else nf = 10.; + }; + return nf * pow( 10., exp ); +} + +void EuclideanCoords::drawGrid( KigPainter& p, bool showgrid, bool showaxes ) const +{ + p.setWholeWinOverlay(); + + // this instruction in not necessary, but there is a little + // optimization when there are no grid and no axes. + if ( !( showgrid || showaxes ) ) + return; + + // this function is inspired upon ( public domain ) code from the + // first Graphics Gems book. Credits to Paul S. Heckbert, who wrote + // the "Nice number for graph labels" gem. + + const double hmax = ceil( p.window().right() ); + const double hmin = floor( p.window().left() ); + const double vmax = ceil( p.window().top() ); + const double vmin = floor( p.window().bottom() ); + + // the number of intervals we would like to have: + // we try to have one of them per 40 pixels or so.. + const int ntick = static_cast<int>( + kigMax( hmax - hmin, vmax - vmin ) / p.pixelWidth() / 40. ) + 1; + + double hrange = nicenum( hmax - hmin, false ); + double vrange = nicenum( vmax - vmin, false ); + const double newrange = kigMin( hrange, vrange ); + hrange = newrange; + vrange = newrange; + + const double hd = nicenum( hrange / ( ntick - 1 ), true ); + const double vd = nicenum( vrange / ( ntick - 1 ), true ); + + const double hgraphmin = ceil( hmin / hd) * hd; + const double hgraphmax = floor( hmax / hd ) * hd; + const double vgraphmin = ceil( vmin / vd ) * vd; + const double vgraphmax = floor( vmax / vd ) * vd; + + const int hnfrac = kigMax( (int) - floor( log10( hd ) ), 0 ); + const int vnfrac = kigMax( (int) - floor( log10( vd ) ), 0 ); + + /****** the grid lines ******/ + if ( showgrid ) + { + p.setPen( QPen( lightGray, 0, DotLine ) ); + // vertical lines... + for ( double i = hgraphmin; i <= hgraphmax + hd/2; i += hd ) + p.drawSegment( Coordinate( i, vgraphmin ), + Coordinate( i, vgraphmax ) ); + // horizontal lines... + for ( double i = vgraphmin; i <= vgraphmax + vd/2; i += vd ) + p.drawSegment( Coordinate( hgraphmin, i ), + Coordinate( hgraphmax, i ) ); + } + + /****** the axes ******/ + if ( showaxes ) + { + p.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) ); + // x axis + p.drawSegment( Coordinate( hmin, 0 ), Coordinate( hmax, 0 ) ); + // y axis + p.drawSegment( Coordinate( 0, vmin ), Coordinate( 0, vmax ) ); + + /****** the numbers ******/ + + // x axis + for( double i = hgraphmin; i <= hgraphmax + hd / 2; i += hd ) + { + // we skip 0 since that would look stupid... (the axes going + // through the 0 etc. ) + if( fabs( i ) < 1e-8 ) continue; + + p.drawText( + Rect( Coordinate( i, 0 ), hd, -2*vd ).normalized(), + KGlobal::locale()->formatNumber( i, hnfrac ), + AlignLeft | AlignTop + ); + }; + // y axis... + for ( double i = vgraphmin; i <= vgraphmax + vd/2; i += vd ) + { + if( fabs( i ) < 1e-8 ) continue; + p.drawText ( Rect( Coordinate( 0, i ), 2*hd, vd ).normalized(), + KGlobal::locale()->formatNumber( i, vnfrac ), + AlignBottom | AlignLeft + ); + }; + // arrows on the ends of the axes... + p.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) ); + p.setBrush( QBrush( Qt::gray ) ); + std::vector<Coordinate> a; + + // the arrow on the right end of the X axis... + a.reserve( 3 ); + double u = p.pixelWidth(); + a.push_back( Coordinate( hmax - 6 * u, -3 * u) ); + a.push_back( Coordinate( hmax, 0 ) ); + a.push_back( Coordinate( hmax - 6 * u, 3 * u ) ); + p.drawArea( a ); +// p.drawPolygon( a, true ); + + // the arrow on the top end of the Y axis... + a.clear(); + a.reserve( 3 ); + a.push_back( Coordinate( 3 * u, vmax - 6 * u ) ); + a.push_back( Coordinate( 0, vmax ) ); + a.push_back( Coordinate( -3 * u, vmax - 6 * u ) ); + p.drawArea( a ); +// p.drawPolygon( a, true ); + }; // if( showaxes ) +} + +QString EuclideanCoords::coordinateFormatNotice() const +{ + return i18n( "Enter coordinates in the following format: \"x;y\",\n" + "where x is the x coordinate, and y is the y coordinate." ); +} + +QString EuclideanCoords::coordinateFormatNoticeMarkup() const +{ + return i18n( "Enter coordinates in the following format: <b>\"x;y\"</b>, " + "where x is the x coordinate, and y is the y coordinate." ); +} + +EuclideanCoords::~EuclideanCoords() +{ +} + +CoordinateSystem::~CoordinateSystem() +{ +} + +CoordinateSystem::CoordinateSystem() +{ +} + +PolarCoords::PolarCoords() +{ +} + +PolarCoords::~PolarCoords() +{ +} + +QString PolarCoords::fromScreen( const Coordinate& pt, const KigDocument& d ) const +{ + Rect sr = d.suggestedRect(); + double m = kigMax( sr.width(), sr.height() ); + int l = kigMax( 0, (int) ( 3 - log10( m ) ) ); + + double r = pt.length(); + double theta = Goniometry::convert( atan2( pt.y, pt.x ), Goniometry::Rad, Goniometry::Deg ); + + QString rs = KGlobal::locale()->formatNumber( r, l ); + QString ts = KGlobal::locale()->formatNumber( theta, 0 ); + + return QString::fromLatin1("( %1; %2° )").arg( rs ).arg( ts ); +} + +QString PolarCoords::coordinateFormatNotice() const +{ + // \xCE\xB8 is utf8 for the greek theta sign.. + return i18n( "Enter coordinates in the following format: \"r; \xCE\xB8°\",\n" + "where r and \xCE\xB8 are the polar coordinates." ); +} + +QString PolarCoords::coordinateFormatNoticeMarkup() const +{ + // \xCE\xB8 is utf8 for the greek theta sign.. + return i18n( "Enter coordinates in the following format: <b>\"r; \xCE\xB8°\"</b>, " + "where r and \xCE\xB8 are the polar coordinates." ); +} + +Coordinate PolarCoords::toScreen(const QString& s, bool& ok) const +{ + QRegExp regexp("\\(? ?([0-9.,+-]+); ?([0-9.,+-]+) ?°? ?\\)?" ); + ok = ( regexp.search( s ) == 0 ); + if (ok) + { + QString rs = regexp.cap( 1 ); + double r = KGlobal::locale()->readNumber( rs, &ok ); + if ( ! ok ) r = rs.toDouble( &ok ); + if ( ! ok ) return Coordinate(); + QString ts = regexp.cap( 2 ); + double theta = KGlobal::locale()->readNumber( ts, &ok ); + if ( ! ok ) theta = ts.toDouble( &ok ); + if ( ! ok ) return Coordinate(); + theta *= M_PI; + theta /= 180; + return Coordinate( cos( theta ) * r, sin( theta ) * r ); + } + else return Coordinate(); +} + +void PolarCoords::drawGrid( KigPainter& p, bool showgrid, bool showaxes ) const +{ + p.setWholeWinOverlay(); + + // this instruction in not necessary, but there is a little + // optimization when there are no grid and no axes. + if ( !( showgrid || showaxes ) ) + return; + + // we multiply by sqrt( 2 ) cause we don't want to miss circles in + // the corners, that intersect with the axes outside of the + // screen.. + + const double hmax = M_SQRT2*p.window().right(); + const double hmin = M_SQRT2*p.window().left(); + const double vmax = M_SQRT2*p.window().top(); + const double vmin = M_SQRT2*p.window().bottom(); + + // the intervals: + // we try to have one of them per 40 pixels or so.. + const int ntick = static_cast<int>( + kigMax( hmax - hmin, vmax - vmin ) / p.pixelWidth() / 40 ) + 1; + + const double hrange = nicenum( hmax - hmin, false ); + const double vrange = nicenum( vmax - vmin, false ); + + const double hd = nicenum( hrange / ( ntick - 1 ), true ); + const double vd = nicenum( vrange / ( ntick - 1 ), true ); + + const double hgraphmin = floor( hmin / hd) * hd; + const double hgraphmax = ceil( hmax / hd ) * hd; + const double vgraphmin = floor( vmin / vd ) * vd; + const double vgraphmax = ceil( vmax / vd ) * vd; + + const int hnfrac = kigMax( (int) - floor( log10( hd ) ), 0 ); + const int vnfrac = kigMax( (int) - floor( log10( vd ) ), 0 ); + const int nfrac = kigMax( hnfrac, vnfrac ); + + /****** the grid lines ******/ + if ( showgrid ) + { + double d = kigMin( hd, vd ); + double begin = kigMin( kigAbs( hgraphmin ), kigAbs( vgraphmin ) ); + if ( kigSgn( hgraphmin ) != kigSgn( hgraphmax ) && kigSgn( vgraphmin ) != kigSgn( vgraphmax ) ) + begin = d; + double end = kigMax( hgraphmax, vgraphmax ); + + // we also want the circles that don't fit entirely in the + // screen.. + Coordinate c( 0, 0 ); + p.setPen( QPen( lightGray, 0, DotLine ) ); + for ( double i = begin; i <= end + d / 2; i += d ) + drawGridLine( p, c, fabs( i ) ); + } + + /****** the axes ******/ + if ( showaxes ) + { + p.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) ); + // x axis + p.drawSegment( Coordinate( hmin, 0 ), Coordinate( hmax, 0 ) ); + // y axis + p.drawSegment( Coordinate( 0, vmin ), Coordinate( 0, vmax ) ); + + /****** the numbers ******/ + + // x axis + for( double i = hgraphmin; i <= hgraphmax + hd / 2; i += hd ) + { + // we skip 0 since that would look stupid... (the axes going + // through the 0 etc. ) + if( fabs( i ) < 1e-8 ) continue; + + QString is = KGlobal::locale()->formatNumber( fabs( i ), nfrac ); + p.drawText( + Rect( Coordinate( i, 0 ), hd, -2*vd ).normalized(), + is, AlignLeft | AlignTop ); + }; + // y axis... + for ( double i = vgraphmin; i <= vgraphmax + vd / 2; i += vd ) + { + if( fabs( i ) < 1e-8 ) continue; + + QString is = KGlobal::locale()->formatNumber( fabs( i ), nfrac ); + + p.drawText ( Rect( Coordinate( 0, i ), hd, vd ).normalized(), + is, AlignBottom | AlignLeft + ); + }; + // arrows on the ends of the axes... + p.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) ); + p.setBrush( QBrush( Qt::gray ) ); + std::vector<Coordinate> a; + + // the arrow on the right end of the X axis... + a.reserve( 3 ); + double u = p.pixelWidth(); + a.push_back( Coordinate( hmax - 6 * u, -3 * u) ); + a.push_back( Coordinate( hmax, 0 ) ); + a.push_back( Coordinate( hmax - 6 * u, 3 * u ) ); +// p.drawPolygon( a, true ); + p.drawArea( a ); + + // the arrow on the top end of the Y axis... + a.clear(); + a.reserve( 3 ); + a.push_back( Coordinate( 3 * u, vmax - 6 * u ) ); + a.push_back( Coordinate( 0, vmax ) ); + a.push_back( Coordinate( -3 * u, vmax - 6 * u ) ); +// p.drawPolygon( a, true ); + p.drawArea( a ); + }; // if( showaxes ) +} + +QValidator* EuclideanCoords::coordinateValidator() const +{ + return new CoordinateValidator( false ); +} + +QValidator* PolarCoords::coordinateValidator() const +{ + return new CoordinateValidator( true ); +} + +QStringList CoordinateSystemFactory::names() +{ + QStringList ret; + ret << i18n( "&Euclidean" ) + << i18n( "&Polar" ); + return ret; +} + +CoordinateSystem* CoordinateSystemFactory::build( int which ) +{ + if ( which == Euclidean ) + return new EuclideanCoords; + else if ( which == Polar ) + return new PolarCoords; + else return 0; +} + +static const char euclideanTypeString[] = "Euclidean"; +static const char polarTypeString[] = "Polar"; + +CoordinateSystem* CoordinateSystemFactory::build( const char* type ) +{ + if ( std::string( euclideanTypeString ) == type ) + return new EuclideanCoords; + if ( std::string( polarTypeString ) == type ) + return new PolarCoords; + else return 0; +} + +const char* EuclideanCoords::type() const +{ + return euclideanTypeString; +} + +const char* PolarCoords::type() const +{ + return polarTypeString; +} + +int EuclideanCoords::id() const +{ + return CoordinateSystemFactory::Euclidean; +} + +int PolarCoords::id() const +{ + return CoordinateSystemFactory::Polar; +} + +QString CoordinateSystemFactory::setCoordinateSystemStatement( int id ) +{ + switch( id ) + { + case Euclidean: + return i18n( "Set Euclidean Coordinate System" ); + case Polar: + return i18n( "Set Polar Coordinate System" ); + default: + assert( false ); + return QString::null; + } +} + +Coordinate EuclideanCoords::snapToGrid( const Coordinate& c, + const KigWidget& w ) const +{ + Rect rect = w.showingRect(); + // we recalc the interval stuff since there is no way to cache it.. + + // this function is again inspired upon ( public domain ) code from + // the first Graphics Gems book. Credits to Paul S. Heckbert, who + // wrote the "Nice number for graph labels" gem. + + const double hmax = rect.right(); + const double hmin = rect.left(); + const double vmax = rect.top(); + const double vmin = rect.bottom(); + + // the number of intervals we would like to have: + // we try to have one of them per 40 pixels or so.. + const int ntick = static_cast<int>( + kigMax( hmax - hmin, vmax - vmin ) / w.pixelWidth() / 40. ) + 1; + + const double hrange = nicenum( hmax - hmin, false ); + const double vrange = nicenum( vmax - vmin, false ); + + const double hd = nicenum( hrange / ( ntick - 1 ), true ); + const double vd = nicenum( vrange / ( ntick - 1 ), true ); + + const double hgraphmin = ceil( hmin / hd) * hd; + const double vgraphmin = ceil( vmin / vd ) * vd; + + const double nx = qRound( ( c.x - hgraphmin ) / hd ) * hd + hgraphmin; + const double ny = qRound( ( c.y - vgraphmin ) / vd ) * vd + vgraphmin; + return Coordinate( nx, ny ); +} + +Coordinate PolarCoords::snapToGrid( const Coordinate& c, + const KigWidget& w ) const +{ + // we reuse the drawGrid code to find + + // we multiply by sqrt( 2 ) cause we don't want to miss circles in + // the corners, that intersect with the axes outside of the + // screen.. + + Rect r = w.showingRect(); + + const double hmax = M_SQRT2 * r.right(); + const double hmin = M_SQRT2 * r.left(); + const double vmax = M_SQRT2 * r.top(); + const double vmin = M_SQRT2 * r.bottom(); + + // the intervals: + // we try to have one of them per 40 pixels or so.. + const int ntick = static_cast<int>( + kigMax( hmax - hmin, vmax - vmin ) / w.pixelWidth() / 40 ) + 1; + + const double hrange = nicenum( hmax - hmin, false ); + const double vrange = nicenum( vmax - vmin, false ); + + const double hd = nicenum( hrange / ( ntick - 1 ), true ); + const double vd = nicenum( vrange / ( ntick - 1 ), true ); + + double d = kigMin( hd, vd ); + + double dist = c.length(); + double ndist = qRound( dist / d ) * d; + return c.normalize( ndist ); +} + +void PolarCoords::drawGridLine( KigPainter& p, const Coordinate& c, + double r ) const +{ + Rect rect = p.window(); + + struct iterdata_t + { + int xd; + int yd; + Coordinate ( Rect::*point )() const; + Coordinate ( Rect::*oppositepoint )() const; + double horizAngle; + double vertAngle; + }; + + static const iterdata_t iterdata[] = + { + { +1, +1, &Rect::topRight, &Rect::bottomLeft, 0, M_PI/2 }, + { -1, +1, &Rect::topLeft, &Rect::bottomRight, M_PI, M_PI / 2 }, + { -1, -1, &Rect::bottomLeft, &Rect::topRight, M_PI, 3*M_PI/2 }, + { +1, -1, &Rect::bottomRight, &Rect::topLeft, 2*M_PI, 3*M_PI/2 } + }; + for ( int i = 0; i < 4; ++i ) + { + int xd = iterdata[i].xd; + int yd = iterdata[i].yd; + Coordinate point = ( rect.*iterdata[i].point )(); + Coordinate opppoint = ( rect.*iterdata[i].oppositepoint )(); + double horizangle = iterdata[i].horizAngle; + double vertangle = iterdata[i].vertAngle; + + if ( ( c.x - point.x )*xd > 0 || ( c.y - point.y )*yd > 0 ) + continue; + if ( ( c.x - opppoint.x )*-xd > r || ( c.y - opppoint.y )*-yd > r ) + continue; + + int posdir = xd*yd; + double hd = ( point.x - c.x )*xd; + assert( hd >= 0 ); + if ( hd < r ) + { + double anglediff = acos( hd/r ); + horizangle += posdir * anglediff; + } + + hd = ( c.x - opppoint.x )*-xd; + if ( hd >= 0 ) + { + double anglediff = asin( hd/r ); + vertangle -= posdir * anglediff; + } + + double vd = ( point.y - c.y )*yd; + assert( vd >= 0 ); + if ( vd < r ) + { + double anglediff = acos( vd/r ); + vertangle -= posdir * anglediff; + } + + vd = ( c.y - opppoint.y ) * -xd; + if ( vd >= 0 ) + { + double anglediff = asin( hd/r ); + horizangle += posdir * anglediff; + } + + p.drawArc( c, r, kigMin( horizangle, vertangle ), kigMax( horizangle, vertangle ) ); + } +// p.drawCircle( c, r ); +} diff --git a/kig/misc/coordinate_system.h b/kig/misc/coordinate_system.h new file mode 100644 index 00000000..af426909 --- /dev/null +++ b/kig/misc/coordinate_system.h @@ -0,0 +1,134 @@ +/* + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#ifndef KIG_MISC_COORDINATE_SYSTEM_H +#define KIG_MISC_COORDINATE_SYSTEM_H + +#include <qnamespace.h> + +class KigPainter; +class KigDocument; +class KigWidget; +class CoordinateSystem; +class QValidator; +class Coordinate; +class QString; +class QStringList; +class QWidget; + +/** + * a factory to build a CoordinateSystem and a small handle to the + * existant CoordinateSystem's... + */ +class CoordinateSystemFactory +{ +public: + enum { Euclidean = 0, Polar = 1 }; + + static QStringList names(); + static QString setCoordinateSystemStatement( int id ); + static CoordinateSystem* build( int which ); + static CoordinateSystem* build( const char* type ); +}; + +/** + * a CoordinateSystem is what the user sees: it is kept by KigPart to + * show the user a grid, and to show the coordinates of points... it + * allows for weird CoordinateSystem's like homogeneous or + * projective... + * internally, it does nothing, it could almost have been an ordinary + * Object..., mapping coordinates from and to the screen to and from + * the internal coordinates is done elsewhere ( KigPainter and + * KigWidget... ) + */ +class CoordinateSystem + : public Qt +{ +public: + CoordinateSystem(); + virtual ~CoordinateSystem(); + + virtual QString fromScreen ( const Coordinate& pt, const KigDocument& w ) const = 0; + /** + * This returns a notice to say in which format coordinates should + * be entered. This should be something like: + * i18n( "Enter coordinates in the following form: \"(x,y)\", where + * x is the x coordinate, and y is the y coordinate." ); + */ + virtual QString coordinateFormatNotice() const = 0; + /** + * Like \ref coordinateFormatNotice(), but with HTML tags useful to + * have a rich text... + */ + virtual QString coordinateFormatNoticeMarkup() const = 0; + virtual Coordinate toScreen (const QString& pt, bool& ok) const = 0; + virtual void drawGrid ( KigPainter& p, bool showgrid = true, + bool showaxes = true ) const = 0; + virtual QValidator* coordinateValidator() const = 0; + virtual Coordinate snapToGrid( const Coordinate& c, + const KigWidget& w ) const = 0; + + virtual const char* type() const = 0; + virtual int id() const = 0; +}; + +class EuclideanCoords + : public CoordinateSystem +{ +public: + EuclideanCoords(); + ~EuclideanCoords(); + QString fromScreen( const Coordinate& pt, const KigDocument& w ) const; + QString coordinateFormatNotice() const; + QString coordinateFormatNoticeMarkup() const; + Coordinate toScreen (const QString& pt, bool& ok) const; + void drawGrid ( KigPainter& p, bool showgrid = true, + bool showaxes = true ) const; + QValidator* coordinateValidator() const; + Coordinate snapToGrid( const Coordinate& c, + const KigWidget& w ) const; + + const char* type() const; + int id() const; +}; + +class PolarCoords + : public CoordinateSystem +{ + void drawGridLine( KigPainter& p, const Coordinate& center, + double radius ) const; +public: + PolarCoords(); + ~PolarCoords(); + QString fromScreen( const Coordinate& pt, const KigDocument& w ) const; + QString coordinateFormatNotice() const; + QString coordinateFormatNoticeMarkup() const; + Coordinate toScreen (const QString& pt, bool& ok) const; + void drawGrid ( KigPainter& p, bool showgrid = true, + bool showaxes = true ) const; + QValidator* coordinateValidator() const; + Coordinate snapToGrid( const Coordinate& c, + const KigWidget& w ) const; + + const char* type() const; + int id() const; +}; + +#endif diff --git a/kig/misc/cubic-common.cc b/kig/misc/cubic-common.cc new file mode 100644 index 00000000..029f1194 --- /dev/null +++ b/kig/misc/cubic-common.cc @@ -0,0 +1,527 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include <config.h> + +#include "cubic-common.h" +#include "kignumerics.h" +#include "kigtransform.h" + +#ifdef HAVE_IEEEFP_H +#include <ieeefp.h> +#endif + +/* + * coefficients of the cartesian equation for cubics + */ + +CubicCartesianData::CubicCartesianData() +{ + std::fill( coeffs, coeffs + 10, 0 ); +} + +CubicCartesianData::CubicCartesianData( + const double incoeffs[10] ) +{ + std::copy( incoeffs, incoeffs + 10, coeffs ); +} + +const CubicCartesianData calcCubicThroughPoints ( + const std::vector<Coordinate>& points ) +{ + // points is a vector of at most 9 points through which the cubic is + // constrained. + // this routine should compute the coefficients in the cartesian equation + // they are defined up to a multiplicative factor. + // since we don't know (in advance) which one of them is nonzero, we + // simply keep all 10 parameters, obtaining a 9x10 linear system which + // we solve using gaussian elimination with complete pivoting + // If there are too few, then we choose some cool way to fill in the + // empty parts in the matrix according to the LinearConstraints + // given.. + + // 9 rows, 10 columns.. + double row0[10]; + double row1[10]; + double row2[10]; + double row3[10]; + double row4[10]; + double row5[10]; + double row6[10]; + double row7[10]; + double row8[10]; + double *matrix[9] = {row0, row1, row2, row3, row4, row5, row6, row7, row8}; + double solution[10]; + int scambio[10]; + + int numpoints = points.size(); + int numconstraints = 9; + + // fill in the matrix elements + for ( int i = 0; i < numpoints; ++i ) + { + double xi = points[i].x; + double yi = points[i].y; + matrix[i][0] = 1.0; + matrix[i][1] = xi; + matrix[i][2] = yi; + matrix[i][3] = xi*xi; + matrix[i][4] = xi*yi; + matrix[i][5] = yi*yi; + matrix[i][6] = xi*xi*xi; + matrix[i][7] = xi*xi*yi; + matrix[i][8] = xi*yi*yi; + matrix[i][9] = yi*yi*yi; + } + + for ( int i = 0; i < numconstraints; i++ ) + { + if (numpoints >= 9) break; // don't add constraints if we have enough + for (int j = 0; j < 10; ++j) matrix[numpoints][j] = 0.0; + bool addedconstraint = true; + switch (i) + { + case 0: + matrix[numpoints][7] = 1.0; + matrix[numpoints][8] = -1.0; + break; + case 1: + matrix[numpoints][7] = 1.0; + break; + case 2: + matrix[numpoints][9] = 1.0; + break; + case 3: + matrix[numpoints][4] = 1.0; + break; + case 4: + matrix[numpoints][5] = 1.0; + break; + case 5: + matrix[numpoints][3] = 1.0; + break; + case 6: + matrix[numpoints][1] = 1.0; + break; + + default: + addedconstraint = false; + break; + } + + if (addedconstraint) ++numpoints; + } + + if ( ! GaussianElimination( matrix, numpoints, 10, scambio ) ) + return CubicCartesianData::invalidData(); + // fine della fase di eliminazione + BackwardSubstitution( matrix, numpoints, 10, scambio, solution ); + + // now solution should contain the correct coefficients.. + return CubicCartesianData( solution ); +} + +const CubicCartesianData calcCubicCuspThroughPoints ( + const std::vector<Coordinate>& points ) +{ + // points is a vector of at most 4 points through which the cubic is + // constrained. Moreover the cubic is required to have a cusp at the + // origin. + + // 9 rows, 10 columns.. + double row0[10]; + double row1[10]; + double row2[10]; + double row3[10]; + double row4[10]; + double row5[10]; + double row6[10]; + double row7[10]; + double row8[10]; + double *matrix[9] = {row0, row1, row2, row3, row4, row5, row6, row7, row8}; + double solution[10]; + int scambio[10]; + + int numpoints = points.size(); + int numconstraints = 9; + + // fill in the matrix elements + for ( int i = 0; i < numpoints; ++i ) + { + double xi = points[i].x; + double yi = points[i].y; + matrix[i][0] = 1.0; + matrix[i][1] = xi; + matrix[i][2] = yi; + matrix[i][3] = xi*xi; + matrix[i][4] = xi*yi; + matrix[i][5] = yi*yi; + matrix[i][6] = xi*xi*xi; + matrix[i][7] = xi*xi*yi; + matrix[i][8] = xi*yi*yi; + matrix[i][9] = yi*yi*yi; + } + + for ( int i = 0; i < numconstraints; i++ ) + { + if (numpoints >= 9) break; // don't add constraints if we have enough + for (int j = 0; j < 10; ++j) matrix[numpoints][j] = 0.0; + bool addedconstraint = true; + switch (i) + { + case 0: + matrix[numpoints][0] = 1.0; // through the origin + break; + case 1: + matrix[numpoints][1] = 1.0; + break; + case 2: + matrix[numpoints][2] = 1.0; // no first degree term + break; + case 3: + matrix[numpoints][3] = 1.0; // a011 (x^2 coeff) = 0 + break; + case 4: + matrix[numpoints][4] = 1.0; // a012 (xy coeff) = 0 + break; + case 5: + matrix[numpoints][7] = 1.0; + matrix[numpoints][8] = -1.0; + break; + case 6: + matrix[numpoints][7] = 1.0; + break; + case 7: + matrix[numpoints][9] = 1.0; + break; + case 8: + matrix[numpoints][6] = 1.0; + break; + + default: + addedconstraint = false; + break; + } + + if (addedconstraint) ++numpoints; + } + + if ( ! GaussianElimination( matrix, numpoints, 10, scambio ) ) + return CubicCartesianData::invalidData(); + // fine della fase di eliminazione + BackwardSubstitution( matrix, numpoints, 10, scambio, solution ); + + // now solution should contain the correct coefficients.. + return CubicCartesianData( solution ); +} + +const CubicCartesianData calcCubicNodeThroughPoints ( + const std::vector<Coordinate>& points ) +{ + // points is a vector of at most 6 points through which the cubic is + // constrained. Moreover the cubic is required to have a node at the + // origin. + + // 9 rows, 10 columns.. + double row0[10]; + double row1[10]; + double row2[10]; + double row3[10]; + double row4[10]; + double row5[10]; + double row6[10]; + double row7[10]; + double row8[10]; + double *matrix[9] = {row0, row1, row2, row3, row4, row5, row6, row7, row8}; + double solution[10]; + int scambio[10]; + + int numpoints = points.size(); + int numconstraints = 9; + + // fill in the matrix elements + for ( int i = 0; i < numpoints; ++i ) + { + double xi = points[i].x; + double yi = points[i].y; + matrix[i][0] = 1.0; + matrix[i][1] = xi; + matrix[i][2] = yi; + matrix[i][3] = xi*xi; + matrix[i][4] = xi*yi; + matrix[i][5] = yi*yi; + matrix[i][6] = xi*xi*xi; + matrix[i][7] = xi*xi*yi; + matrix[i][8] = xi*yi*yi; + matrix[i][9] = yi*yi*yi; + } + + for ( int i = 0; i < numconstraints; i++ ) + { + if (numpoints >= 9) break; // don't add constraints if we have enough + for (int j = 0; j < 10; ++j) matrix[numpoints][j] = 0.0; + bool addedconstraint = true; + switch (i) + { + case 0: + matrix[numpoints][0] = 1.0; + break; + case 1: + matrix[numpoints][1] = 1.0; + break; + case 2: + matrix[numpoints][2] = 1.0; + break; + case 3: + matrix[numpoints][7] = 1.0; + matrix[numpoints][8] = -1.0; + break; + case 4: + matrix[numpoints][7] = 1.0; + break; + case 5: + matrix[numpoints][9] = 1.0; + break; + case 6: + matrix[numpoints][4] = 1.0; + break; + case 7: + matrix[numpoints][5] = 1.0; + break; + case 8: + matrix[numpoints][3] = 1.0; + break; + + default: + addedconstraint = false; + break; + } + + if (addedconstraint) ++numpoints; + } + + if ( ! GaussianElimination( matrix, numpoints, 10, scambio ) ) + return CubicCartesianData::invalidData(); + // fine della fase di eliminazione + BackwardSubstitution( matrix, numpoints, 10, scambio, solution ); + + // now solution should contain the correct coefficients.. + return CubicCartesianData( solution ); +} + +/* + * computation of the y value corresponding to some x value + */ + +double calcCubicYvalue ( double x, double ymin, double ymax, int root, + CubicCartesianData data, bool& valid, + int &numroots ) +{ + valid = true; + + // compute the third degree polinomial: + double a000 = data.coeffs[0]; + double a001 = data.coeffs[1]; + double a002 = data.coeffs[2]; + double a011 = data.coeffs[3]; + double a012 = data.coeffs[4]; + double a022 = data.coeffs[5]; + double a111 = data.coeffs[6]; + double a112 = data.coeffs[7]; + double a122 = data.coeffs[8]; + double a222 = data.coeffs[9]; + + // first the y^3 coefficient, it coming only from a222: + double a = a222; + // next the y^2 coefficient (from a122 and a022): + double b = a122*x + a022; + // next the y coefficient (from a112, a012 and a002): + double c = a112*x*x + a012*x + a002; + // finally the constant coefficient (from a111, a011, a001 and a000): + double d = a111*x*x*x + a011*x*x + a001*x + a000; + + return calcCubicRoot ( ymin, ymax, a, b, c, d, root, valid, numroots ); +} + +const Coordinate calcCubicLineIntersect( const CubicCartesianData& cu, + const LineData& l, + int root, bool& valid ) +{ + assert( root == 1 || root == 2 || root == 3 ); + + double a, b, c, d; + calcCubicLineRestriction ( cu, l.a, l.b-l.a, a, b, c, d ); + int numroots; + double param = + calcCubicRoot ( -1e10, 1e10, a, b, c, d, root, valid, numroots ); + return l.a + param*(l.b - l.a); +} + +/* + * calculate the cubic polynomial resulting from the restriction + * of a cubic to a line (defined by two "Coordinates": a point and a + * direction) + */ + +void calcCubicLineRestriction ( CubicCartesianData data, + Coordinate p, Coordinate v, + double& a, double& b, double& c, double& d ) +{ + a = b = c = d = 0; + + double a000 = data.coeffs[0]; + double a001 = data.coeffs[1]; + double a002 = data.coeffs[2]; + double a011 = data.coeffs[3]; + double a012 = data.coeffs[4]; + double a022 = data.coeffs[5]; + double a111 = data.coeffs[6]; + double a112 = data.coeffs[7]; + double a122 = data.coeffs[8]; + double a222 = data.coeffs[9]; + + // zero degree term + d += a000; + + // first degree terms + d += a001*p.x + a002*p.y; + c += a001*v.x + a002*v.y; + + // second degree terms + d += a011*p.x*p.x + a012*p.x*p.y + a022*p.y*p.y; + c += 2*a011*p.x*v.x + a012*(p.x*v.y + v.x*p.y) + 2*a022*p.y*v.y; + b += a011*v.x*v.x + a012*v.x*v.y + a022*v.y*v.y; + + // third degree terms: a111 x^3 + a222 y^3 + d += a111*p.x*p.x*p.x + a222*p.y*p.y*p.y; + c += 3*(a111*p.x*p.x*v.x + a222*p.y*p.y*v.y); + b += 3*(a111*p.x*v.x*v.x + a222*p.y*v.y*v.y); + a += a111*v.x*v.x*v.x + a222*v.y*v.y*v.y; + + // third degree terms: a112 x^2 y + a122 x y^2 + d += a112*p.x*p.x*p.y + a122*p.x*p.y*p.y; + c += a112*(p.x*p.x*v.y + 2*p.x*v.x*p.y) + a122*(v.x*p.y*p.y + 2*p.x*p.y*v.y); + b += a112*(v.x*v.x*p.y + 2*v.x*p.x*v.y) + a122*(p.x*v.y*v.y + 2*v.x*v.y*p.y); + a += a112*v.x*v.x*v.y + a122*v.x*v.y*v.y; +} + + +const CubicCartesianData calcCubicTransformation ( + const CubicCartesianData& data, + const Transformation& t, bool& valid ) +{ + double a[3][3][3]; + double b[3][3][3]; + CubicCartesianData dataout; + + int icount = 0; + for (int i=0; i < 3; i++) + { + for (int j=i; j < 3; j++) + { + for (int k=j; k < 3; k++) + { + a[i][j][k] = data.coeffs[icount++]; + if ( i < k ) + { + if ( i == j ) // case aiik + { + a[i][i][k] /= 3.; + a[i][k][i] = a[k][i][i] = a[i][i][k]; + } + else if ( j == k ) // case aijj + { + a[i][j][j] /= 3.; + a[j][i][j] = a[j][j][i] = a[i][j][j]; + } + else // case aijk (i<j<k) + { + a[i][j][k] /= 6.; + a[i][k][j] = a[j][i][k] = a[j][k][i] = + a[k][i][j] = a[k][j][i] = a[i][j][k]; + } + } + } + } + } + + Transformation ti = t.inverse( valid ); + if ( ! valid ) return dataout; + + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + { + for (int k = 0; k < 3; k++) + { + b[i][j][k] = 0.; + for (int ii = 0; ii < 3; ii++) + { + for (int jj = 0; jj < 3; jj++) + { + for (int kk = 0; kk < 3; kk++) + { + b[i][j][k] += a[ii][jj][kk]*ti.data( ii, i )*ti.data( jj, j )*ti.data( kk, k ); + } + } + } + } + } + } + +// assert (fabs(b[0][1][2] - b[1][2][0]) < 1e-8); // test a couple of cases +// assert (fabs(b[0][1][1] - b[1][1][0]) < 1e-8); + + // apparently, the above assertions are wrong ( due to rounding + // errors, Maurizio and I hope :) ), so since the symmetry is not + // present, we just take the sum of the parts of the matrix elements + // that should be symmetric, instead of taking one of them, and + // multiplying it.. + + dataout.coeffs[0] = b[0][0][0]; + dataout.coeffs[1] = b[0][0][1] + b[0][1][0] + b[1][0][0]; + dataout.coeffs[2] = b[0][0][2] + b[0][2][0] + b[2][0][0]; + dataout.coeffs[3] = b[0][1][1] + b[1][0][1] + b[1][1][0]; + dataout.coeffs[4] = b[0][1][2] + b[0][2][1] + b[1][2][0] + b[1][0][2] + b[2][1][0] + b[2][0][1]; + dataout.coeffs[5] = b[0][2][2] + b[2][0][2] + b[2][2][0]; + dataout.coeffs[6] = b[1][1][1]; + dataout.coeffs[7] = b[1][1][2] + b[1][2][1] + b[2][1][1]; + dataout.coeffs[8] = b[1][2][2] + b[2][1][2] + b[2][2][1]; + dataout.coeffs[9] = b[2][2][2]; + + return dataout; +} + +bool operator==( const CubicCartesianData& lhs, const CubicCartesianData& rhs ) +{ + for ( int i = 0; i < 10; ++i ) + if ( lhs.coeffs[i] != rhs.coeffs[i] ) + return false; + return true; +} + +CubicCartesianData CubicCartesianData::invalidData() +{ + CubicCartesianData ret; + ret.coeffs[0] = double_inf; + return ret; +} + +bool CubicCartesianData::valid() const +{ + return finite( coeffs[0] ); +} diff --git a/kig/misc/cubic-common.h b/kig/misc/cubic-common.h new file mode 100644 index 00000000..8fbcd1a2 --- /dev/null +++ b/kig/misc/cubic-common.h @@ -0,0 +1,120 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_MISC_CUBIC_COMMON_H +#define KIG_MISC_CUBIC_COMMON_H + +#include "common.h" + +class Transformation; + +/** + * This class represents an equation of a cubic in the form + * \f$ a_{ijk} x_i x_j x_k = 0 \f$ (in homogeneous coordinates, + * \f$ i,j,k = 0,1,2 \f$), \f$ i <= j <= k \f$. + * The coefficients are stored in lessicografic order. + */ +class CubicCartesianData +{ +public: + double coeffs[10]; + /** + * \ifnot creating-python-scripting-doc + * \brief Default Constructor + * + * Constructs a new CubicCartesianData, with all the coeffs + * initialized to 0. + * \endif + */ + explicit CubicCartesianData(); + /** + * Constructor. Construct a new CubicCartesianData, with the given + * values as coeffs. + */ + CubicCartesianData( double a000, double a001, double a002, + double a011, double a012, double a022, + double a111, double a112, double a122, + double a222 ) + { + coeffs[0] = a000; + coeffs[1] = a001; + coeffs[2] = a002; + coeffs[3] = a011; + coeffs[4] = a012; + coeffs[5] = a022; + coeffs[6] = a111; + coeffs[7] = a112; + coeffs[8] = a122; + coeffs[9] = a222; + } + CubicCartesianData( const double incoeffs[10] ); + + /** + * Create an invalid CubicCartesianData. This is a special state of a + * CubicCartesianData that signals that something went wrong.. + * + * \see CubicCartesianData::valid + * + * \internal We represent an invalid CubicCartesianData by setting all + * the coeffs to positive or negative infinity. This is handy, since + * it doesn't require us to adapt most of the functions, it doesn't + * need extra space, and most of the times that we should get an + * invalid CubicCartesianData, we get one automatically.. + */ + static CubicCartesianData invalidData(); + /** + * Return whether this is a valid CubicCartesianData. + * + * \see CubicCartesianData::invalidData + */ + bool valid() const; +}; + +bool operator==( const CubicCartesianData& lhs, const CubicCartesianData& rhs ); + +/** + * This function calcs a cartesian cubic equation such that the + * given points are on the cubic. There can be at most 9 and at + * least 2 point. If there are less than 9, than the coefficients + * will be chosen to 1.0 if possible + */ +const CubicCartesianData calcCubicThroughPoints ( + const std::vector<Coordinate>& points ); + +const CubicCartesianData calcCubicCuspThroughPoints ( + const std::vector<Coordinate>& points ); + +const CubicCartesianData calcCubicNodeThroughPoints ( + const std::vector<Coordinate>& points ); + +double calcCubicYvalue ( double x, double ymin, double ymax, + int root, CubicCartesianData data, + bool& valid, int& numroots ); + +const Coordinate calcCubicLineIntersect( const CubicCartesianData& c, + const LineData& l, + int root, bool& valid ); + +void calcCubicLineRestriction ( CubicCartesianData data, + Coordinate p1, Coordinate dir, + double& a, double& b, double& c, double& d ); + +const CubicCartesianData calcCubicTransformation ( + const CubicCartesianData& data, + const Transformation& t, bool& valid ); + +#endif diff --git a/kig/misc/goniometry.cc b/kig/misc/goniometry.cc new file mode 100644 index 00000000..13d72fdb --- /dev/null +++ b/kig/misc/goniometry.cc @@ -0,0 +1,137 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2004 Dominique Devriese <devriese@kde.org> + Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + + +#include "goniometry.h" + +#include <qstringlist.h> + +#include <kdebug.h> +#include <klocale.h> + +#include <cmath> + +Goniometry::Goniometry() +{ + mvalue = 0.0; + msys = Rad; +} + +Goniometry::Goniometry( double value, Goniometry::System system ) +{ + mvalue = value; + msys = system; +} + +Goniometry::~Goniometry() +{ +} + +void Goniometry::setValue( double value ) +{ + mvalue = value; +} + +const double Goniometry::value() const +{ + return mvalue; +} + +void Goniometry::setSystem( Goniometry::System system ) +{ + msys = system; +} + +void Goniometry::convertTo( Goniometry::System system ) +{ + mvalue = convert( mvalue, msys, system ); + msys = system; +} + +const Goniometry::System Goniometry::system() const +{ + return msys; +} + +double Goniometry::getValue( Goniometry::System system ) +{ + return convert( mvalue, msys, system ); +} + +Goniometry& Goniometry::operator=( const Goniometry& g ) +{ + mvalue = g.value(); + msys = g.system(); + return *this; +} + +double Goniometry::convert( const double angle, const Goniometry::System from, const Goniometry::System to ) +{ + switch( from ) + { + case Deg: + { + if ( to == Rad ) + return angle * M_PI / 180; + if ( to == Grad ) + return angle * 10 / 9; + break; + } + case Rad: + { + if ( to == Deg ) + return angle * 180 / M_PI; + if ( to == Grad ) + return angle * 200 / M_PI; + break; + } + case Grad: + { + if ( to == Deg ) + return angle * 9 / 10; + if ( to == Rad ) + return angle * M_PI / 200; + break; + } + } + return angle; +} + +QStringList Goniometry::systemList() +{ + QStringList sl; + sl << i18n( "Translators: Degrees", "Deg" ); + sl << i18n( "Translators: Radians", "Rad" ); + sl << i18n( "Translators: Gradians", "Grad" ); + return sl; +} + +Goniometry::System Goniometry::intToSystem( const int index ) +{ + if( index == 0 ) + return Deg; + else if( index == 1 ) + return Rad; + else if( index == 2 ) + return Grad; + kdDebug() << "No goniometric system with index " << index << endl; + return Rad; +} diff --git a/kig/misc/goniometry.h b/kig/misc/goniometry.h new file mode 100644 index 00000000..eae84ced --- /dev/null +++ b/kig/misc/goniometry.h @@ -0,0 +1,72 @@ +// This file is part of Kig, a KDE program for Interactive Geometry... +// Copyright (C) 2004 Dominique Devriese <devriese@kde.org> +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_MISC_GONIOMETRY_H +#define KIG_MISC_GONIOMETRY_H + +#include <qstringlist.h> + +/** + * Manage an angle and convert it from/to other goniometric systems. + */ +class Goniometry +{ +public: + enum System { Deg, Rad, Grad }; + Goniometry(); + Goniometry( double value, Goniometry::System system ); + ~Goniometry(); + void setValue( double value ); + const double value() const; + /** + * Set the system of the current angle to \p system, but it doesn't + * convert the value to the new system. + * + * \see convertTo() + */ + void setSystem( Goniometry::System system ); + /** + * Set the system of the current angle to \p system and convert the + * value to the new system using \ref convert(). + * + * \see setSystem() + */ + void convertTo( Goniometry::System system ); + const Goniometry::System system() const; + double getValue( Goniometry::System system ); + /** + * The most useful method of this class: convert the specified + * \p angle from the system \p from to the system \p to. + */ + static double convert( const double angle, const Goniometry::System from, const Goniometry::System to ); + /** + * Get a list of the supported goniometric systems. + */ + static QStringList systemList(); + static Goniometry::System intToSystem( const int index ); + + Goniometry& operator= ( const Goniometry& g ); + +private: + double mvalue; + typedef Goniometry::System goniosys; + goniosys msys; +}; + +#endif diff --git a/kig/misc/guiaction.cc b/kig/misc/guiaction.cc new file mode 100644 index 00000000..d4be4ded --- /dev/null +++ b/kig/misc/guiaction.cc @@ -0,0 +1,367 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "guiaction.h" +#include "guiaction.moc" + +#include "coordinate_system.h" +#include "coordinate.h" +#include "object_constructor.h" + +#include "../kig/kig_part.h" +#include "../kig/kig_document.h" +#include "../misc/kiginputdialog.h" +#include "../modes/construct_mode.h" +#include "../modes/label.h" +#include "../objects/object_holder.h" +#include "../objects/object_factory.h" +#include "../objects/bogus_imp.h" + +#include <kiconloader.h> +#include <klocale.h> + +#include <qregexp.h> + +int GUIAction::shortcut() const +{ + return 0; +} + +GUIAction::~GUIAction() +{ +} + +ConstructibleAction::~ConstructibleAction() +{ +} + +ConstructibleAction::ConstructibleAction( + ObjectConstructor* ctor, + const QCString& actionname, + int shortcut ) + : GUIAction(), mctor( ctor ), mactionname( actionname ), mshortcut( shortcut ) +{ +} + +QString ConstructibleAction::description() const +{ + return mctor->description(); +} + +QCString ConstructibleAction::iconFileName() const +{ + return mctor->iconFileName(); +} + +QString ConstructibleAction::descriptiveName() const +{ + return mctor->descriptiveName(); +} + +void ConstructibleAction::act( KigPart& d ) +{ + BaseConstructMode* m = mctor->constructMode( d ); + d.runMode( m ); + delete m; +} + +KigGUIAction::KigGUIAction( GUIAction* act, + KigPart& doc, + QObject* parent ) + : KAction( act->descriptiveName(), + doc.instance()->iconLoader()->loadIcon( + act->iconFileName(), KIcon::Toolbar, 0, KIcon::DefaultState, 0L, true ), + act->shortcut(), + 0, 0, // no slot connection + parent, act->actionName() ), + mact( act ), + mdoc( doc ) +{ + setWhatsThis( act->description() ); + QString tooltip = act->descriptiveName(); + tooltip.replace( QRegExp( "&&" ), "&" ); + setToolTip( tooltip ); +} + +void KigGUIAction::slotActivated() +{ + mact->act( mdoc ); +} + +const char* ConstructibleAction::actionName() const +{ + return mactionname; +} + +ConstructPointAction::~ConstructPointAction() +{ +} + +QString ConstructPointAction::description() const +{ + return i18n( + "A normal point, i.e. one that is either independent or attached " + "to a line, circle, segment." + ); +} + +QCString ConstructPointAction::iconFileName() const +{ + return "point"; +} + +QString ConstructPointAction::descriptiveName() const +{ + return i18n("Point"); +} + +const char* ConstructPointAction::actionName() const +{ + return mactionname; +} + +int ConstructPointAction::shortcut() const +{ + return Qt::Key_P; +} + +void ConstructPointAction::act( KigPart& d ) +{ + PointConstructMode m( d ); + d.runMode( &m ); +} + +ConstructPointAction::ConstructPointAction( const char* actionname ) + : mactionname( actionname ) +{ +} + +GUIAction* KigGUIAction::guiAction() +{ + return mact; +} + +void KigGUIAction::plug( KigPart* doc ) +{ + mact->plug( doc, this ); +} + +void ConstructibleAction::plug( KigPart* doc, KigGUIAction* kact ) +{ + mctor->plug( doc, kact ); +} + +QString ConstructTextLabelAction::description() const +{ + return i18n( "Construct a text label." ); +} + +QCString ConstructTextLabelAction::iconFileName() const +{ + return "kig_text"; +} + +QString ConstructTextLabelAction::descriptiveName() const +{ + return i18n( "Text Label" ); +} + +const char* ConstructTextLabelAction::actionName() const +{ + return mactionname; +} + +void ConstructTextLabelAction::act( KigPart& d ) +{ + TextLabelConstructionMode m( d ); + d.runMode( &m ); +} + +ConstructTextLabelAction::ConstructTextLabelAction( const char* actionname ) + : mactionname( actionname ) +{ +} + +QString AddFixedPointAction::description() const +{ + return i18n( "Construct a Point by its Coordinates" ); +} + +QCString AddFixedPointAction::iconFileName() const +{ + return "pointxy"; +} + +QString AddFixedPointAction::descriptiveName() const +{ + return i18n( "Point by Coordinates" ); +} + +const char* AddFixedPointAction::actionName() const +{ + return mactionname; +} + +void AddFixedPointAction::act( KigPart& doc ) +{ + bool ok; + Coordinate c = Coordinate::invalidCoord(); + KigInputDialog::getCoordinate( + i18n( "Fixed Point" ), + i18n( "Enter the coordinates for the new point." ) + + QString::fromLatin1( "<br>" ) + + doc.document().coordinateSystem().coordinateFormatNoticeMarkup(), + doc.widget(), &ok, doc.document(), &c ); + if ( ! ok ) return; + ObjectHolder* p = ObjectFactory::instance()->fixedPoint( c ); + p->calc( doc.document() ); + doc.addObject( p ); +} + +AddFixedPointAction::AddFixedPointAction( const char* actionname ) + : mactionname( actionname ) +{ +} + +AddFixedPointAction::~AddFixedPointAction() +{ +} + +void GUIAction::plug( KigPart*, KigGUIAction* ) +{ +} + +int ConstructibleAction::shortcut() const +{ + return mshortcut; +} + +int ConstructTextLabelAction::shortcut() const +{ + return Qt::Key_B; +} + +int AddFixedPointAction::shortcut() const +{ + return Qt::Key_F; +} + +#if 0 +TestAction::TestAction( const char* actionname ) + : mactionname( actionname ) +{ +} + +TestAction::~TestAction() +{ +} + +QString TestAction::description() const +{ + return QString::fromLatin1( "Test stuff !!!" ); +} + +QCString TestAction::iconFileName() const +{ + return "new"; +} + +QString TestAction::descriptiveName() const +{ + return QString::fromLatin1( "Test stuff !!!" ); +} + +const char* TestAction::actionName() const +{ + return mactionname; +} + +void TestAction::act( KigPart& doc ) +{ + const char* script = + "def calc( a ):\n\treturn Point( a.coordinate() + Coordinate( 2, 0 ) )\n"; + Object* constantpoint = ObjectFactory::instance()->fixedPoint( Coordinate( -1, -1 ) ); + constantpoint->calc( doc ); + + Object* codeobject = new DataObject( new StringImp( QString::fromLatin1( script ) ) ); + Object* compiledcode = new RealObject( PythonCompileType::instance(), Objects( codeobject ) ); + compiledcode->calc( doc ); + + Objects args( compiledcode ); + args.push_back( constantpoint ); + Object* scriptobject = new RealObject( PythonExecuteType::instance(), args ); + scriptobject->calc( doc ); + + doc.addObject( constantpoint ); + doc.addObject( scriptobject ); +} + +#endif // if 0 ( TestAction ) + +#ifdef KIG_ENABLE_PYTHON_SCRIPTING +#include "../scripting/python_type.h" +#include "../scripting/script_mode.h" + +NewScriptAction::NewScriptAction( const char* descname, const char* description, + const char* actionname, const ScriptType::Type type, + const char* icon ) + : GUIAction(), mactionname( actionname ), mdescname( descname ), + mdescription( description ), micon( icon ), mtype( type ) +{ + if ( QString( micon ).isEmpty() ) + { + micon = ScriptType::icon( type ); + } +} + +NewScriptAction::~NewScriptAction() +{ +} + +QString NewScriptAction::description() const +{ + return i18n( mdescription ); +} + +QCString NewScriptAction::iconFileName() const +{ + return micon; +} + +QString NewScriptAction::descriptiveName() const +{ + return i18n( mdescname ); +} + +const char* NewScriptAction::actionName() const +{ + return mactionname; +} + +void NewScriptAction::act( KigPart& doc ) +{ + ScriptCreationMode m( doc ); + m.setScriptType( mtype ); + doc.runMode( &m ); +} + +int NewScriptAction::shortcut() const +{ + return 0; +} + +#endif // if KIG_ENABLE_PYTHON_SCRIPTING ( NewScriptAction ) diff --git a/kig/misc/guiaction.h b/kig/misc/guiaction.h new file mode 100644 index 00000000..c188a492 --- /dev/null +++ b/kig/misc/guiaction.h @@ -0,0 +1,174 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_MISC_GUIACTION_H +#define KIG_MISC_GUIACTION_H + +#include <config.h> + +#ifdef KIG_ENABLE_PYTHON_SCRIPTING +#include "../scripting/script-common.h" +#endif + +#include <qstring.h> +#include <qcstring.h> +#include <kaction.h> + +class GUIAction; +class KigPart; + +class KigGUIAction + : public KAction +{ + Q_OBJECT + GUIAction* mact; + KigPart& mdoc; +public: + KigGUIAction( GUIAction* act, + KigPart& doc, + QObject* parent ); + void slotActivated(); + + GUIAction* guiAction(); + + void plug( KigPart* doc ); +}; + +class GUIAction +{ +public: + virtual ~GUIAction(); + + virtual QString description() const = 0; + virtual QCString iconFileName() const = 0; + virtual QString descriptiveName() const = 0; + virtual const char* actionName() const = 0; + virtual int shortcut() const = 0; + virtual void act( KigPart& ) = 0; + + virtual void plug( KigPart* doc, KigGUIAction* kact ); +}; + +class ObjectConstructor; + +class ConstructibleAction + : public GUIAction +{ + ObjectConstructor* mctor; + QCString mactionname; + int mshortcut; +public: + ConstructibleAction( ObjectConstructor* ctor, const QCString& actionname, + int shortcut = 0 ); + ~ConstructibleAction(); + QString description() const; + QCString iconFileName() const; + QString descriptiveName() const; + const char* actionName() const; + int shortcut() const; + void act( KigPart& ); + void plug( KigPart* doc, KigGUIAction* kact ); +}; + +class ConstructPointAction + : public GUIAction +{ + const char* mactionname; +public: + ConstructPointAction( const char* actionname ); + ~ConstructPointAction(); + + QString description() const; + QCString iconFileName() const; + QString descriptiveName() const; + const char* actionName() const; + int shortcut() const; + void act( KigPart& ); +}; + +class ConstructTextLabelAction + : public GUIAction +{ + const char* mactionname; +public: + ConstructTextLabelAction( const char* actionname ); + + QString description() const; + QCString iconFileName() const; + QString descriptiveName() const; + const char* actionName() const; + int shortcut() const; + void act( KigPart& ); +}; + +class AddFixedPointAction + : public GUIAction +{ + const char* mactionname; +public: + AddFixedPointAction( const char* actionname ); + ~AddFixedPointAction(); + QString description() const; + QCString iconFileName() const; + QString descriptiveName() const; + const char* actionName() const; + int shortcut() const; + void act( KigPart& ); +}; + +#if 0 +class TestAction + : public GUIAction +{ + const char* mactionname; +public: + TestAction( const char* actionname ); + ~TestAction(); + QString description() const; + QCString iconFileName() const; + QString descriptiveName() const; + const char* actionName() const; + void act( KigPart& ); +}; +#endif + +#ifdef KIG_ENABLE_PYTHON_SCRIPTING + +class NewScriptAction + : public GUIAction +{ + const char* mactionname; + const char* mdescname; + const char* mdescription; + const char* micon; + const ScriptType::Type mtype; +public: + NewScriptAction( const char* descname, const char* description, + const char* actionname, const ScriptType::Type type, + const char* icon = "" ); + ~NewScriptAction(); + QString description() const; + QCString iconFileName() const; + QString descriptiveName() const; + const char* actionName() const; + void act( KigPart& ); + int shortcut() const; +}; + +#endif // KIG_ENABLE_PYTHON_SCRIPTING + +#endif diff --git a/kig/misc/kigfiledialog.cc b/kig/misc/kigfiledialog.cc new file mode 100644 index 00000000..6b8d8cb4 --- /dev/null +++ b/kig/misc/kigfiledialog.cc @@ -0,0 +1,81 @@ +// Copyright (C) 2005 Pino Toscano <toscano.pino@tiscali.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +// USA + +#include "kigfiledialog.h" +#include "kigfiledialog.moc" + +#include <qfile.h> +#include <qpoint.h> + +#include <klocale.h> +#include <kmessagebox.h> + +KigFileDialog::KigFileDialog( const QString& startDir, const QString& filter, + const QString& caption, QWidget* parent ) + : KFileDialog( startDir, filter, parent, "kigfiledialog", true ), + mow( 0L ) +{ + setCaption( caption ); + setOperationMode( Saving ); + setMode( KFile::File | KFile::LocalOnly ); + moptcaption = i18n( "Options" ); +} + +void KigFileDialog::setOptionsWidget( QWidget* w ) +{ + mow = w; +} + +void KigFileDialog::accept() +{ + // i know this is an ugly hack, but i hadn't found other ways to get + // the selected file name _before_ the dialog is accept()'ed or + // reject()'ed... in every case, below we make sure to accept() or + // reject()... + setResult( QDialog::Accepted ); + + QString sFile = selectedFile(); + if ( QFile::exists( sFile ) ) + { + int ret = KMessageBox::warningContinueCancel( this, + i18n( "The file \"%1\" already exists. Do you wish to overwrite it?" ) + .arg( sFile ), i18n( "Overwrite File?" ), i18n("Overwrite") ); + if ( ret != KMessageBox::Continue ) + { + KFileDialog::reject(); + return; + } + } + if ( mow ) + { + KDialogBase* optdlg = new KDialogBase( + this, "optdlg", true, moptcaption, Cancel|Ok, Cancel, true ); + mow->reparent( optdlg, QPoint() ); + optdlg->setMainWidget( mow ); + optdlg->exec() == QDialog::Accepted ? KFileDialog::accept() : KFileDialog::reject(); + } + else + KFileDialog::accept(); +} + +void KigFileDialog::setOptionCaption( const QString& caption ) +{ + if ( caption.isEmpty() ) + return; + + moptcaption = caption; +} diff --git a/kig/misc/kigfiledialog.h b/kig/misc/kigfiledialog.h new file mode 100644 index 00000000..0337236d --- /dev/null +++ b/kig/misc/kigfiledialog.h @@ -0,0 +1,77 @@ +// Copyright (C) 2005 Pino Toscano <toscano.pino@tiscali.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +// USA + +#ifndef KIG_MISC_KIGFILEDIALOG_H +#define KIG_MISC_KIGFILEDIALOG_H + +#include <kfiledialog.h> + +/** + * This file dialog is pretty like KFileDialog, but allow us to make an option + * widget popup to the user. + */ +class KigFileDialog + : public KFileDialog +{ + Q_OBJECT + +private: + /** + * Options widget + */ + QWidget* mow; + + QString moptcaption; + +public: + /** + * Construct a new KigFileDialog. + * + * \param startDir the start dir of the file dialog. Consult the + * documentation of KFileDialog for more help about this + * \param filter the filter for the file dialog + * \param caption the caption of this file dialog + * \param parent the parent for this file dialog + */ + KigFileDialog( const QString& startDir, const QString& filter, + const QString& caption, QWidget *parent ); + + /** + * Use this to set the widget containing the options of eg an export filter. + * The option widget will be popped up in a dialog right after the user + * presses OK and before the dialog is closed. + * + * You can construct the option widget with no parent, as it will be + * reparented. + * + * \param w the option widget + */ + void setOptionsWidget( QWidget* w ); + + /** + * Set the caption of the option dialog + * + * \param caption the caption of the option dialog + */ + void setOptionCaption( const QString& caption ); + +protected slots: + virtual void accept(); + +}; + +#endif diff --git a/kig/misc/kiginputdialog.cc b/kig/misc/kiginputdialog.cc new file mode 100644 index 00000000..8a2c38e5 --- /dev/null +++ b/kig/misc/kiginputdialog.cc @@ -0,0 +1,283 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2005 Pino Toscano <toscano.pino@tiscali.it> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#include "kiginputdialog.h" +#include "kiginputdialog.moc" + +#include "coordinate.h" +#include "coordinate_system.h" +#include "goniometry.h" + +#include "../kig/kig_document.h" + +#include <qlabel.h> +#include <qlayout.h> +#include <qpushbutton.h> +#include <qvalidator.h> +#include <qwhatsthis.h> + +#include <kcombobox.h> +#include <kdebug.h> +#include <klineedit.h> +#include <klocale.h> +#include <ktextedit.h> + +class KigInputDialogPrivate +{ +public: + KigInputDialogPrivate(); + + QLabel* m_label; + KLineEdit* m_lineEditFirst; + KLineEdit* m_lineEditSecond; + KComboBox* m_comboBox; + KTextEdit* m_textEdit; + + Coordinate m_coord1; + Coordinate m_coord2; + KigDocument m_doc; + QValidator* m_vtor; + Goniometry m_gonio; + bool m_gonioIsNum; +}; + +KigInputDialogPrivate::KigInputDialogPrivate() + : m_label( 0L ), m_lineEditFirst( 0L ), m_lineEditSecond( 0L ), m_comboBox( 0L ), + m_textEdit( 0L ) +{ +} + +KigInputDialog::KigInputDialog( const QString& caption, const QString& label, + QWidget* parent, const KigDocument& doc, Coordinate* c1, Coordinate* c2 ) + : KDialogBase( parent, "kigdialog", true, caption, Ok|Cancel, Cancel, true ), + d( new KigInputDialogPrivate() ) +{ + d->m_coord1 = c1 ? Coordinate( *c1 ) : Coordinate::invalidCoord(); + d->m_coord2 = c2 ? Coordinate( *c2 ) : Coordinate::invalidCoord(); + d->m_doc = doc; + d->m_vtor = d->m_doc.coordinateSystem().coordinateValidator(); + + int deltay = 0; + bool ok = false; + + QFrame* frame = makeMainWidget(); + QVBoxLayout* mainlay = new QVBoxLayout( frame, 0, spacingHint() ); + mainlay->activate(); + + d->m_textEdit = new KTextEdit( frame ); + d->m_textEdit->setText( label ); + d->m_textEdit->setReadOnly( true ); + d->m_textEdit->setFocusPolicy( NoFocus ); +// d->m_textEdit->setAlignment( d->m_textEdit->alignment() | Qt::WordBreak ); + d->m_textEdit->setFrameStyle( QFrame::NoFrame ); + mainlay->addWidget( d->m_textEdit ); + + d->m_lineEditFirst = new KLineEdit( frame ); +// d->m_lineEditFirst->setValidator( d->m_vtor ); + if ( d->m_coord1.valid() ) + { + d->m_lineEditFirst->setText( d->m_doc.coordinateSystem().fromScreen( d->m_coord1, d->m_doc ) ); + ok = true; + } + mainlay->addWidget( d->m_lineEditFirst ); + + connect( d->m_lineEditFirst, SIGNAL(textChanged(const QString&)), + this, SLOT(slotCoordsChanged(const QString&)) ); + + if ( d->m_coord2.valid() ) + { + d->m_lineEditSecond = new KLineEdit( frame ); +// d->m_lineEditSecond->setValidator( d->m_vtor ); + d->m_lineEditSecond->setText( d->m_doc.coordinateSystem().fromScreen( d->m_coord2, d->m_doc ) ); + mainlay->addWidget( d->m_lineEditSecond ); + + connect( d->m_lineEditSecond, SIGNAL(textChanged(const QString&)), + this, SLOT(slotCoordsChanged(const QString&)) ); + + deltay += d->m_lineEditSecond->height() + spacingHint(); + } + + resize( 400, 160 + deltay ); + + d->m_lineEditFirst->setFocus(); + + enableButtonOK( ok ); +} + +KigInputDialog::KigInputDialog( QWidget* parent, const Goniometry& g ) + : KDialogBase( parent, "kigdialog", true, i18n( "Set Angle Size" ), Ok|Cancel, Cancel, true ), + d( new KigInputDialogPrivate() ) +{ + d->m_gonio = g; + d->m_gonioIsNum = true; + + QFrame* frame = makeMainWidget(); + QVBoxLayout* mainlay = new QVBoxLayout( frame, 0, spacingHint() ); + mainlay->activate(); + + d->m_label = new QLabel( frame ); + d->m_label->setText( i18n( "Insert the new size of this angle:" ) ); + mainlay->addWidget( d->m_label ); + + QHBoxLayout* horlay = new QHBoxLayout( 0, 0, spacingHint() ); + horlay->activate(); + + d->m_lineEditFirst = new KLineEdit( frame ); + d->m_lineEditFirst->setText( QString::number( d->m_gonio.value() ) ); + QWhatsThis::add( + d->m_lineEditFirst, + i18n( "Use this edit field to modify the size of this angle." ) ); + horlay->addWidget( d->m_lineEditFirst ); + + d->m_comboBox = new KComboBox( frame ); + d->m_comboBox->insertStringList( Goniometry::systemList() ); + d->m_comboBox->setCurrentItem( d->m_gonio.system() ); + QWhatsThis::add( + d->m_comboBox, + i18n( "Choose from this list the goniometric unit you want to use to " + "modify the size of this angle.<br>\n" + "If you switch to another unit, the value in the edit field on " + "the left will be converted to the new selected unit." ) ); + horlay->addWidget( d->m_comboBox ); + + mainlay->addLayout( horlay ); + + connect( d->m_lineEditFirst, SIGNAL(textChanged(const QString&)), + this, SLOT(slotGonioTextChanged(const QString&)) ); + connect( d->m_comboBox, SIGNAL(activated(int)), + this, SLOT(slotGonioSystemChanged(int)) ); + + resize( 350, 100 ); + + d->m_lineEditFirst->setFocus(); +} + +void KigInputDialog::keyPressEvent( QKeyEvent* e ) +{ + if ( ( e->key() == Qt::Key_Return ) && ( e->state() == 0 ) ) + { + if ( actionButton( Ok )->isEnabled() ) + { + actionButton( Ok )->animateClick(); + e->accept(); + return; + } + } + else if ( ( e->key() == Qt::Key_Escape ) && ( e->state() == 0 ) ) + { + actionButton( Cancel )->animateClick(); + e->accept(); + return; + } + +} + +void KigInputDialog::slotCoordsChanged( const QString& ) +{ + int p = 0; + QString t = d->m_lineEditFirst->text(); + bool ok = d->m_vtor->validate( t, p ) == QValidator::Acceptable; + if ( ok ) + d->m_coord1 = d->m_doc.coordinateSystem().toScreen( t, ok ); + if ( d->m_lineEditSecond ) + { + p = 0; + t = d->m_lineEditSecond->text(); + ok &= d->m_vtor->validate( t, p ) == QValidator::Acceptable; + if ( ok ) + d->m_coord2 = d->m_doc.coordinateSystem().toScreen( t, ok ); + } + + enableButtonOK( ok ); +} + +void KigInputDialog::slotGonioSystemChanged( int index ) +{ + if ( d->m_gonioIsNum ) + { + Goniometry::System newsys = Goniometry::intToSystem( index ); + d->m_gonio.convertTo( newsys ); + d->m_lineEditFirst->setText( QString::number( d->m_gonio.value() ) ); + } +} + +void KigInputDialog::slotGonioTextChanged( const QString& txt ) +{ + if ( txt.isNull() ) + d->m_gonioIsNum = false; + else + { + double v = txt.toDouble( &(d->m_gonioIsNum) ); + d->m_gonio.setValue( v ); + } + enableButtonOK( d->m_gonioIsNum ); +} + + +Coordinate KigInputDialog::coordinateFirst() const +{ + return d->m_coord1; +} + +Coordinate KigInputDialog::coordinateSecond() const +{ + return d->m_coord2; +} + +Goniometry KigInputDialog::goniometry() const +{ + return d->m_gonio; +} + +void KigInputDialog::getCoordinate( const QString& caption, const QString& label, + QWidget* parent, bool* ok, const KigDocument& doc, Coordinate* cvalue ) +{ + getTwoCoordinates( caption, label, parent, ok, doc, cvalue, 0 ); +} + +void KigInputDialog::getTwoCoordinates( const QString& caption, const QString& label, + QWidget* parent, bool* ok, const KigDocument& doc, Coordinate* cvalue, + Coordinate* cvalue2 ) +{ + KigInputDialog dlg( caption, label, parent, doc, cvalue, cvalue2 ); + + *ok = ( dlg.exec() == Accepted ); + + if ( *ok ) + { + Coordinate a = dlg.coordinateFirst(); + *cvalue = a; + if ( cvalue2 ) + { + Coordinate b = dlg.coordinateSecond(); + *cvalue2 = b; + } + } + +} + +Goniometry KigInputDialog::getAngle( QWidget* parent, bool* ok, const Goniometry& g ) +{ + KigInputDialog dlg( parent, g ); + + *ok = ( dlg.exec() == Accepted ); + + return dlg.goniometry(); +} diff --git a/kig/misc/kiginputdialog.h b/kig/misc/kiginputdialog.h new file mode 100644 index 00000000..afdd303d --- /dev/null +++ b/kig/misc/kiginputdialog.h @@ -0,0 +1,123 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2005 Pino Toscano <toscano.pino@tiscali.it> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#ifndef KIG_MISC_KIGINPUTDIALOG_H +#define KIG_MISC_KIGINPUTDIALOG_H + +class QString; +class Coordinate; +class Goniometry; +class KigDocument; +class KigInputDialogPrivate; + +#include <kdialogbase.h> + +/** + * The KigInputDialog class provides easy ways of interaction with the user. + * For example, it provides a flexible way to get one or two coordinates at + * once. + * + * It provides several static convenience functions: getCoordinate(), + * getTwoCoordinates(), getAngle(). + */ +class KigInputDialog + : KDialogBase +{ +Q_OBJECT + +public: + +private: + KigInputDialog( const QString& caption, const QString& label, QWidget* parent, + const KigDocument& doc, Coordinate* c1, Coordinate* c2 ); + KigInputDialog( QWidget* parent, const Goniometry& g ); + + virtual void keyPressEvent( QKeyEvent* e ); + + KigInputDialogPrivate* const d; + friend class KInputDialogPrivate; + + Coordinate coordinateFirst() const; + Coordinate coordinateSecond() const; + Goniometry goniometry() const; + +private slots: + void slotCoordsChanged( const QString& ); + void slotGonioSystemChanged( int index ); + void slotGonioTextChanged( const QString& txt ); + +public: + /** + * Static convenience function to get a Coordinate from the user. + * + * \param caption caption of the dialog + * \param label text of the label of the dialog + * \param parent parent of the dialog widget + * \param ok it will be set to true if the user pressed Ok after inserting a + * well-formatted Coordinate + * \param doc the actual Kig document + * \param cvalue a pointer to a Coordinate class. If the user inserted + * successfully a new Coordinate, the value will be stored + * here. If this points to a valid Coordinate, then it will be + * displayed as initial value of the correspondenting text edit + */ + static void getCoordinate( const QString& caption, const QString& label, + QWidget* parent, bool* ok, const KigDocument& doc, Coordinate* cvalue ); + + /** + * Static convenience function to get two Coordinates at once from the user. + * + * \param caption caption of the dialog + * \param label text of the label of the dialog + * \param parent parent of the dialog widget + * \param ok it will be set to true if the user pressed Ok after inserting + * well-formatted Coordinates + * \param doc the actual Kig document + * \param cvalue a pointer to a Coordinate class. If the user inserted + * successfully new Coordinates, the value of the first + * Coordinate will be stored here. If this points to a valid + * Coordinate, then it will be displayed as initial value of + * the text edit representing the first Coordinate. + * \param cvalue2 a pointer to a Coordinate class. If the user inserted + * successfully new Coordinates, the value of the second + * Coordinate will be stored here. If this points to a valid + * Coordinate, then it will be displayed as initial value of + * the text edit representing the second Coordinate. + */ + static void getTwoCoordinates( const QString& caption, const QString& label, + QWidget* parent, bool* ok, const KigDocument& doc, Coordinate* cvalue, + Coordinate* cvalue2 ); + + /** + * Static convenience function to get an angle incapsulated in a Goniometry + * class. + * + * \param parent parent of the dialog widget + * \param ok it will be set to true if the user pressed Ok after inserting a + * well-formatted angle + * \param g the Goniometry class containing the original angle we are going + * to modify. + * + * \return a Goniometry class containing the new angle + */ + static Goniometry getAngle( QWidget* parent, bool* ok, const Goniometry& g ); +}; + +#endif diff --git a/kig/misc/kignumerics.cpp b/kig/misc/kignumerics.cpp new file mode 100644 index 00000000..4711d058 --- /dev/null +++ b/kig/misc/kignumerics.cpp @@ -0,0 +1,389 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Maurizio Paolini <paolini@dmf.unicatt.it> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#include "kignumerics.h" +#include "common.h" + +/* + * compute one of the roots of a cubic polynomial + * if xmin << 0 or xmax >> 0 then autocompute a bound for all the + * roots + */ + +double calcCubicRoot ( double xmin, double xmax, double a, + double b, double c, double d, int root, bool& valid, int& numroots ) +{ + // renormalize: positive a and infinity norm = 1 + + double infnorm = fabs(a); + if ( infnorm < fabs(b) ) infnorm = fabs(b); + if ( infnorm < fabs(c) ) infnorm = fabs(c); + if ( infnorm < fabs(d) ) infnorm = fabs(d); + if ( a < 0 ) infnorm = -infnorm; + a /= infnorm; + b /= infnorm; + c /= infnorm; + d /= infnorm; + + const double small = 1e-7; + valid = false; + if ( fabs(a) < small ) + { + if ( fabs(b) < small ) + { + if ( fabs(c) < small ) + { // degree = 0; + numroots = 0; + return 0.0; + } + // degree = 1 + double rootval = -d/c; + numroots = 1; + if ( rootval < xmin || xmax < rootval ) numroots--; + if ( root > numroots ) return 0.0; + valid = true; + return rootval; + } + // degree = 2 + if ( b < 0 ) { b = -b; c = -c; d = -d; } + double discrim = c*c - 4*b*d; + numroots = 2; + if ( discrim < 0 ) + { + numroots = 0; + return 0.0; + } + discrim = sqrt(discrim)/(2*fabs(b)); + double rootmiddle = -c/(2*b); + if ( rootmiddle - discrim < xmin ) numroots--; + if ( rootmiddle + discrim > xmax ) numroots--; + if ( rootmiddle + discrim < xmin ) numroots--; + if ( rootmiddle - discrim > xmax ) numroots--; + if ( root > numroots ) return 0.0; + valid = true; + if ( root == 2 || rootmiddle - discrim < xmin ) return rootmiddle + discrim; + return rootmiddle - discrim; + } + + if ( xmin < -1e8 || xmax > 1e8 ) + { + + // compute a bound for all the real roots: + + xmax = fabs(d/a); + if ( fabs(c/a) + 1 > xmax ) xmax = fabs(c/a) + 1; + if ( fabs(b/a) + 1 > xmax ) xmax = fabs(b/a) + 1; + xmin = -xmax; + } + + // computing the coefficients of the Sturm sequence + double p1a = 2*b*b - 6*a*c; + double p1b = b*c - 9*a*d; + double p0a = c*p1a*p1a + p1b*(3*a*p1b - 2*b*p1a); + + int varbottom = calcCubicVariations (xmin, a, b, c, d, p1a, p1b, p0a); + int vartop = calcCubicVariations (xmax, a, b, c, d, p1a, p1b, p0a); + numroots = vartop - varbottom; + valid = false; + if (root <= varbottom || root > vartop ) return 0.0; + + valid = true; + + // now use bisection to separate the required root + double dx = (xmax - xmin)/2; + while ( vartop - varbottom > 1 ) + { + if ( fabs( dx ) < 1e-8 ) return (xmin + xmax)/2; + double xmiddle = xmin + dx; + int varmiddle = calcCubicVariations (xmiddle, a, b, c, d, p1a, p1b, p0a); + if ( varmiddle < root ) // I am below + { + xmin = xmiddle; + varbottom = varmiddle; + } else { + xmax = xmiddle; + vartop = varmiddle; + } + dx /= 2; + } + + /* + * now [xmin, xmax] enclose a single root, try using Newton + */ + if ( vartop - varbottom == 1 ) + { + double fval1 = a; // double check... + double fval2 = a; + fval1 = b + xmin*fval1; + fval2 = b + xmax*fval2; + fval1 = c + xmin*fval1; + fval2 = c + xmax*fval2; + fval1 = d + xmin*fval1; + fval2 = d + xmax*fval2; + assert ( fval1 * fval2 <= 0 ); + return calcCubicRootwithNewton ( xmin, xmax, a, b, c, d, 1e-8 ); + } + else // probably a double root here! + return ( xmin + xmax )/2; +} + +/* + * computation of the number of sign changes in the sturm sequence for + * a third degree polynomial at x. This number counts the number of + * roots of the polynomial on the left of point x. + * + * a, b, c, d: coefficients of the third degree polynomial (a*x^3 + ...) + * + * the second degree polynomial in the sturm sequence is just minus the + * derivative, so we don't need to compute it. + * + * p1a*x + p1b: is the third (first degree) polynomial in the sturm sequence. + * + * p0a: is the (constant) fourth polynomial of the sturm sequence. + */ + +int calcCubicVariations (double x, double a, double b, double c, + double d, double p1a, double p1b, double p0a) +{ + double fval, fpval; + fval = fpval = a; + fval = b + x*fval; + fpval = fval + x*fpval; + fval = c + x*fval; + fpval = fval + x*fpval; + fval = d + x*fval; + + double f1val = p1a*x + p1b; + + bool f3pos = fval >= 0; + bool f2pos = fpval <= 0; + bool f1pos = f1val >= 0; + bool f0pos = p0a >= 0; + + int variations = 0; + if ( f3pos != f2pos ) variations++; + if ( f2pos != f1pos ) variations++; + if ( f1pos != f0pos ) variations++; + return variations; +} + +/* + * use newton to solve a third degree equation with already isolated + * root + */ + +inline void calcCubicDerivatives ( double x, double a, double b, double c, + double d, double& fval, double& fpval, double& fppval ) +{ + fval = fpval = fppval = a; + fval = b + x*fval; + fpval = fval + x*fpval; + fppval = fpval + x*fppval; // this is really half the second derivative + fval = c + x*fval; + fpval = fval + x*fpval; + fval = d + x*fval; +} + +double calcCubicRootwithNewton ( double xmin, double xmax, double a, + double b, double c, double d, double tol ) +{ + double fval, fpval, fppval; + + double fval1, fval2, fpval1, fpval2, fppval1, fppval2; + calcCubicDerivatives ( xmin, a, b, c, d, fval1, fpval1, fppval1 ); + calcCubicDerivatives ( xmax, a, b, c, d, fval2, fpval2, fppval2 ); + assert ( fval1 * fval2 <= 0 ); + + assert ( xmax > xmin ); + while ( xmax - xmin > tol ) + { + // compute the values of function, derivative and second derivative: + assert ( fval1 * fval2 <= 0 ); + if ( fppval1 * fppval2 < 0 || fpval1 * fpval2 < 0 ) + { + double xmiddle = (xmin + xmax)/2; + calcCubicDerivatives ( xmiddle, a, b, c, d, fval, fpval, fppval ); + if ( fval1*fval <= 0 ) + { + xmax = xmiddle; + fval2 = fval; + fpval2 = fpval; + fppval2 = fppval; + } else { + xmin = xmiddle; + fval1 = fval; + fpval1 = fpval; + fppval1 = fppval; + } + } else + { + // now we have first and second derivative of constant sign, we + // can start with Newton from the Fourier point. + double x = xmin; + if ( fval2*fppval2 > 0 ) x = xmax; + double p = 1.0; + int iterations = 0; + while ( fabs(p) > tol && iterations++ < 100 ) + { + calcCubicDerivatives ( x, a, b, c, d, fval, fpval, fppval ); + p = fval/fpval; + x -= p; + } + if( iterations >= 100 ) + { + // Newton scheme did not converge.. + // we should end up with an invalid Coordinate + return double_inf; + }; + return x; + } + } + + // we cannot apply Newton, (perhaps we are at an inflection point) + + return (xmin + xmax)/2; +} + +/* + * This function computes the LU factorization of a mxn matrix, with + * m typically less then n. This is done with complete pivoting; the + * exchanges in columns are recorded in the integer vector "exchange" + */ +bool GaussianElimination( double *matrix[], int numrows, + int numcols, int exchange[] ) +{ + // start gaussian elimination + for ( int k = 0; k < numrows; ++k ) + { + // ricerca elemento di modulo massimo + double maxval = -double_inf; + int imax = k; + int jmax = k; + for( int i = k; i < numrows; ++i ) + { + for( int j = k; j < numcols; ++j ) + { + if (fabs(matrix[i][j]) > maxval) + { + maxval = fabs(matrix[i][j]); + imax = i; + jmax = j; + } + } + } + + // row exchange + if ( imax != k ) + for( int j = k; j < numcols; ++j ) + { + double t = matrix[k][j]; + matrix[k][j] = matrix[imax][j]; + matrix[imax][j] = t; + } + + // column exchange + if ( jmax != k ) + for( int i = 0; i < numrows; ++i ) + { + double t = matrix[i][k]; + matrix[i][k] = matrix[i][jmax]; + matrix[i][jmax] = t; + } + + // remember this column exchange at step k + exchange[k] = jmax; + + // we can't usefully eliminate a singular matrix.. + if ( maxval == 0. ) return false; + + // ciclo sulle righe + for( int i = k+1; i < numrows; ++i) + { + double mik = matrix[i][k]/matrix[k][k]; + matrix[i][k] = mik; //ricorda il moltiplicatore... (not necessary) + // ciclo sulle colonne + for( int j = k+1; j < numcols; ++j ) + { + matrix[i][j] -= mik*matrix[k][j]; + } + } + } + return true; +} + +/* + * solve an undetermined homogeneous triangular system. the matrix is nonzero + * on its diagonal. The last unknown(s) are chosen to be 1. The + * vector "exchange" contains exchanges to be performed on the + * final solution components. + */ + +void BackwardSubstitution( double *matrix[], int numrows, int numcols, + int exchange[], double solution[] ) +{ + // the system is homogeneous and underdetermined, the last unknown(s) + // are chosen = 1 + for ( int j = numrows; j < numcols; ++j ) + { + solution[j] = 1.0; // other choices are possible here + }; + + for( int k = numrows - 1; k >= 0; --k ) + { + // backward substitution + solution[k] = 0.0; + for ( int j = k+1; j < numcols; ++j) + { + solution[k] -= matrix[k][j]*solution[j]; + } + solution[k] /= matrix[k][k]; + } + + // ultima fase: riordinamento incognite + + for( int k = numrows - 1; k >= 0; --k ) + { + int jmax = exchange[k]; + double t = solution[k]; + solution[k] = solution[jmax]; + solution[jmax] = t; + } +} + +bool Invert3by3matrix ( const double m[3][3], double inv[3][3] ) +{ + double det = m[0][0]*(m[1][1]*m[2][2] - m[1][2]*m[2][1]) - + m[0][1]*(m[1][0]*m[2][2] - m[1][2]*m[2][0]) + + m[0][2]*(m[1][0]*m[2][1] - m[1][1]*m[2][0]); + if (det == 0) return false; + + for (int i=0; i < 3; i++) + { + for (int j=0; j < 3; j++) + { + int i1 = (i+1)%3; + int i2 = (i+2)%3; + int j1 = (j+1)%3; + int j2 = (j+2)%3; + inv[j][i] = (m[i1][j1]*m[i2][j2] - m[i1][j2]*m[i2][j1])/det; + } + } + return true; +} diff --git a/kig/misc/kignumerics.h b/kig/misc/kignumerics.h new file mode 100644 index 00000000..7beef59f --- /dev/null +++ b/kig/misc/kignumerics.h @@ -0,0 +1,47 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Maurizio Paolini <paolini@dmf.unicatt.it> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#ifndef KIG_MISC_KIGNUMERICS_H +#define KIG_MISC_KIGNUMERICS_H + +#include <cmath> + +double calcCubicRoot ( double xmin, double xmax, double a, + double b, double c, double d, int root, bool& valid, int& numroots ); + +int calcCubicVariations (double x, double a, double b, double c, + double d, double p1a, double p1b, double p0a); + +double calcCubicRootwithNewton ( double ymin, double ymax, double a, + double b, double c, double d, double tol ); + +/** + * Gaussian Elimination. We return false if the matrix is singular, + * and can't be usefully eliminated.. + */ +bool GaussianElimination( double *matrix[], int numrows, int numcols, + int scambio[] ); + +void BackwardSubstitution( double *matrix[], int numrows, int numcols, + int scambio[], double solution[] ); + +bool Invert3by3matrix ( const double m[3][3], double inv[3][3] ); + +#endif // KIG_MISC_KIGNUMERICS_H diff --git a/kig/misc/kigpainter.cpp b/kig/misc/kigpainter.cpp new file mode 100644 index 00000000..e2b2f440 --- /dev/null +++ b/kig/misc/kigpainter.cpp @@ -0,0 +1,953 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002-2003 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#include "kigpainter.h" + +#include "../kig/kig_view.h" +#include "../kig/kig_document.h" +#include "../misc/goniometry.h" +#include "../objects/object_holder.h" +#include "../objects/curve_imp.h" +#include "../objects/point_imp.h" +#include "object_hierarchy.h" +#include "common.h" +#include "conic-common.h" +#include "cubic-common.h" +#include "coordinate_system.h" + +#include <qpen.h> + +#include <cmath> +#include <stack> +#include <functional> +#include <algorithm> + +KigPainter::KigPainter( const ScreenInfo& si, QPaintDevice* device, + const KigDocument& doc, bool no ) + : mP ( device ), + color( Qt::blue ), + style( Qt::SolidLine ), + pointstyle( 0 ), + width( -1 ), + brushStyle( Qt::NoBrush ), + brushColor( Qt::blue ), + mdoc( doc ), + msi( si ), + mNeedOverlay( no ), + overlayenlarge( 0 ) +{ + mP.setBackgroundColor( Qt::white ); +} + +KigPainter::~KigPainter() +{ +} + +void KigPainter::drawRect( const Rect& r ) +{ + Rect rt = r.normalized(); + QRect qr = toScreen(rt); + qr.normalize(); + mP.drawRect(qr); + if( mNeedOverlay ) mOverlay.push_back( qr ); +} + +void KigPainter::drawRect( const QRect& r ) +{ + mP.drawRect(r); + if( mNeedOverlay ) mOverlay.push_back( r ); +} + +void KigPainter::drawCircle( const Coordinate& center, const double radius ) +{ + Coordinate bottomLeft = center - Coordinate(radius, radius); + Coordinate topRight = center + Coordinate(radius, radius); + Rect r( bottomLeft, topRight ); + QRect qr = toScreen( r ); + mP.drawEllipse ( qr ); + if( mNeedOverlay ) circleOverlay( center, radius ); +} + +void KigPainter::drawSegment( const Coordinate& from, const Coordinate& to ) +{ + QPoint tF = toScreen(from), tT = toScreen(to); + mP.drawLine( tF, tT ); + if( mNeedOverlay ) segmentOverlay( from, to ); +} + +void KigPainter::drawFatPoint( const Coordinate& p ) +{ + int twidth = width == -1 ? 5 : width; + mP.setPen( QPen( color, 1, style ) ); + switch ( pointstyle ) + { + case 0: + { + double radius = twidth * pixelWidth(); + setBrushStyle( Qt::SolidPattern ); + Coordinate rad( radius, radius ); + rad /= 2; + Coordinate tl = p - rad; + Coordinate br = p + rad; + Rect r( tl, br ); + QRect qr = toScreen( r ); + mP.drawEllipse( qr ); + if( mNeedOverlay ) mOverlay.push_back( qr ); + break; + } + case 1: + { + double radius = twidth * pixelWidth(); + setBrushStyle( Qt::NoBrush ); + Coordinate rad( radius, radius ); + rad /= 2; + Coordinate tl = p - rad; + Coordinate br = p + rad; + Rect r( tl, br ); + QRect qr = toScreen( r ); + mP.drawEllipse( qr ); + if( mNeedOverlay ) mOverlay.push_back( qr ); + break; + } + case 2: + { + double radius = twidth * pixelWidth(); + Coordinate rad( radius, radius ); + rad /= 2; + Coordinate tl = p - rad; + Coordinate br = p + rad; + Rect r( tl, br ); + QRect qr = toScreen( r ); + mP.drawRect( qr ); + mP.fillRect( qr, QBrush( color, Qt::SolidPattern ) ); + if( mNeedOverlay ) mOverlay.push_back( qr ); + break; + } + case 3: + { + double radius = twidth * pixelWidth(); + Coordinate rad( radius, radius ); + rad /= 2; + Coordinate tl = p - rad; + Coordinate br = p + rad; + Rect r( tl, br ); + QRect qr = toScreen( r ); + mP.drawRect( qr ); + if( mNeedOverlay ) mOverlay.push_back( qr ); + break; + } + case 4: + { + double radius = twidth * pixelWidth(); + Coordinate rad( radius, radius ); + rad /= 2; + Coordinate tl = p - rad; + Coordinate br = p + rad; + Rect r( tl, br ); + QRect qr = toScreen( r ); + mP.setPen( QPen( color, 2 ) ); + mP.drawLine( qr.topLeft(), qr.bottomRight() ); + mP.drawLine( qr.topRight(), qr.bottomLeft() ); + if( mNeedOverlay ) mOverlay.push_back( qr ); + break; + } + } + mP.setPen( QPen( color, twidth, style ) ); +} + +void KigPainter::drawPoint( const Coordinate& p ) +{ + mP.drawPoint( toScreen(p) ); + if( mNeedOverlay ) pointOverlay( p ); +} + +void KigPainter::drawLine( const Coordinate& p1, const Coordinate& p2 ) +{ + drawLine( LineData( p1, p2 ) ); +} + +void KigPainter::drawText( const Rect p, const QString s, int textFlags, int len ) +{ + QRect t = toScreen(p); + int tf = textFlags; + t.moveBy( 2, 2 ); + t.setWidth( t.width() - 4 ); + t.setHeight( t.height() - 4 ); + mP.drawText( t, tf, s, len ); + if( mNeedOverlay ) textOverlay( t, s, tf, len ); +} + +void KigPainter::textOverlay( const QRect& r, const QString s, int textFlags, int len ) +{ + // kdDebug() << Rect::fromQRect( mP.boundingRect( r, textFlags, s, len ) ) << endl; + QRect newr( mP.boundingRect( r, textFlags, s, len ) ); + newr.setWidth( newr.width() + 4 ); + newr.setHeight( newr.height() + 4 ); + mOverlay.push_back( newr ); +} + +const Rect KigPainter::boundingRect( const Rect& r, const QString s, + int f, int l ) const +{ + QRect qr = mP.boundingRect( toScreen( r ), f, s, l ); + qr.setWidth( qr.width() + 4 ); + qr.setHeight( qr.height() + 4 ); + return fromScreen( qr ); +} + +void KigPainter::setColor( const QColor& c ) +{ + color = c; + mP.setPen( QPen( color, width == -1 ? 1 : width, style ) ); +} + +void KigPainter::setStyle( const PenStyle c ) +{ + style = c; + mP.setPen( QPen( color, width == -1 ? 1 : width, style ) ); +} + +void KigPainter::setWidth( const int c ) +{ + width = c; + if (c > 0) overlayenlarge = c - 1; + mP.setPen( QPen( color, width == -1 ? 1 : width, style ) ); +} + +void KigPainter::setPointStyle( const int p ) +{ + pointstyle = p; +} + +void KigPainter::setPen( const QPen& p ) +{ + color = p.color(); + width = p.width(); + style = p.style(); + mP.setPen(p); +} + +void KigPainter::setBrush( const QBrush& b ) +{ + brushStyle = b.style(); + brushColor = b.color(); + mP.setBrush( b ); +} + +void KigPainter::setBrushStyle( const BrushStyle c ) +{ + brushStyle = c; + mP.setBrush( QBrush( brushColor, brushStyle ) ); +} + +void KigPainter::setBrushColor( const QColor& c ) +{ + brushColor = c; + mP.setBrush( QBrush( brushColor, brushStyle ) ); +} + +bool KigPainter::getNightVision( ) const +{ + return mdoc.getNightVision(); +} + +QColor KigPainter::getColor() const +{ + return color; +} + +/* +static void setContains( QRect& r, const QPoint& p ) +{ + if ( r.left() > p.x() ) r.setLeft( p.x() ); + if ( r.right() < p.x() ) r.setRight( p.x() ); + // this is correct, i think. In qt the bottom has the highest y + // coord... + if ( r.bottom() > p.y() ) r.setBottom( p.y() ); + if ( r.top() < p.y() ) r.setTop( p.y() ); +} +*/ + +void KigPainter::drawPolygon( const std::vector<QPoint>& pts, + bool winding, int index, int npoints ) +{ + QPen oldpen = mP.pen(); + QBrush oldbrush = mP.brush(); + setBrush( QBrush( color, Dense4Pattern ) ); + setPen( Qt::NoPen ); + // i know this isn't really fast, but i blame it all on Qt with its + // stupid container classes... what's wrong with the STL ? + QPointArray t( pts.size() ); + int c = 0; + for( std::vector<QPoint>::const_iterator i = pts.begin(); i != pts.end(); ++i ) + { + t.putPoints( c++, 1, i->x(), i->y() ); + }; + mP.drawPolygon( t, winding, index, npoints ); + setPen( oldpen ); + setBrush( oldbrush ); + if( mNeedOverlay ) mOverlay.push_back( t.boundingRect() ); +} + +void KigPainter::drawArea( const std::vector<Coordinate>& pts, bool border ) +{ + QPen oldpen = mP.pen(); + QBrush oldbrush = mP.brush(); + setBrush( QBrush( color, SolidPattern ) ); + if ( border ) + setPen( QPen( color, width == -1 ? 1 : width ) ); + else + setPen( Qt::NoPen ); + QPointArray t( pts.size() ); + int c = 0; + for( std::vector<Coordinate>::const_iterator i = pts.begin(); i != pts.end(); ++i ) + { + QPoint p = toScreen( *i ); + t.putPoints( c++, 1, p.x(), p.y() ); + } + mP.drawPolygon( t ); + setPen( oldpen ); + setBrush( oldbrush ); + if( mNeedOverlay ) mOverlay.push_back( t.boundingRect() ); +} + +Rect KigPainter::window() +{ + return msi.shownRect(); +} + +void KigPainter::circleOverlayRecurse( const Coordinate& centre, + double radiussq, + const Rect& cr ) +{ + Rect currentRect = cr.normalized(); + + if( !currentRect.intersects( window() ) ) return; + + // this code is an adaptation of Marc Bartsch's code, from KGeo + Coordinate tl = currentRect.topLeft(); + Coordinate br = currentRect.bottomRight(); + Coordinate tr = currentRect.topRight(); + Coordinate bl = currentRect.bottomLeft(); + Coordinate c = currentRect.center(); + + // mp: we compute the minimum and maximum distance from the center + // of the circle and this rect + double distxmin = 0, distxmax = 0, distymin = 0, distymax = 0; + if ( centre.x >= tr.x ) distxmin = centre.x - tr.x; + if ( centre.x <= bl.x ) distxmin = bl.x - centre.x; + if ( centre.y >= tr.y ) distymin = centre.y - tr.y; + if ( centre.y <= bl.y ) distymin = bl.y - centre.y; + distxmax = fabs(centre.x - c.x) + currentRect.width()/2; + distymax = fabs(centre.y - c.y) + currentRect.height()/2; + // this should take into account the thickness of the line... + distxmin -= pixelWidth(); + if (distxmin < 0) distxmin = 0; + distxmax += pixelWidth(); + distymin -= pixelWidth(); + if (distymin < 0) distymin = 0; + distymax += pixelWidth(); + double distminsq = distxmin*distxmin + distymin*distymin; + double distmaxsq = distxmax*distxmax + distymax*distymax; + + // if the circle doesn't touch this rect, we return + // too far from the centre + if (distminsq > radiussq) return; + + // too near to the centre + if (distmaxsq < radiussq) return; + + // the rect contains some of the circle + // -> if it's small enough, we keep it + if( currentRect.width() < overlayRectSize() ) { + mOverlay.push_back( toScreenEnlarge( currentRect) ); + } else { + // this func works recursive: we subdivide the current rect, and if + // it is of a good size, we keep it, otherwise we handle it again + double width = currentRect.width() / 2; + double height = currentRect.height() / 2; + Rect r1 ( c, -width, -height); + r1.normalize(); + circleOverlayRecurse(centre, radiussq, r1); + Rect r2 ( c, width, -height); + r2.normalize(); + circleOverlayRecurse(centre, radiussq, r2); + Rect r3 ( c, -width, height); + r3.normalize(); + circleOverlayRecurse(centre, radiussq, r3); + Rect r4 ( c, width, height); + r4.normalize(); + circleOverlayRecurse(centre, radiussq, r4); + }; +} + +void KigPainter::circleOverlay( const Coordinate& centre, double radius ) +{ + double t = radius + pixelWidth(); + Coordinate r( t, t ); + Coordinate bottomLeft = centre - r; + Coordinate topRight = centre + r; + Rect rect( bottomLeft, topRight ); + circleOverlayRecurse ( centre , radius*radius, rect ); +} + +void KigPainter::segmentOverlay( const Coordinate& p1, const Coordinate& p2 ) +{ + // this code is based upon what Marc Bartsch wrote for KGeo + + // some stuff we may need: + Coordinate p3 = p2 - p1; + Rect border = window(); +// double length = p3.length(); + // mp: using the l-infinity distance is more natural here + double length = fabs(p3.x); + if ( fabs( p3.y ) > length ) length = fabs( p3.y ); + if ( length < pixelWidth() ) + { + // hopefully prevent SIGZERO's + mOverlay.push_back( toScreen( Rect( p1, p2 ) ) ); + return; + }; + p3 *= overlayRectSize(); + p3 /= length; + + int counter = 0; + + Rect r(p1, p2); + r.normalize(); + + for (;;) { + Rect tR( Coordinate( 0, 0 ), overlayRectSize(), overlayRectSize() ); + Coordinate tP = p1+p3*counter; + tR.setCenter(tP); + if (!tR.intersects(r)) + { + //kdDebug()<< "stopped after "<< counter << " passes." << endl; + break; + } + if (tR.intersects(border)) mOverlay.push_back( toScreenEnlarge( tR ) ); + if (++counter > 100) + { + kdDebug()<< k_funcinfo << "counter got too big :( " << endl; + break; + } + } +} + +double KigPainter::overlayRectSize() +{ + return 20 * pixelWidth(); +} + +void KigPainter::pointOverlay( const Coordinate& p1 ) +{ + Rect r( p1, 3*pixelWidth(), 3*pixelWidth()); + r.setCenter( p1 ); + mOverlay.push_back( toScreen( r) ); +} + +double KigPainter::pixelWidth() +{ + return msi.pixelWidth(); +} + +void KigPainter::setWholeWinOverlay() +{ + mOverlay.clear(); + mOverlay.push_back( mP.viewport() ); + // don't accept any more overlay's... + mNeedOverlay = false; +} + +QPoint KigPainter::toScreen( const Coordinate p ) const +{ + return msi.toScreen( p ); +} + +void KigPainter::drawGrid( const CoordinateSystem& c, bool showGrid, bool showAxes ) +{ + c.drawGrid( *this, showGrid, showAxes ); + setWholeWinOverlay(); +} + +void KigPainter::drawObject( const ObjectHolder* o, bool ss ) +{ + o->draw( *this, ss ); +} + +void KigPainter::drawObjects( const std::vector<ObjectHolder*>& os, bool sel ) +{ + drawObjects( os.begin(), os.end(), sel ); +} + +void KigPainter::drawFilledRect( const QRect& r ) +{ + QPen pen( Qt::black, 1, Qt::DotLine ); + setPen( pen ); + setBrush( QBrush( Qt::cyan, Dense6Pattern ) ); + drawRect( r.normalize() ); +} + +void KigPainter::drawTextStd( const QPoint& p, const QString& s ) +{ + if ( s.isNull() ) return; + // tf = text formatting flags + int tf = AlignLeft | AlignTop | DontClip | WordBreak; + // we need the rect where we're going to paint text + setPen(QPen(Qt::blue, 1, SolidLine)); + setBrush(Qt::NoBrush); + drawText( Rect( + msi.fromScreen(p), window().bottomRight() + ).normalized(), s, tf ); + +} + +QRect KigPainter::toScreen( const Rect r ) const +{ + return msi.toScreen( r ); +} + +QRect KigPainter::toScreenEnlarge( const Rect r ) const +{ + if ( overlayenlarge == 0 ) return msi.toScreen( r ); + + QRect qr = msi.toScreen( r ); + qr.moveBy ( -overlayenlarge, -overlayenlarge ); + int w = qr.width(); + int h = qr.height(); + qr.setWidth (w + 2*overlayenlarge); + qr.setHeight (h + 2*overlayenlarge); + return qr; +} + +void KigPainter::drawSimpleText( const Coordinate& c, const QString s ) +{ + int tf = AlignLeft | AlignTop | DontClip | WordBreak; + drawText( c, s, tf); +} + +void KigPainter::drawText( const Coordinate p, const QString s, + int textFlags, int len ) +{ + drawText( Rect( p, mP.window().right(), mP.window().top() ), + s, textFlags, len ); +} +const Rect KigPainter::simpleBoundingRect( const Coordinate& c, const QString s ) +{ + int tf = AlignLeft | AlignTop | DontClip | WordBreak; + return boundingRect( c, s, tf ); +} + +const Rect KigPainter::boundingRect( const Coordinate& c, const QString s, + int f, int l ) const +{ + return boundingRect( Rect( c, mP.window().right(), mP.window().top() ), + s, f, l ); +} + +Coordinate KigPainter::fromScreen( const QPoint& p ) const +{ + return msi.fromScreen( p ); +} + +Rect KigPainter::fromScreen( const QRect& r ) const +{ + return msi.fromScreen( r ); +} + +void KigPainter::drawRay( const Coordinate& a, const Coordinate& b ) +{ + Coordinate tb = b; + calcRayBorderPoints( a, tb, window() ); + drawSegment( a, tb ); +} + +typedef std::pair<double,Coordinate> coordparampair; + +struct workitem +{ + workitem( coordparampair f, coordparampair s, Rect *o) : + first(f), second(s), overlay(o) {} + coordparampair first; + coordparampair second; + Rect *overlay; +}; + +void KigPainter::drawLine( const LineData& d ) +{ + if ( d.a != d.b ) + { + LineData l = calcBorderPoints( d, window() ); + drawSegment( l.a, l.b ); + } +} + +void KigPainter::drawSegment( const LineData& d ) +{ + drawSegment( d.a, d.b ); +} + +void KigPainter::drawRay( const LineData& d ) +{ + drawRay( d.a, d.b ); +} + +void KigPainter::drawAngle( const Coordinate& cpoint, const double dstartangle, + const double dangle ) +{ + // convert to 16th of degrees... + const int startangle = static_cast<int>( Goniometry::convert( 16 * dstartangle, Goniometry::Rad, Goniometry::Deg ) ); + const int angle = static_cast<int>( Goniometry::convert( 16 * dangle, Goniometry::Rad, Goniometry::Deg ) ); + + QPoint point = toScreen( cpoint ); + +// int radius = mP.window().width() / 5; + int radius = 50; + QRect surroundingRect( 0, 0, radius*2, radius*2 ); + surroundingRect.moveCenter( point ); + + mP.drawArc( surroundingRect, startangle, angle ); + + // now for the arrow... + QPoint end( static_cast<int>( point.x() + radius * cos( dstartangle + dangle ) ), + static_cast<int>( point.y() - radius * sin( dstartangle + dangle ) ) ); + QPoint vect = (end - point); + double vectlen = sqrt( float( vect.x() * vect.x() + vect.y() * vect.y() ) ); + QPoint orthvect( -vect.y(), vect.x() ); + vect = vect * 6 / vectlen; + orthvect = orthvect * 6 / vectlen; + + QPointArray arrow( 3 ); + arrow.setPoint( 0, end ); + arrow.setPoint( 1, end + orthvect + vect ); + arrow.setPoint( 2, end + orthvect - vect ); +// std::vector<QPoint> arrow; +// arrow.push_back( end ); +// arrow.push_back( end + orthvect + vect ); +// arrow.push_back( end + orthvect - vect ); + + setBrushStyle( Qt::SolidPattern ); +// drawPolygon( arrow ); + mP.drawPolygon( arrow, false, 0, -1 ); + +// if ( mNeedOverlay ) mOverlay.push_back( toScreen( r ) ); + setWholeWinOverlay(); //mp: ugly! why not compute a correct overlay? + // mOverlay.push_back( arrow.boundingRect() ); +} + +void KigPainter::drawPolygon( const std::vector<Coordinate>& pts, + bool winding, int index, int npoints ) +{ + using namespace std; + vector<QPoint> points; + for ( uint i = 0; i < pts.size(); ++i ) + points.push_back( toScreen( pts[i] ) ); + drawPolygon( points, winding, index, npoints ); +} + +void KigPainter::drawVector( const Coordinate& a, const Coordinate& b ) +{ + // bugfix... + if ( a == b ) return; + // the segment + drawSegment( a, b ); + // the arrows... + Coordinate dir = b - a; + Coordinate perp( dir.y, -dir.x ); + double length = perp.length(); + perp *= 10* pixelWidth(); + perp /= length; + dir *= 10 * pixelWidth(); + dir /= length; + Coordinate c = b - dir + perp; + Coordinate d = b - dir - perp; + // draw the arrow lines with a normal style + mP.setPen( QPen( color, width == -1 ? 1 : width, Qt::SolidLine ) ); + drawSegment( b, c ); + drawSegment( b, d ); + // setting again the original style + mP.setPen( QPen( color, width == -1 ? 1 : width, style ) ); +} + +/* *** this function is commented out *** +inline Coordinate locusGetCoord( double p, const CurveImp* curve, const ObjectHierarchy& h, + bool& valid, const KigDocument& doc ) +{ + Coordinate pt = curve->getPoint( p, valid, doc ); + if ( ! valid ) return Coordinate(); + PointImp pimp( pt ); + Args args; + args.push_back( &pimp ); + std::vector<ObjectImp*> calced = h.calc( args, doc ); + assert( calced.size() == 1 ); + ObjectImp* o = calced.front(); + Coordinate ret; + if ( o->inherits( ObjectImp::ID_PointImp ) ) + { + valid = true; + ret = static_cast<PointImp*>( o )->coordinate(); + } + else + valid = false; + delete o; + return ret; +}; +*/ + +class CurveImpPointCalcer +{ + const CurveImp* curve; +public: + CurveImpPointCalcer( const CurveImp* c ) + : curve( c ) + { + } + static const double endinterval; + inline const Coordinate getPoint( double param, const KigDocument& d ) const { + return curve->getPoint( param, d ); + } +}; + +const double CurveImpPointCalcer::endinterval = 1.; + +void KigPainter::drawCurve( const CurveImp* curve ) +{ + // we manage our own overlay + bool tNeedOverlay = mNeedOverlay; + mNeedOverlay = false; + + QPen pen = mP.pen(); + + // this stack contains pairs of Coordinates ( parameter intervals ) + // that we still need to process: + std::stack<workitem> workstack; + // mp: this stack contains all the generated overlays: + // the strategy for generating the overlay structure is the same + // recursive-like used to draw the segments: a new rectangle is + // generated whenever the length of a segment becomes lower than + // overlayRectSize(), or if the segment would be drawn anyway + // to avoid strange things from happening we impose that the distance + // in parameter space be less than a threshold before generating + // any overlay. + // + // The third parameter in workitem is a pointer into a stack of + // all generated rectangles (in real coordinate space); if 0 + // there is no rectangles associated to that segment yet. + // + // Using the final mOverlay stack would be much more efficient, but + // 1. needs transformations into window space + // 2. would be more difficult to drop rectangles not intersecting + // the window. + std::stack<Rect> overlaystack; + + // mp: the original version in which an initial set of 20 intervals + // were pushed onto the stack is replaced by a single interval and + // by forcing subdivision till h < hmax (with more or less the same + // final result). + // First push the [0,1] interval into the stack: + + Coordinate coo1 = curve->getPoint( 0., mdoc ); + Coordinate coo2 = curve->getPoint( 1., mdoc ); + workstack.push( workitem( + coordparampair( 0., coo1 ), + coordparampair( 1., coo2 ), + 0 ) ); + + // maxlength is the square of the maximum size that we allow + // between two points.. + double maxlength = 1.5 * pixelWidth(); + maxlength *= maxlength; + // error squared is required to be less that sigma (half pixel) + double sigma = maxlength/4; + // distance between two parameter values cannot be too small + double hmin = 3e-5; + // distance between two parameter values cannot be too large + double hmax = 1./40; + double hmaxoverlay = 1./8; + + int count = 1; // the number of segments we've already + // visited... + static const int maxnumberofpoints = 1000; + + const Rect& sr = window(); + + // what this algorithm does is approximating the curve with a set of + // segments. we don't draw the individual segments, but use + // QPainter::drawPolyline() so that the line styles work properly. + // Possibly there are performance advantages as well ? this array + // is a buffer of the polyline approximation of the part of the + // curve that we are currently processing. + QPointArray curpolyline( 1000 ); + int curpolylinenextfree = 0; + + // we don't use recursion, but a stack based approach for efficiency + // concerns... + while ( ! workstack.empty() && count < maxnumberofpoints ) + { + workitem curitem = workstack.top(); + workstack.pop(); + bool curitemok = true; + while ( curitemok && count++ < maxnumberofpoints ) + { + double t0 = curitem.first.first; + double t1 = curitem.second.first; + Coordinate p0 = curitem.first.second; + bool valid0 = p0.valid(); + Coordinate p1 = curitem.second.second; + bool valid1 = p1.valid(); + + // we take the middle parameter of the two previous points... + double t2 = ( t0 + t1 ) / 2; + double h = fabs( t1 - t0 ) /2; + + // if exactly one of the two endpoints is invalid, then + // we prefer to find an internal value of the parameter + // separating valid points from invalid points. We use + // a bisection strategy (this is not implemented yet!) +// if ( ( valid0 && ! valid1 ) || ( valid1 && ! valid0 ) ) +// { +// while ( h >= hmin ) +// { +// ....................................... +// } +// } + + Rect *overlaypt = curitem.overlay; + Coordinate p2 = curve->getPoint( t2, mdoc ); + bool allvalid = p2.valid() && valid0 && valid1; + bool dooverlay = ! overlaypt && h < hmaxoverlay && valid0 && valid1 + && fabs( p0.x - p1.x ) <= overlayRectSize() + && fabs( p0.y - p1.y ) <= overlayRectSize(); + bool addn = sr.contains( p2 ) || h >= hmax; + // estimated error between the curve and the segments + double errsq = 1e21; + if ( allvalid ) errsq = (0.5*p0 + 0.5*p1 - p2).squareLength(); + errsq /= 4; + curitemok = false; +// bool dodraw = allvalid && h < hmax && ( errsq < sigma || h < hmin ); + bool dodraw = allvalid && h < hmax && errsq < sigma; + if ( tNeedOverlay && ( dooverlay || dodraw ) ) + { + Rect newoverlay( p0, p1 ); + overlaystack.push( newoverlay ); + overlaypt = &overlaystack.top(); + } + if ( overlaypt ) overlaypt->setContains( p2 ); + if ( dodraw ) + { + // draw the two segments + QPoint tp0 = toScreen(p0); + QPoint tp1 = toScreen(p1); + QPoint tp2 = toScreen(p2); + if ( curpolylinenextfree > 0 && curpolyline[curpolylinenextfree - 1] != tp1 ) + { + // flush the current part of the curve + mP.drawPolyline( curpolyline, 0, curpolylinenextfree ); + curpolylinenextfree = 0; + } + if ( curpolylinenextfree == 0 ) + curpolyline[curpolylinenextfree++] = tp1; + curpolyline[curpolylinenextfree++] = tp2; + curpolyline[curpolylinenextfree++] = tp0; + } + else if ( h >= hmin ) // we do not continue to subdivide indefinitely! + { + // push into stack in order to process both subintervals + if ( addn || ( valid0 && sr.contains( p0 ) ) ) + workstack.push( workitem( curitem.first, coordparampair( t2, p2 ), + overlaypt ) ); + if ( addn || ( valid1 && sr.contains( p1 ) ) ) + { + curitem = workitem( coordparampair( t2, p2 ), curitem.second , + overlaypt ); + curitemok = true; + } + } + } + } + // flush the rest of the curve + mP.drawPolyline( curpolyline, 0, curpolylinenextfree ); + curpolylinenextfree = 0; + + if ( ! workstack.empty () ) + kdDebug() << "Stack not empty in KigPainter::drawCurve!\n" << endl; + assert ( tNeedOverlay || overlaystack.empty() ); + if ( tNeedOverlay ) + { + Rect border = window(); + while ( ! overlaystack.empty() ) + { + Rect overlay = overlaystack.top(); + overlaystack.pop(); + if (overlay.intersects( border )) + mOverlay.push_back( toScreenEnlarge( overlay ) ); + } + } + mNeedOverlay = tNeedOverlay; +} + +void KigPainter::drawTextFrame( const Rect& frame, + const QString& s, bool needframe ) +{ + QPen oldpen = mP.pen(); + QBrush oldbrush = mP.brush(); + if ( needframe ) + { + // inspired upon kgeo, thanks to Marc Bartsch, i've taken some of + // his code too.. + setPen( QPen( Qt::black, 1 ) ); + setBrush( QBrush( QColor( 255, 255, 222 ) ) ); + drawRect( frame ); + setPen( QPen( QColor( 197, 194, 197 ), 1, Qt::SolidLine ) ); + + QRect qr = toScreen( frame ); + + mP.drawLine( qr.topLeft(), qr.topRight() ); + mP.drawLine( qr.topLeft(), qr.bottomLeft() ); + }; + setPen( oldpen ); + setBrush( oldbrush ); + drawText( frame, s, Qt::AlignVCenter | Qt::AlignLeft ); +} + +void KigPainter::drawArc( const Coordinate& center, const double radius, + const double dstartangle, const double dangle ) +{ + // convert to 16th of degrees... + const int startangle = static_cast<int>( Goniometry::convert( 16 * dstartangle, Goniometry::Rad, Goniometry::Deg ) ); + const int angle = static_cast<int>( Goniometry::convert( 16 * dangle, Goniometry::Rad, Goniometry::Deg ) ); + + if ( angle <= 16 ) + { + Coordinate a = center + radius * Coordinate( cos( dstartangle ), sin( dstartangle )); + Coordinate b = center + radius * Coordinate( cos( dstartangle + dangle ), sin( dstartangle + dangle )); + drawSegment ( a , b ); + } + else + { + Rect krect( 0, 0, 2*radius, 2*radius ); + krect.setCenter( center ); + QRect rect = toScreen( krect ); + + mP.drawArc( rect, startangle, angle ); + setWholeWinOverlay(); + } +} + diff --git a/kig/misc/kigpainter.h b/kig/misc/kigpainter.h new file mode 100644 index 00000000..e7f884f4 --- /dev/null +++ b/kig/misc/kigpainter.h @@ -0,0 +1,291 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002-2003 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + + +#ifndef KIGPAINTER_H +#define KIGPAINTER_H + +#include "coordinate.h" +#include "rect.h" +#include "screeninfo.h" + +#include <qpainter.h> +#include <qcolor.h> + +#include <vector> + +class KigWidget; +class QPaintDevice; +class CoordinateSystem; +class ObjectHierarchy; +class ConicPolarData; +class CubicCartesianData; +class LineData; +class CurveImp; +class KigDocument; +class ObjectHolder; + +/** + * KigPainter is an extended QPainter. + * + * Currently the only difference is that it translates coordinates + * from and to the internal coordinates/ the widget coordinates... + * + * It calls KigWidget::appendOverlay() for all of the places it draws in... + */ +class KigPainter + : public Qt +{ +protected: + // don't blaim me for this mutable hack. It's TT that hasn't got + // its consts correctly... + mutable QPainter mP; + + QColor color; + PenStyle style; + int pointstyle; + int width; + BrushStyle brushStyle; + QColor brushColor; + + const KigDocument& mdoc; + ScreenInfo msi; + + bool mNeedOverlay; + int overlayenlarge; +public: + /** + * construct a new KigPainter: + * the ScreenInfo is used to map the document coordinates to the + * widget coordinates. This is done transparently to the objects. + * needOverlay sets whether we try to remember the places we're + * drawing on using the various overlay methods. @see overlay() + */ + KigPainter( const ScreenInfo& r, QPaintDevice* device, const KigDocument& doc, + bool needOverlay = true ); + ~KigPainter(); + + /** + * what rect are we drawing on ? + */ + Rect window(); + + QPoint toScreen( const Coordinate p ) const; + QRect toScreen( const Rect r ) const; + QRect toScreenEnlarge( const Rect r ) const; + Coordinate fromScreen( const QPoint& p ) const; + Rect fromScreen( const QRect& r ) const; + + // colors and stuff... + void setStyle( const PenStyle c ); + void setColor( const QColor& c ); + /** + * setting this to -1 means to use the default width for the object + * being drawn.. a point -> 5, other objects -> 1 + */ + void setWidth( const int c ); + void setPointStyle( const int p ); + void setPen( const QPen& p ); + void setBrushStyle( const BrushStyle c ); + void setBrush( const QBrush& b ); + void setBrushColor( const QColor& c ); + + QColor getColor() const; + bool getNightVision( ) const; + + double pixelWidth(); + + /** + * this is called by some drawing functions that modify the 'entire' + * screen, i.e. they do so many changes that it's easier to just + * update the entire screen, or else i have been to lazy to + * implement an appropriate overlay function ;) + * it clears mOverlay, and sets it to the entire widget... + */ + void setWholeWinOverlay(); + + /** + * draw an object ( by calling its draw function.. ) + */ + void drawObject( const ObjectHolder* o, bool sel ); + void drawObjects( const std::vector<ObjectHolder*>& os, bool sel ); + template<typename iter> + void drawObjects( iter begin, iter end, bool sel ) + { + for ( ; begin != end; ++begin ) + drawObject( *begin, sel ); + } + + /** + * draw a generic curve... + */ + void drawCurve( const CurveImp* curve ); + + /** + * draws text in a standard manner, convenience function... + */ + void drawTextStd( const QPoint& p, const QString& s ); + + /** + * draws a rect filled up with a pattern of cyan lines... + */ + void drawFilledRect( const QRect& ); + + /** + * draw a rect.. + */ + void drawRect( const Rect& r ); + + /** + * overload, mainly for drawing the selection rectangle by + * KigWidget... + */ + void drawRect( const QRect& r ); + + /** + * draw a circle... + */ + void drawCircle( const Coordinate& center, const double radius ); + + /** + * draw a segment... + */ + void drawSegment ( const Coordinate& from, const Coordinate& to ); + void drawSegment( const LineData& d ); + + /** + * draw a ray... + */ + void drawRay( const Coordinate& a, const Coordinate& b ); + void drawRay( const LineData& d ); + + /** + * draw a line... + */ + void drawLine ( const Coordinate& p1, const Coordinate& p2 ); + void drawLine( const LineData& d ); + + /** + * draw a point... This means a single point, as in + * QPainter::drawPoint(), unlike drawFatPoint()... + */ + void drawPoint( const Coordinate& p ); + + /** + * draw a thick point.. This is what the user sees when he draws a + * point. In fact it isn't a point, but a filled circle of a + * certain radius... + */ + void drawFatPoint( const Coordinate& p ); + + /** + * draw a polygon defined by the points in pts... + */ + void drawPolygon( const std::vector<QPoint>& pts, bool winding = false, int index = 0, int npoints = -1 ); + void drawPolygon( const std::vector<Coordinate>& pts, bool winding = false, int index = 0, int npoints = -1 ); + + /** + * draw an area defined by the points in pts filled with the set + * color... + */ + void drawArea( const std::vector<Coordinate>& pts, bool border = true ); + + /** + * draw the angle with center point, with size angle, starting + * at the angle startAngle.. Angles should be in radians. + */ + void drawAngle( const Coordinate& point, const double startangle, + const double angle ); + + /** + * draw the arc ( a part of a circle ), of the circle with center + * center, radius radius, with size angle, starting at the angle + * startAngle.. Angles should be in radians.. + */ + void drawArc( const Coordinate& center, const double radius, + const double startangle, const double angle ); + + /** + * draw a vector ( with an arrow etc. ) + */ + + void drawVector( const Coordinate& a, const Coordinate& b ); + + /** + * draw text... + * \see QPainter::drawText() + */ + void drawText( const Rect r, const QString s, int textFlags = 0, + int len = -1); + void drawText( const Coordinate p, const QString s, + int textFlags = 0, int len = -1); + + void drawSimpleText( const Coordinate& c, const QString s ); + void drawTextFrame( const Rect& frame, const QString& s, bool needframe ); + + const Rect boundingRect( const Rect& r, const QString s, + int f = 0, int l = -1 ) const; + + const Rect boundingRect( const Coordinate& c, const QString s, + int f = 0, int l = -1 ) const; + + const Rect simpleBoundingRect( const Coordinate& c, const QString s ); + + void drawGrid( const CoordinateSystem& c, bool showGrid = true, bool showAxes = true ); + + const std::vector<QRect>& overlay() { return mOverlay; } + +protected: + /** + * adds a number of rects to mOverlay so that the rects entirely + * contain the circle... + * \see mOverlay + */ + void circleOverlay( const Coordinate& centre, double radius ); + // this works recursively... + void circleOverlayRecurse( const Coordinate& centre, double radius, const Rect& currentRect ); + + /** + * adds some rects to mOverlay, so that they cover the segment p1p2 + * completely... + * \see Object::getOverlay() + */ + void segmentOverlay( const Coordinate& p1, const Coordinate& p2 ); + + /** + * ... + */ + void pointOverlay( const Coordinate& p1 ); + + /** + * ... + * \see drawText(), QPainter::boundingRect() + */ + void textOverlay( const QRect& r, const QString s, int textFlags, int len ); + + /** + * the size we want the overlay rects to be... + */ + double overlayRectSize(); + + std::vector<QRect> mOverlay; +}; + +#endif diff --git a/kig/misc/kigtransform.cpp b/kig/misc/kigtransform.cpp new file mode 100644 index 00000000..a297ed6e --- /dev/null +++ b/kig/misc/kigtransform.cpp @@ -0,0 +1,810 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Maurizio Paolini <paolini@dmf.unicatt.it> + Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#include "kigtransform.h" + +#include "kignumerics.h" +#include "common.h" + +#include <cmath> + +#include <klocale.h> +#include <kdebug.h> + +// Transformation getProjectiveTransformation ( int argsnum, +// Object *transforms[], bool& valid ) +// { +// valid = true; + +// assert ( argsnum > 0 ); +// int argn = 0; +// Object* transform = transforms[argn++]; +// if (transform->toVector()) +// { +// // translation +// assert (argn == argsnum); +// Vector* v = transform->toVector(); +// Coordinate dir = v->getDir(); +// return Transformation::translation( dir ); +// } + +// if (transform->toPoint()) +// { +// // point reflection ( or is point symmetry the correct term ? ) +// assert (argn == argsnum); +// Point* p = transform->toPoint(); +// return Transformation::pointReflection( p->getCoord() ); +// } + +// if (transform->toLine()) +// { +// // line reflection ( or is it line symmetry ? ) +// Line* line = transform->toLine(); +// assert (argn == argsnum); +// return Transformation::lineReflection( line->lineData() ); +// } + +// if (transform->toRay()) +// { +// // domi: sorry, but what kind of transformation does this do ? +// // i'm guessing it's some sort of rotation, but i'm not +// // really sure.. +// Ray* line = transform->toRay(); +// Coordinate d = line->direction().normalize(); +// Coordinate t = line->p1(); +// double alpha = 0.1*M_PI/2; // a small angle for the DrawPrelim +// if (argn < argsnum) +// { +// Angle* angle = transforms[argn++]->toAngle(); +// alpha = angle->size(); +// } +// assert (argn == argsnum); +// return Transformation::projectiveRotation( alpha, d, t ); +// } + +// if (transform->toAngle()) +// { +// // rotation.. +// Coordinate center = Coordinate( 0., 0. ); +// if (argn < argsnum) +// { +// Object* arg = transforms[argn++]; +// assert (arg->toPoint()); +// center = arg->toPoint()->getCoord(); +// } +// Angle* angle = transform->toAngle(); +// double alpha = angle->size(); + +// assert (argn == argsnum); + +// return Transformation::rotation( alpha, center ); +// } + +// if (transform->toSegment()) // this is a scaling +// { +// Segment* segment = transform->toSegment(); +// Coordinate p = segment->p2() - segment->p1(); +// double s = p.length(); +// if (argn < argsnum) +// { +// Object* arg = transforms[argn++]; +// if (arg->toSegment()) // s is the length of the first segment +// // divided by the length of the second.. +// { +// Segment* segment = arg->toSegment(); +// Coordinate p = segment->p2() - segment->p1(); +// s /= p.length(); +// if (argn < argsnum) arg = transforms[argn++]; +// } +// if (arg->toPoint()) // scaling w.r. to a point +// { +// Point* p = arg->toPoint(); +// assert (argn == argsnum); +// return Transformation::scaling( s, p->getCoord() ); +// } +// if (arg->toLine()) // scaling w.r. to a line +// { +// Line* line = arg->toLine(); +// assert( argn == argsnum ); +// return Transformation::scaling( s, line->lineData() ); +// } +// } + +// return Transformation::scaling( s, Coordinate( 0., 0. ) ); +// } + +// valid = false; +// return Transformation::identity(); +// } + +// tWantArgsResult WantTransformation ( Objects::const_iterator& i, +// const Objects& os ) +// { +// Object* o = *i++; +// if (o->toVector()) return tComplete; +// if (o->toPoint()) return tComplete; +// if (o->toLine()) return tComplete; +// if (o->toAngle()) +// { +// if ( i == os.end() ) return tNotComplete; +// o = *i++; +// if (o->toPoint()) return tComplete; +// if (o->toLine()) return tComplete; +// return tNotGood; +// } +// if (o->toRay()) +// { +// if ( i == os.end() ) return tNotComplete; +// o = *i++; +// if (o->toAngle()) return tComplete; +// return tNotGood; +// } +// if (o->toSegment()) +// { +// if ( i == os.end() ) return tNotComplete; +// o = *i++; +// if ( o->toSegment() ) +// { +// if ( i == os.end() ) return tNotComplete; +// o = *i++; +// } +// if (o->toPoint()) return tComplete; +// if (o->toLine()) return tComplete; +// return tNotGood; +// } +// return tNotGood; +// } + +// QString getTransformMessage ( const Objects& os, const Object *o ) +// { +// int size = os.size(); +// switch (size) +// { +// case 1: +// if (o->toVector()) return i18n("translate by this vector"); +// if (o->toPoint()) return i18n("central symmetry by this point. You" +// " can obtain different transformations by clicking on lines (mirror)," +// " vectors (translation), angles (rotation), segments (scaling) and rays" +// " (projective transformation)"); +// if (o->toLine()) return i18n("reflect in this line"); +// if (o->toAngle()) return i18n("rotate by this angle"); +// if (o->toSegment()) return i18n("scale using the length of this vector"); +// if (o->toRay()) return i18n("a projective transformation in the direction" +// " indicated by this ray, it is a rotation in the projective plane" +// " about a point at infinity"); +// return i18n("Use this transformation"); + +// case 2: // we ask for the first parameter of the transformation +// case 3: +// if (os[1]->toAngle()) +// { +// if (o->toPoint()) return i18n("about this point"); +// assert (false); +// } +// if (os[1]->toSegment()) +// { +// if (o->toSegment()) +// return i18n("relative to the length of this other vector"); +// if (o->toPoint()) +// return i18n("about this point"); +// if (o->toLine()) +// return i18n("about this line"); +// } +// if (os[1]->toRay()) +// { +// if (o->toAngle()) return i18n("rotate by this angle in the projective" +// " plane"); +// } +// return i18n("Using this object"); + +// default: assert(false); +// } + +// return i18n("Use this transformation"); +// } + + +/* domi: not necessary anymore, homotheticness is kept as a bool in + * the Transformation class.. + * keeping it here, in case a need for it arises some time in the + * future... + * decide if the given transformation is homotetic + */ +// bool isHomoteticTransformation ( double transformation[3][3] ) +// { +// if (transformation[0][1] != 0 || transformation[0][2] != 0) return (false); +// // test the orthogonality of the matrix 2x2 of second and third rows +// // and columns +// if (fabs(fabs(transformation[1][1]) - +// fabs(transformation[2][2])) > 1e-8) return (false); +// if (fabs(fabs(transformation[1][2]) - +// fabs(transformation[2][1])) > 1e-8) return (false); + +// return transformation[1][2] * transformation[2][1] * +// transformation[1][1] * transformation[2][2] <= 0.; +// } + +const Transformation Transformation::identity() +{ + Transformation ret; + for ( int i = 0; i < 3; ++i ) + for ( int j = 0; j < 3; ++j ) + ret.mdata[i][j] = ( i == j ? 1 : 0 ); + ret.mIsHomothety = ret.mIsAffine = true; + return ret; +} + +const Transformation Transformation::scalingOverPoint( double factor, const Coordinate& center ) +{ + Transformation ret; + for ( int i = 0; i < 3; ++i ) + for ( int j = 0; j < 3; ++j ) + ret.mdata[i][j] = ( i == j ? factor : 0 ); + ret.mdata[0][0] = 1; + ret.mdata[1][0] = center.x - factor * center.x; + ret.mdata[2][0] = center.y - factor * center.y; + ret.mIsHomothety = ret.mIsAffine = true; + return ret; +} + +const Transformation Transformation::translation( const Coordinate& c ) +{ + Transformation ret = identity(); + ret.mdata[1][0] = c.x; + ret.mdata[2][0] = c.y; + + // this is already set in the identity() constructor, but just for + // clarity.. + ret.mIsHomothety = ret.mIsAffine = true; + return ret; +} + +const Transformation Transformation::pointReflection( const Coordinate& c ) +{ + Transformation ret = scalingOverPoint( -1, c ); + ret.mIsHomothety = ret.mIsAffine = true; + return ret; +} + +const Transformation operator*( const Transformation& a, const Transformation& b ) +{ + // just multiply the two matrices.. + Transformation ret; + + for ( int i = 0; i < 3; ++i ) + for ( int j = 0; j < 3; ++j ) + { + ret.mdata[i][j] = 0; + for ( int k = 0; k < 3; ++k ) + ret.mdata[i][j] += a.mdata[i][k] * b.mdata[k][j]; + }; + + // combination of two homotheties is a homothety.. + + ret.mIsHomothety = a.mIsHomothety && b.mIsHomothety; + + // combination of two affinities is affine.. + + ret.mIsAffine = a.mIsAffine && b.mIsAffine; + + return ret; +} + +const Transformation Transformation::lineReflection( const LineData& l ) +{ + Transformation ret = scalingOverLine( -1, l ); + // a reflection is a homothety... + ret.mIsHomothety = ret.mIsAffine = true; + return ret; +} + +const Transformation Transformation::scalingOverLine( double factor, const LineData& l ) +{ + Transformation ret = identity(); + + Coordinate a = l.a; + Coordinate d = l.dir(); + double dirnormsq = d.squareLength(); + ret.mdata[1][1] = (d.x*d.x + factor*d.y*d.y)/dirnormsq; + ret.mdata[2][2] = (d.y*d.y + factor*d.x*d.x)/dirnormsq; + ret.mdata[1][2] = ret.mdata[2][1] = (d.x*d.y - factor*d.x*d.y)/dirnormsq; + + ret.mdata[1][0] = a.x - ret.mdata[1][1]*a.x - ret.mdata[1][2]*a.y; + ret.mdata[2][0] = a.y - ret.mdata[2][1]*a.x - ret.mdata[2][2]*a.y; + + // domi: is 1e-8 a good value ? + ret.mIsHomothety = ( fabs( factor - 1 ) < 1e-8 || fabs ( factor + 1 ) < 1e-8 ); + ret.mIsAffine = true; + return ret; +} + +const Transformation Transformation::harmonicHomology( + const Coordinate& center, const LineData& axis ) +{ + // this is a well known projective transformation. We find it by first + // computing the homogeneous equation of the axis ax + by + cz = 0 + // then a straightforward computation shows that the 3x3 matrix describing + // the transformation is of the form: + // + // (r . C) Id - 2 (C tensor r) + // + // where r = [c, a, b], C = [1, Cx, Cy], Cx and Cy are the coordinates of + // the center, '.' denotes the scalar product, Id is the identity matrix, + // 'tensor' is the tensor product producing a 3x3 matrix. + // + // note: here we decide to use coordinate '0' in place of the third coordinate + // in homogeneous notation; e.g. C = [1, cx, cy] + + Coordinate pointa = axis.a; + Coordinate pointb = axis.b; + + double a = pointa.y - pointb.y; + double b = pointb.x - pointa.x; + double c = pointa.x*pointb.y - pointa.y*pointb.x; + + double cx = center.x; + double cy = center.y; + + double scalprod = a*cx + b*cy + c; + scalprod *= 0.5; + Transformation ret; + + ret.mdata[0][0] = c - scalprod; + ret.mdata[0][1] = a; + ret.mdata[0][2] = b; + + ret.mdata[1][0] = c*cx; + ret.mdata[1][1] = a*cx - scalprod; + ret.mdata[1][2] = b*cx; + + ret.mdata[2][0] = c*cy; + ret.mdata[2][1] = a*cy; + ret.mdata[2][2] = b*cy - scalprod; + + ret.mIsHomothety = ret.mIsAffine = false; + return ret; +} + +const Transformation Transformation::affinityGI3P( + const std::vector<Coordinate>& FromPoints, + const std::vector<Coordinate>& ToPoints, + bool& valid ) +{ + // construct the (generically) unique affinity that transforms 3 given + // point into 3 other given points; i.e. it depends on the coordinates of + // a total of 6 points. This actually amounts in solving a 6x6 linear + // system to find the entries of a 2x2 linear transformation matrix T + // and of a translation vector t. + // If Pi denotes one of the starting points and Qi the corresponding + // final position we actually have to solve: Qi = t + T Pi, for i=1,2,3 + // (each one is a vector equation, so that it really gives 2 equations). + // In our context T and t are used to build a 3x3 projective transformation + // as follows: + // + // [ 1 0 0 ] + // [ t1 T11 T12 ] + // [ t2 T21 T22 ] + // + // In order to take advantage of the two functions "GaussianElimination" + // and "BackwardSubstitution", which are specifically aimed at solving + // homogeneous underdetermined linear systems, we just add a further + // unknown m and solve for t + T Pi - m Qi = 0. Since our functions + // returns a nonzero solution we shall have a nonzero 'm' in the end and + // can build the 3x3 matrix as follows: + // + // [ m 0 0 ] + // [ t1 T11 T12 ] + // [ t2 T21 T22 ] + // + // we order the unknowns as follows: m, t1, t2, T11, T12, T21, T22 + + double row0[7], row1[7], row2[7], row3[7], row4[7], row5[7]; + + double *matrix[6] = {row0, row1, row2, row3, row4, row5}; + + double solution[7]; + int scambio[7]; + + assert (FromPoints.size() == 3); + assert (ToPoints.size() == 3); + + // fill in the matrix elements + for ( int i = 0; i < 6; i++ ) + { + for ( int j = 0; j < 7; j++ ) + { + matrix[i][j] = 0.0; + } + } + + for ( int i = 0; i < 3; i++ ) + { + Coordinate p = FromPoints[i]; + Coordinate q = ToPoints[i]; + matrix[i][0] = -q.x; + matrix[i][1] = 1.0; + matrix[i][3] = p.x; + matrix[i][4] = p.y; + matrix[i+3][0] = -q.y; + matrix[i+3][2] = 1.0; + matrix[i+3][5] = p.x; + matrix[i+3][6] = p.y; + } + + Transformation ret; + valid = true; + if ( ! GaussianElimination( matrix, 6, 7, scambio ) ) + { valid = false; return ret; } + + // fine della fase di eliminazione + BackwardSubstitution( matrix, 6, 7, scambio, solution ); + + // now we can build the 3x3 transformation matrix; remember that + // unknown 0 is the multiplicator 'm' + + ret.mdata[0][0] = solution[0]; + ret.mdata[0][1] = ret.mdata[0][2] = 0.0; + ret.mdata[1][0] = solution[1]; + ret.mdata[2][0] = solution[2]; + ret.mdata[1][1] = solution[3]; + ret.mdata[1][2] = solution[4]; + ret.mdata[2][1] = solution[5]; + ret.mdata[2][2] = solution[6]; + + ret.mIsHomothety = false; + ret.mIsAffine = true; + return ret; +} + +const Transformation Transformation::projectivityGI4P( + const std::vector<Coordinate>& FromPoints, + const std::vector<Coordinate>& ToPoints, + bool& valid ) +{ + // construct the (generically) unique projectivity that transforms 4 given + // point into 4 other given points; i.e. it depends on the coordinates of + // a total of 8 points. This actually amounts in solving an underdetermined + // homogeneous linear system. + + double + row0[13], row1[13], row2[13], row3[13], row4[13], row5[13], row6[13], row7[13], + row8[13], row9[13], row10[13], row11[13]; + + double *matrix[12] = {row0, row1, row2, row3, row4, row5, row6, row7, + row8, row9, row10, row11}; + + double solution[13]; + int scambio[13]; + + assert (FromPoints.size() == 4); + assert (ToPoints.size() == 4); + + // fill in the matrix elements + for ( int i = 0; i < 12; i++ ) + { + for ( int j = 0; j < 13; j++ ) + { + matrix[i][j] = 0.0; + } + } + + for ( int i = 0; i < 4; i++ ) + { + Coordinate p = FromPoints[i]; + Coordinate q = ToPoints[i]; + matrix[i][0] = matrix[4+i][3] = matrix[8+i][6] = 1.0; + matrix[i][1] = matrix[4+i][4] = matrix[8+i][7] = p.x; + matrix[i][2] = matrix[4+i][5] = matrix[8+i][8] = p.y; + matrix[i][9+i] = -1.0; + matrix[4+i][9+i] = -q.x; + matrix[8+i][9+i] = -q.y; + } + + Transformation ret; + valid = true; + if ( ! GaussianElimination( matrix, 12, 13, scambio ) ) + { valid = false; return ret; } + + // fine della fase di eliminazione + BackwardSubstitution( matrix, 12, 13, scambio, solution ); + + // now we can build the 3x3 transformation matrix; remember that + // unknowns from 9 to 13 are just multiplicators that we don't need here + + int k = 0; + for ( int i = 0; i < 3; i++ ) + { + for ( int j = 0; j < 3; j++ ) + { + ret.mdata[i][j] = solution[k++]; + } + } + + ret.mIsHomothety = ret.mIsAffine = false; + return ret; +} + +const Transformation Transformation::castShadow( + const Coordinate& lightsrc, const LineData& l ) +{ + // first deal with the line l, I need to find an appropriate reflection + // that transforms l onto the x-axis + + Coordinate d = l.dir(); + Coordinate a = l.a; + double k = d.length(); + if ( d.x < 0 ) k *= -1; // for numerical stability + Coordinate w = d + Coordinate( k, 0 ); + // w /= w.length(); + // w defines a Householder transformation, but we don't need to normalize + // it here. + // warning: this w is the orthogonal of the w of the textbooks! + // this is fine for us since in this way it indicates the line direction + Coordinate ra = Coordinate ( a.x + w.y*a.y/(2*w.x), a.y/2 ); + Transformation sym = lineReflection ( LineData( ra, ra + w ) ); + + // in the new coordinates the line is the x-axis + // I must transform the point + + Coordinate modlightsrc = sym.apply ( lightsrc ); + Transformation ret = identity(); + // parameter t indicates the distance of the light source from + // the plane of the drawing. A negative value means that the light + // source is behind the plane. + double t = -1.0; + // double t = -modlightsrc.y; <-- this gives the old transformation! + double e = modlightsrc.y - t; + ret.mdata[0][0] = e; + ret.mdata[0][2] = -1; + ret.mdata[1][1] = e; + ret.mdata[1][2] = -modlightsrc.x; + ret.mdata[2][2] = -t; + + ret.mIsHomothety = ret.mIsAffine = false; + return sym*ret*sym; +// return translation( t )*ret*translation( -t ); +} + +const Transformation Transformation::projectiveRotation( + double alpha, const Coordinate& d, const Coordinate& t ) +{ + Transformation ret; + double cosalpha = cos( alpha ); + double sinalpha = sin( alpha ); + ret.mdata[0][0] = cosalpha; + ret.mdata[1][1] = cosalpha*d.x*d.x + d.y*d.y; + ret.mdata[0][1] = -sinalpha*d.x; + ret.mdata[1][0] = sinalpha*d.x; + ret.mdata[0][2] = -sinalpha*d.y; + ret.mdata[2][0] = sinalpha*d.y; + ret.mdata[1][2] = cosalpha*d.x*d.y - d.x*d.y; + ret.mdata[2][1] = cosalpha*d.x*d.y - d.x*d.y; + ret.mdata[2][2] = cosalpha*d.y*d.y + d.x*d.x; + + ret.mIsHomothety = ret.mIsAffine = false; + return translation( t )*ret*translation( -t ); +} + +const Coordinate Transformation::apply( const double x0, + const double x1, + const double x2) const +{ + double phom[3] = {x0, x1, x2}; + double rhom[3] = {0., 0., 0.}; + + + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + { + rhom[i] += mdata[i][j]*phom[j]; + } + } + + if (rhom[0] == 0.) + return Coordinate::invalidCoord(); + + return Coordinate (rhom[1]/rhom[0], rhom[2]/rhom[0]); +} + +const Coordinate Transformation::apply( const Coordinate& p ) const +{ + return apply( 1., p.x, p.y ); +// double phom[3] = {1., p.x, p.y}; +// double rhom[3] = {0., 0., 0.}; +// +// for (int i = 0; i < 3; i++) +// { +// for (int j = 0; j < 3; j++) +// { +// rhom[i] += mdata[i][j]*phom[j]; +// } +// } +// +// if (rhom[0] == 0.) +// return Coordinate::invalidCoord(); +// +// return Coordinate (rhom[1]/rhom[0], rhom[2]/rhom[0]); +} + +const Coordinate Transformation::apply0( const Coordinate& p ) const +{ + return apply( 0., p.x, p.y ); +} + +const Transformation Transformation::rotation( double alpha, const Coordinate& center ) +{ + Transformation ret = identity(); + + double x = center.x; + double y = center.y; + + double cosalpha = cos( alpha ); + double sinalpha = sin( alpha ); + + ret.mdata[1][1] = ret.mdata[2][2] = cosalpha; + ret.mdata[1][2] = -sinalpha; + ret.mdata[2][1] = sinalpha; + ret.mdata[1][0] = x - ret.mdata[1][1]*x - ret.mdata[1][2]*y; + ret.mdata[2][0] = y - ret.mdata[2][1]*x - ret.mdata[2][2]*y; + + // this is already set in the identity() constructor, but just for + // clarity.. + ret.mIsHomothety = ret.mIsAffine = true; + + return ret; +} + +bool Transformation::isHomothetic() const +{ + return mIsHomothety; +} + +bool Transformation::isAffine() const +{ + return mIsAffine; +} + +/* + *mp: + * this function has the property that it changes sign if computed + * on two points that lie on either sides with respect to the critical + * line (this is the line that goes to the line at infinity). + * For affine transformations the result has always the same sign. + * NOTE: the result is *not* invariant under rescaling of all elements + * of the transformation matrix. + * The typical use is to determine whether a segment is transformed + * into a segment or a couple of half-lines. + */ + +double Transformation::getProjectiveIndicator( const Coordinate& c ) const +{ + return mdata[0][0] + mdata[0][1]*c.x + mdata[0][2]*c.y; +} + +// assuming that this is an affine transformation, return its +// determinant. What is really important here is just the sign +// of the determinant. +double Transformation::getAffineDeterminant() const +{ + return mdata[1][1]*mdata[2][2] - mdata[1][2]*mdata[2][1]; +} + +// this assumes that the 2x2 affine part of the matrix is of the +// form [ cos a, sin a; -sin a, cos a] or a multiple +double Transformation::getRotationAngle() const +{ + return atan2( mdata[1][2], mdata[1][1] ); +} + +const Coordinate Transformation::apply2by2only( const Coordinate& p ) const +{ + double x = p.x; + double y = p.y; + double nx = mdata[1][1]*x + mdata[1][2]*y; + double ny = mdata[2][1]*x + mdata[2][2]*y; + return Coordinate( nx, ny ); +} + +double Transformation::data( int r, int c ) const +{ + return mdata[r][c]; +} + +const Transformation Transformation::inverse( bool& valid ) const +{ + Transformation ret; + + valid = Invert3by3matrix( mdata, ret.mdata ); + + // the inverse of a homothety is a homothety, same for affinities.. + ret.mIsHomothety = mIsHomothety; + ret.mIsAffine = mIsAffine; + + return ret; +} + +Transformation::Transformation() +{ + // this is the constructor used by the static Transformation + // creation functions, so mIsHomothety is in general false + mIsHomothety = mIsAffine = false; + for ( int i = 0; i < 3; ++i ) + for ( int j = 0; j < 3; ++j ) + mdata[i][j] = ( i == j ) ? 1 : 0; +} + +Transformation::~Transformation() +{ +} + +double Transformation::apply( double length ) const +{ + assert( isHomothetic() ); + double det = mdata[1][1]*mdata[2][2] - + mdata[1][2]*mdata[2][1]; + return sqrt( fabs( det ) ) * length; +} + +Transformation::Transformation( double data[3][3], bool ishomothety ) + : mIsHomothety( ishomothety ) +{ + for ( int i = 0; i < 3; ++i ) + for ( int j = 0; j < 3; ++j ) + mdata[i][j] = data[i][j]; + + //mp: a test for affinity is used to initialize mIsAffine... + mIsAffine = false; + if ( fabs(mdata[0][1]) + fabs(mdata[0][2]) < 1e-8 * fabs(mdata[0][0]) ) + mIsAffine = true; +} + +bool operator==( const Transformation& lhs, const Transformation& rhs ) +{ + for ( int i = 0; i < 3; ++i ) + for ( int j = 0; j < 3; ++j ) + if ( lhs.data( i, j ) != rhs.data( i, j ) ) + return false; + return true; +} + +const Transformation Transformation::similitude( + const Coordinate& center, double theta, double factor ) +{ + //kdDebug() << k_funcinfo << "theta: " << theta << " factor: " << factor << endl; + Transformation ret; + ret.mIsHomothety = true; + double costheta = cos( theta ); + double sintheta = sin( theta ); + ret.mdata[0][0] = 1; + ret.mdata[0][1] = 0; + ret.mdata[0][2] = 0; + ret.mdata[1][0] = ( 1 - factor*costheta )*center.x + factor*sintheta*center.y; + ret.mdata[1][1] = factor*costheta; + ret.mdata[1][2] = -factor*sintheta; + ret.mdata[2][0] = -factor*sintheta*center.x + ( 1 - factor*costheta )*center.y; + ret.mdata[2][1] = factor*sintheta; + ret.mdata[2][2] = factor*costheta; + // fails for factor == infinity + //assert( ( ret.apply( center ) - center ).length() < 1e-5 ); + ret.mIsHomothety = ret.mIsAffine = true; + return ret; +} diff --git a/kig/misc/kigtransform.h b/kig/misc/kigtransform.h new file mode 100644 index 00000000..252a0f72 --- /dev/null +++ b/kig/misc/kigtransform.h @@ -0,0 +1,190 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Maurizio Paolini <paolini@dmf.unicatt.it> + Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#ifndef KIG_MISC_KIGTRANSFORM_H +#define KIG_MISC_KIGTRANSFORM_H + +#include "coordinate.h" +#include <vector> + +class LineData; + +/** + * Class representing a transformation. More specifically, this class + * represents a pretty generic 2-dimensional transformation. Various + * common transformations can be used. Construct a Transformation by + * using one of its static members, and use it either with its + * Transformation::apply method, or the ObjectImp::transform method. + */ +class Transformation +{ + double mdata[3][3]; + bool mIsHomothety; + bool mIsAffine; + Transformation(); +public: + ~Transformation(); + Transformation( double data[3][3], bool ishomothety ); + + /** + * Apply this Tranformation. Apply this transformation to the + * Coordinate c. Can return an invalid Coordinate. + * apply0 assumes that c indicates a point at infinity, having + * [0, c.x, c.y] as homogeneous coordinates + */ + const Coordinate apply( const double x0, const double x1, const double x2 ) const; + const Coordinate apply( const Coordinate& c ) const; + const Coordinate apply0( const Coordinate& c ) const; + + /** + * Returns whether this is a homothetic (affine) transformation. + */ + bool isHomothetic() const; + bool isAffine() const; + double getProjectiveIndicator( const Coordinate& c ) const; + double getAffineDeterminant() const; + double getRotationAngle() const; + const Coordinate apply2by2only( const Coordinate& c ) const; + /** + * \ifnot creating-python-scripting-doc + * a homothetic transformation maintains the ratio's of lengths. + * This means that every length is multiplied by a fixed number when + * it is projected... This function does that calculation for + * you.. + * \endif + */ + double apply( double length ) const; + double data( int r, int c ) const; + /** + * The inverse Transformation. Returns the inverse Transformation + * of this Transformation. + */ + const Transformation inverse( bool& valid ) const; + + /** + * Identity. Returns the Identity Transformation, i.e. a + * Transformation that doesn't do anything. + */ + static const Transformation identity(); + /** + * Scaling over Point. Returns a Transformation that scales points + * by a certain factor with relation to a center point. + */ + static const Transformation scalingOverPoint( double factor, const Coordinate& center = Coordinate() ); + /** + * Scaling over Line. Returns a Transformation that scales points + * by a certain factor with relation to a line. Note: This is not a + * homothetic transformation. + */ + static const Transformation scalingOverLine( double factor, const LineData& l ); + /** + * Translation. Returns a Translation by a vector c. + */ + static const Transformation translation( const Coordinate& c ); + /** + * Rotation. Returns a Rotation by a certain angle, around a + * certain center. + */ + static const Transformation rotation( double angle, const Coordinate& center = Coordinate() ); + /** + * Point Reflection. Returns a reflection over a point + * \note This equals scaling( -1, c ); + */ + static const Transformation pointReflection( const Coordinate& c ); + /** + * Line Reflection. Returns a reflection over a line + * \note This equals scaling( -1, l ); + */ + static const Transformation lineReflection( const LineData& l ); + /** + * Harmonic Homology. Returns a Transformation that transforms points in + * such a way that it appears to cast a shadow, given a certain + * light source (center), and a line (axis) indicating a plane. + */ + static const Transformation harmonicHomology( const Coordinate& center, + const LineData& axis ); + /** + * Affinity given the image of 3 points. Returns the unique + * affinity that transforms 3 given points into 3 given points. + */ + static const Transformation affinityGI3P( + const std::vector<Coordinate>& FromPoints, + const std::vector<Coordinate>& ToPoints, + bool& valid ); + /** + * Projectivity given the image of 4 points. Returns the unique + * projectivity that transforms 4 given points into 4 given points. + */ + static const Transformation projectivityGI4P( + const std::vector<Coordinate>& FromPoints, + const std::vector<Coordinate>& ToPoints, + bool& valid ); + /** + * Cast Shadow. Returns a Transformation that transforms points in + * such a way that it appears to cast a shadow, given a certain + * light source, and a line indicating a plane. + */ + static const Transformation castShadow( const Coordinate& ls, + const LineData& d ); + /** + * Projective Rotation. This is really only a test example of a + * projective non-affine transformation... + */ + static const Transformation projectiveRotation( double alpha, + const Coordinate& d, + const Coordinate& t ); + + /** + * Similitude. Sequence of a rotation and a scaling with relation + * to a certain center. + */ + static const Transformation similitude( + const Coordinate& center, double theta, double factor ); + + /** + * Sequence. This creates a Transformation that executes first + * transformation b, and then a. + */ + friend const Transformation operator*( const Transformation& a, const Transformation& b ); + + /** + * Equality. Tests two Transformation's for equality. + */ + friend bool operator==( const Transformation& lhs, const Transformation& rhs ); +}; + +const Transformation operator*( const Transformation&, const Transformation& ); +bool operator==( const Transformation& lhs, const Transformation& rhs ); + +// enum tWantArgsResult { tComplete, tNotComplete, tNotGood }; + +// Transformation getProjectiveTransformation( +// int transformationsnum, Object *mtransformations[], +// bool& valid ); + +// tWantArgsResult WantTransformation ( Objects::const_iterator& i, +// const Objects& os ); + +// QString getTransformMessage ( const Objects& os, const Object *o ); + +// bool isHomoteticTransformation ( double transformation[3][3] ); + +#endif // KIG_MISC_KIGTRANSFORM_H diff --git a/kig/misc/lists.cc b/kig/misc/lists.cc new file mode 100644 index 00000000..e93700a1 --- /dev/null +++ b/kig/misc/lists.cc @@ -0,0 +1,389 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "lists.h" + +#include "object_constructor.h" +#include "guiaction.h" +#include "object_hierarchy.h" +#include "../kig/kig_part.h" + +#include "config.h" + +#include <klocale.h> +#include <kmessagebox.h> +#include <qfile.h> +#include <qtextstream.h> +#include <qdom.h> +#include <qregexp.h> +#include <algorithm> +using namespace std; + +template<typename T> +void vect_remove( std::vector<T>& v, const T& t ) +{ + typename std::vector<T>::iterator new_end = std::remove( v.begin(), v.end(), t ); + v.erase( new_end, v.end() ); +} + +GUIActionList* GUIActionList::instance() +{ + static GUIActionList l; + return &l; +} + +GUIActionList::~GUIActionList() +{ + for ( avectype::iterator i = mactions.begin(); i != mactions.end(); ++i ) + delete *i; +} + +GUIActionList::GUIActionList() +{ +} + +void GUIActionList::regDoc( KigPart* d ) +{ + mdocs.insert( d ); +} + +void GUIActionList::unregDoc( KigPart* d ) +{ + mdocs.erase( d ); +} + +void GUIActionList::add( const std::vector<GUIAction*>& a ) +{ + copy( a.begin(), a.end(), inserter( mactions, mactions.begin() ) ); + for ( dvectype::iterator i = mdocs.begin(); i != mdocs.end(); ++i ) + { + KigPart::GUIUpdateToken t = (*i)->startGUIActionUpdate(); + for ( uint j = 0; j < a.size(); ++j ) + (*i)->actionAdded( a[j], t ); + (*i)->endGUIActionUpdate( t ); + }; +} + +void GUIActionList::add( GUIAction* a ) +{ + mactions.insert( a ); + for ( dvectype::iterator i = mdocs.begin(); i != mdocs.end(); ++i ) + { + KigPart::GUIUpdateToken t = (*i)->startGUIActionUpdate(); + (*i)->actionAdded( a, t ); + (*i)->endGUIActionUpdate( t ); + }; +} + +void GUIActionList::remove( const std::vector<GUIAction*>& a ) +{ + for ( uint i = 0; i < a.size(); ++i ) + { + mactions.erase( a[i] ); + }; + for ( dvectype::iterator i = mdocs.begin(); i != mdocs.end(); ++i ) + { + KigPart::GUIUpdateToken t = (*i)->startGUIActionUpdate(); + for ( uint j = 0; j < a.size(); ++j ) + (*i)->actionRemoved( a[j], t ); + (*i)->endGUIActionUpdate( t ); + }; + delete_all( a.begin(), a.end() ); +} + +void GUIActionList::remove( GUIAction* a ) +{ + mactions.erase( a ); + for ( dvectype::iterator i = mdocs.begin(); i != mdocs.end(); ++i ) + { + KigPart::GUIUpdateToken t = (*i)->startGUIActionUpdate(); + (*i)->actionRemoved( a, t ); + (*i)->endGUIActionUpdate( t ); + }; + delete a; +} + +ObjectConstructorList::ObjectConstructorList() +{ +} + +ObjectConstructorList::~ObjectConstructorList() +{ + for ( vectype::iterator i = mctors.begin(); i != mctors.end(); ++i ) + delete *i; +} + +ObjectConstructorList* ObjectConstructorList::instance() +{ + static ObjectConstructorList s; + return &s; +} + +ObjectConstructorList::vectype ObjectConstructorList::ctorsThatWantArgs( + const std::vector<ObjectCalcer*>& os, const KigDocument& d, + const KigWidget& w, bool co ) const +{ + vectype ret; + for ( vectype::const_iterator i = mctors.begin(); i != mctors.end(); ++i ) + { + int r = (*i)->wantArgs( os, d, w ); + if ( r == ArgsParser::Complete || ( !co && r == ArgsParser::Valid ) ) + ret.push_back( *i ); + }; + return ret; +} + +void ObjectConstructorList::remove( ObjectConstructor* a ) +{ + vect_remove( mctors, a ); + delete a; +} + +void ObjectConstructorList::add( ObjectConstructor* a ) +{ + mctors.push_back( a ); +} + +Macro::Macro( GUIAction* a, MacroConstructor* c ) + : action( a ), ctor( c ) +{ +} + +bool operator==( const Macro& l, const Macro& r ) +{ + return ( l.action->descriptiveName() == r.action->descriptiveName() ) && + ( l.action->description() == r.action->description() ) && + ( l.action->iconFileName() == r.action->iconFileName() ); +} + +MacroList::MacroList() +{ +} + +MacroList::~MacroList() +{ + std::vector<GUIAction*> actions; + std::vector<ObjectConstructor*> ctors; + for ( vectype::iterator i = mdata.begin(); i != mdata.end(); ++i ) + { + Macro* m = *i; + GUIAction* a = m->action; + actions.push_back( a ); + ObjectConstructor* c = m->ctor; + ctors.push_back( c ); + delete m; + }; + mdata.clear(); + GUIActionList::instance()->remove( actions ); + for ( uint i = 0; i < ctors.size(); ++i ) + ObjectConstructorList::instance()->remove( ctors[i] ); +} + +MacroList* MacroList::instance() +{ + static MacroList t; + return &t; +} + +void MacroList::add( const std::vector<Macro*>& ms ) +{ + copy( ms.begin(), ms.end(), back_inserter( mdata ) ); + std::vector<GUIAction*> acts; + for ( uint i = 0; i < ms.size(); ++i ) + { + ObjectConstructorList::instance()->add( ms[i]->ctor ); + acts.push_back( ms[i]->action ); + }; + GUIActionList::instance()->add( acts ); +} + +void MacroList::add( Macro* m ) +{ + mdata.push_back( m ); + ObjectConstructorList::instance()->add( m->ctor ); + GUIActionList::instance()->add( m->action ); +} + +void MacroList::remove( Macro* m ) +{ + GUIAction* a = m->action; + ObjectConstructor* c = m->ctor; + mdata.erase( std::remove( mdata.begin(), mdata.end(), m ), + mdata.end() ); + delete m; + GUIActionList::instance()->remove( a ); + ObjectConstructorList::instance()->remove( c ); +} + +const MacroList::vectype& MacroList::macros() const +{ + return mdata; +} + +Macro::~Macro() +{ +} + +bool MacroList::save( Macro* m, const QString& f ) +{ + std::vector<Macro*> ms; + ms.push_back( m ); + return save( ms, f ); +} + +bool MacroList::save( const std::vector<Macro*>& ms, const QString& f ) +{ + QDomDocument doc( "KigMacroFile" ); + + QDomElement docelem = doc.createElement( "KigMacroFile" ); + docelem.setAttribute( "Version", KIGVERSION ); + docelem.setAttribute( "Number", ms.size() ); + + for ( uint i = 0; i < ms.size(); ++i ) + { + MacroConstructor* ctor = ms[i]->ctor; + + QDomElement macroelem = doc.createElement( "Macro" ); + + // name + QDomElement nameelem = doc.createElement( "Name" ); + nameelem.appendChild( doc.createTextNode( ctor->descriptiveName() ) ); + macroelem.appendChild( nameelem ); + + // desc + QDomElement descelem = doc.createElement( "Description" ); + descelem.appendChild( doc.createTextNode( ctor->description() ) ); + macroelem.appendChild( descelem ); + + // icon + QCString icon = ctor->iconFileName( true ); + if ( !icon.isNull() ) + { + QDomElement descelem = doc.createElement( "IconFileName" ); + descelem.appendChild( doc.createTextNode( icon ) ); + macroelem.appendChild( descelem ); + } + + // data + QDomElement hierelem = doc.createElement( "Construction" ); + ctor->hierarchy().serialize( hierelem, doc ); + macroelem.appendChild( hierelem ); + + docelem.appendChild( macroelem ); + }; + + doc.appendChild( docelem ); + + QFile file( f ); + if ( ! file.open( IO_WriteOnly ) ) + return false; + QTextStream stream( &file ); + stream << doc.toCString(); + return true; +} + +bool MacroList::load( const QString& f, std::vector<Macro*>& ret, const KigPart& kdoc ) +{ + QFile file( f ); + if ( ! file.open( IO_ReadOnly ) ) + { + KMessageBox::sorry( 0, i18n( "Could not open macro file '%1'" ).arg( f ) ); + return false; + } + QDomDocument doc( "KigMacroFile" ); + if ( !doc.setContent( &file ) ) + { + KMessageBox::sorry( 0, i18n( "Could not open macro file '%1'" ).arg( f ) ); + return false; + } + file.close(); + QDomElement main = doc.documentElement(); + + if ( main.tagName() == "KigMacroFile" ) + return loadNew( main, ret, kdoc ); + else + { + KMessageBox::detailedSorry( + 0, i18n( "Kig cannot open the macro file \"%1\"." ).arg( f ), + i18n( "This file was created by a very old Kig version (pre-0.4). " + "Support for this format has been removed from recent Kig versions. " + "You can try to import this macro using a previous Kig version " + "(0.4 to 0.6) and then export it again in the new format." ), + i18n( "Not Supported" ) ); + return false; + } +} + +bool MacroList::loadNew( const QDomElement& docelem, std::vector<Macro*>& ret, const KigPart& ) +{ + bool sok = true; + // unused.. +// int number = docelem.attribute( "Number" ).toInt( &sok ); + if ( ! sok ) return false; + + QString version = docelem.attribute( "Version" ); +// QRegExp re( "(\\d+)\\.(\\d+)\\.(\\d+)" ); +// re.match( version ); + // unused.. +// int major = re.cap( 1 ).toInt( &sok ); +// int minor = re.cap( 2 ).toInt( &sok ); +// int mminor = re.cap( 3 ).toInt( &sok ); +// if ( ! sok ) return false; + + int unnamedindex = 1; + QString tmp; + + for ( QDomElement macroelem = docelem.firstChild().toElement(); + ! macroelem.isNull(); macroelem = macroelem.nextSibling().toElement() ) + { + QString name, description; + ObjectHierarchy* hierarchy = 0; + QCString actionname, iconfile; + if ( macroelem.tagName() != "Macro" ) continue; // forward compat ? + for ( QDomElement dataelem = macroelem.firstChild().toElement(); + ! dataelem.isNull(); dataelem = dataelem.nextSibling().toElement() ) + { + if ( dataelem.tagName() == "Name" ) + name = dataelem.text(); + else if ( dataelem.tagName() == "Description" ) + description = dataelem.text(); + else if ( dataelem.tagName() == "Construction" ) + hierarchy = ObjectHierarchy::buildSafeObjectHierarchy( dataelem, tmp ); + else if ( dataelem.tagName() == "ActionName" ) + actionname = dataelem.text().latin1(); + else if ( dataelem.tagName() == "IconFileName" ) + iconfile = dataelem.text().latin1(); + else continue; + }; + assert( hierarchy ); + // if the macro has no name, we give it a bogus name... + if ( name.isEmpty() ) + name = i18n( "Unnamed Macro #%1" ).arg( unnamedindex++ ); + MacroConstructor* ctor = + new MacroConstructor( *hierarchy, i18n( name.latin1() ), i18n( description.latin1() ), iconfile ); + delete hierarchy; + GUIAction* act = new ConstructibleAction( ctor, actionname ); + Macro* macro = new Macro( act, ctor ); + ret.push_back( macro ); + }; + return true; +} + +const ObjectConstructorList::vectype& ObjectConstructorList::constructors() const +{ + return mctors; +} diff --git a/kig/misc/lists.h b/kig/misc/lists.h new file mode 100644 index 00000000..a3f97f1d --- /dev/null +++ b/kig/misc/lists.h @@ -0,0 +1,170 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_MISC_LISTS_H +#define KIG_MISC_LISTS_H + +#include <vector> +#include <set> + +class GUIAction; +class ObjectConstructor; +class MacroConstructor; +class KigDocument; +class KigPart; +class KigWidget; +class QString; +class QDomElement; +class ObjectCalcer; + +/** + * List of GUIActions for the parts to show. Note that the list owns + * the actions it receives.. + */ +class GUIActionList +{ +public: + typedef std::set<GUIAction*> avectype; + typedef std::set<KigPart*> dvectype; +private: + avectype mactions; + dvectype mdocs; + GUIActionList(); + ~GUIActionList(); +public: + static GUIActionList* instance(); + const avectype& actions() const { return mactions; } + + /** + * register this document, so that it receives notifications for + * added and removed actions.. + */ + void regDoc( KigPart* d ); + void unregDoc( KigPart* d ); + + void add( GUIAction* a ); + void add( const std::vector<GUIAction*>& a ); + void remove( GUIAction* a ); + void remove( const std::vector<GUIAction*>& a ); +}; + +/** + * The list of object constructors for use in various places, e.g. RMB + * menu's. Note that the list owns the ctors it gets.. + */ +class ObjectConstructorList +{ +public: + typedef std::vector<ObjectConstructor*> vectype; +private: + vectype mctors; + ObjectConstructorList(); + ~ObjectConstructorList(); +public: + static ObjectConstructorList* instance(); + void add( ObjectConstructor* a ); + void remove( ObjectConstructor* a ); + vectype ctorsThatWantArgs( const std::vector<ObjectCalcer*>&, const KigDocument&, + const KigWidget&, bool completeOnly = false + ) const; + const vectype& constructors() const; +}; + +/** + * this is just a simple data struct. doesn't have any functionality + * of its own.. + */ +class Macro +{ +public: + GUIAction* action; + MacroConstructor* ctor; + Macro( GUIAction* a, MacroConstructor* c ); + ~Macro(); +}; + +/** + * a simply equality operator for Macro class. + */ +bool operator==( const Macro& l, const Macro& r ); + +/** + * This class keeps a list of all macro's, and allows them to be + * easily accessed, added etc. Macro objects are owned by this + * list.. + */ +class MacroList +{ +public: + typedef std::vector<Macro*> vectype; +private: + vectype mdata; + MacroList(); + ~MacroList(); +public: + /** + * MacroList is a singleton + */ + static MacroList* instance(); + + /** + * Add a Macro \p m . MacroList takes care of adding the action and + * ctor in the relevant places.. + */ + void add( Macro* m ); + /** + * Add the Macro's \p ms. MacroList takes care of adding the action + * and ctor in the relevant places.. + */ + void add( const vectype& ms ); + + /** + * Remove macro \p m . Macrolist takes care of deleting everything, and + * unregistering the action and ctor from the relevant places.. + */ + void remove( Macro* m ); + + /** + * Save macro \p m to file \p f .. + */ + bool save( Macro* m, const QString& f ); + /** + * Save macros \p ms to file \p f .. + */ + bool save( const vectype& ms, const QString& f ); + + /** + * load macro's from file \p f .. + * note that this just returns the loaded macro's, and doesn't add + * them to the various lists. Use add() if you want + * that behaviour.. + * The fact that this functions requires a KigPart argument is + * semantically incorrect, but i haven't been able to work around + * it.. + */ + bool load( const QString& f, vectype& ret, const KigPart& ); + + /** + * get access to the list of macro's.. + */ + const vectype& macros() const; + +private: + bool loadNew( const QDomElement& docelem, std::vector<Macro*>& ret, const KigPart& ); +}; + +#endif diff --git a/kig/misc/object_constructor.cc b/kig/misc/object_constructor.cc new file mode 100644 index 00000000..5634d0d2 --- /dev/null +++ b/kig/misc/object_constructor.cc @@ -0,0 +1,609 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "object_constructor.h" + +#include "argsparser.h" +#include "kigpainter.h" +#include "guiaction.h" + +#include "../kig/kig_part.h" +#include "../kig/kig_view.h" + +#include "../objects/object_holder.h" +#include "../objects/object_drawer.h" +#include "../objects/object_type.h" +#include "../objects/other_type.h" +#include "../objects/object_imp.h" +#include "../objects/bogus_imp.h" +#include "../objects/line_imp.h" +#include "../objects/circle_imp.h" +#include "../objects/point_imp.h" + +#include "../modes/construct_mode.h" + +#include <qpen.h> + +#include <klocale.h> + +#include <algorithm> +#include <functional> + +const QString StandardConstructorBase::descriptiveName() const +{ + return i18n( mdescname ); +} + +const QString StandardConstructorBase::description() const +{ + return i18n( mdesc ); +} + +const QCString StandardConstructorBase::iconFileName( const bool ) const +{ + return miconfile; +} + +const bool StandardConstructorBase::isAlreadySelectedOK( const std::vector<ObjectCalcer*>&, const int& ) const +{ + return false; +} + +StandardConstructorBase::StandardConstructorBase( + const char* descname, const char* desc, + const char* iconfile, const ArgsParser& parser ) + : mdescname( descname ), + mdesc( desc ), + miconfile( iconfile ), + margsparser( parser ) +{ +} + +const int StandardConstructorBase::wantArgs( const std::vector<ObjectCalcer*>& os, + const KigDocument&, + const KigWidget& ) const +{ + return margsparser.check( os ); +} + +void StandardConstructorBase::handleArgs( + const std::vector<ObjectCalcer*>& os, KigPart& d, + KigWidget& v ) const +{ + std::vector<ObjectHolder*> bos = build( os, d.document(), v ); + for ( std::vector<ObjectHolder*>::iterator i = bos.begin(); + i != bos.end(); ++i ) + { + (*i)->calc( d.document() ); + } + + d.addObjects( bos ); +} + +void StandardConstructorBase::handlePrelim( + KigPainter& p, const std::vector<ObjectCalcer*>& os, + const KigDocument& d, const KigWidget& + ) const +{ + assert ( margsparser.check( os ) != ArgsParser::Invalid ); + std::vector<ObjectCalcer*> args = margsparser.parse( os ); + p.setBrushStyle( Qt::NoBrush ); + p.setBrushColor( Qt::red ); + p.setPen( QPen ( Qt::red, 1) ); + p.setWidth( -1 ); // -1 means the default width for the object being + // drawn.. + + ObjectDrawer drawer( Qt::red ); + drawprelim( drawer, p, args, d ); +} + +SimpleObjectTypeConstructor::SimpleObjectTypeConstructor( + const ArgsParserObjectType* t, const char* descname, + const char* desc, const char* iconfile ) + : StandardConstructorBase( descname, desc, iconfile, + t->argsParser() ), + mtype( t ) +{ +} + +SimpleObjectTypeConstructor::~SimpleObjectTypeConstructor() +{ +} + +void SimpleObjectTypeConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, + const KigDocument& doc ) const +{ + Args args; + using namespace std; + transform( parents.begin(), parents.end(), + back_inserter( args ), mem_fun( &ObjectCalcer::imp ) ); + ObjectImp* data = mtype->calc( args, doc ); + drawer.draw( *data, p, true ); + delete data; +} + +std::vector<ObjectHolder*> SimpleObjectTypeConstructor::build( + const std::vector<ObjectCalcer*>& os, KigDocument&, KigWidget& ) const +{ + ObjectTypeCalcer* calcer = new ObjectTypeCalcer( mtype, os ); + ObjectHolder* h = new ObjectHolder( calcer ); + std::vector<ObjectHolder*> ret; + ret.push_back( h ); + return ret; +} + +StandardConstructorBase::~StandardConstructorBase() +{ +} + +MultiObjectTypeConstructor::MultiObjectTypeConstructor( + const ArgsParserObjectType* t, const char* descname, + const char* desc, const char* iconfile, + const std::vector<int>& params ) + : StandardConstructorBase( descname, desc, iconfile, mparser ), + mtype( t ), mparams( params ), + mparser( t->argsParser().without( IntImp::stype() ) ) +{ +} + +MultiObjectTypeConstructor::MultiObjectTypeConstructor( + const ArgsParserObjectType* t, const char* descname, + const char* desc, const char* iconfile, + int a, int b, int c, int d ) + : StandardConstructorBase( descname, desc, iconfile, mparser ), + mtype( t ), mparams(), + mparser( t->argsParser().without( IntImp::stype() ) ) +{ + mparams.push_back( a ); + mparams.push_back( b ); + if ( c != -999 ) mparams.push_back( c ); + if ( d != -999 ) mparams.push_back( d ); +} + +MultiObjectTypeConstructor::~MultiObjectTypeConstructor() +{ +} + +void MultiObjectTypeConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, + const KigDocument& doc ) const +{ + Args args; + using namespace std; + transform( parents.begin(), parents.end(), + back_inserter( args ), mem_fun( &ObjectCalcer::imp ) ); + + for ( vector<int>::const_iterator i = mparams.begin(); i != mparams.end(); ++i ) + { + IntImp param( *i ); + args.push_back( ¶m ); + ObjectImp* data = mtype->calc( args, doc ); + drawer.draw( *data, p, true ); + delete data; + args.pop_back(); + }; +} + +std::vector<ObjectHolder*> MultiObjectTypeConstructor::build( + const std::vector<ObjectCalcer*>& os, KigDocument&, KigWidget& ) const +{ + std::vector<ObjectHolder*> ret; + for ( std::vector<int>::const_iterator i = mparams.begin(); + i != mparams.end(); ++i ) + { + ObjectConstCalcer* d = new ObjectConstCalcer( new IntImp( *i ) ); + + std::vector<ObjectCalcer*> args( os ); + args.push_back( d ); + + ret.push_back( new ObjectHolder( new ObjectTypeCalcer( mtype, args ) ) ); + }; + return ret; +} + +MergeObjectConstructor::~MergeObjectConstructor() +{ + for ( vectype::iterator i = mctors.begin(); i != mctors.end(); ++i ) + delete *i; +} + +MergeObjectConstructor::MergeObjectConstructor( + const char* descname, const char* desc, const char* iconfilename ) + : ObjectConstructor(), mdescname( descname ), mdesc( desc ), + miconfilename( iconfilename ), mctors() +{ +} + +ObjectConstructor::~ObjectConstructor() +{ +} + +void MergeObjectConstructor::merge( ObjectConstructor* e ) +{ + mctors.push_back( e ); +} + +const QString MergeObjectConstructor::descriptiveName() const +{ + return i18n( mdescname ); +} + +const QString MergeObjectConstructor::description() const +{ + return i18n( mdesc ); +} + +const QCString MergeObjectConstructor::iconFileName( const bool ) const +{ + return miconfilename; +} + +const bool MergeObjectConstructor::isAlreadySelectedOK( const std::vector<ObjectCalcer*>&, const int& ) const +{ + return false; +} + +const int MergeObjectConstructor::wantArgs( + const std::vector<ObjectCalcer*>& os, const KigDocument& d, const KigWidget& v ) const +{ + for ( vectype::const_iterator i = mctors.begin(); i != mctors.end(); ++i ) + { + int w = (*i)->wantArgs( os, d, v ); + if ( w != ArgsParser::Invalid ) return w; + }; + return ArgsParser::Invalid; +} + +void MergeObjectConstructor::handleArgs( + const std::vector<ObjectCalcer*>& os, KigPart& d, KigWidget& v ) const +{ + for ( vectype::const_iterator i = mctors.begin(); i != mctors.end(); ++i ) + { + int w = (*i)->wantArgs( os, d.document(), v ); + if ( w == ArgsParser::Complete ) + { + (*i)->handleArgs( os, d, v ); + return; + }; + }; + assert( false ); +} + +void MergeObjectConstructor::handlePrelim( + KigPainter& p, const std::vector<ObjectCalcer*>& sel, + const KigDocument& d, const KigWidget& v ) const +{ + for ( vectype::const_iterator i = mctors.begin(); i != mctors.end(); ++i ) + { + int w = (*i)->wantArgs( sel, d, v ); + if ( w != ArgsParser::Invalid ) + { + (*i)->handlePrelim( p, sel, d, v ); + return; + }; + }; +} + +QString StandardConstructorBase::useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel, + const KigDocument&, const KigWidget& ) const +{ + using namespace std; + Args args; + transform( sel.begin(), sel.end(), back_inserter( args ), mem_fun( &ObjectCalcer::imp ) ); + + std::string ret = margsparser.usetext( o.imp(), args ); + if ( ret.empty() ) return QString::null; + return i18n( ret.c_str() ); +} + +QString StandardConstructorBase::selectStatement( + const std::vector<ObjectCalcer*>& sel, const KigDocument&, + const KigWidget& ) const +{ + using namespace std; + Args args; + transform( sel.begin(), sel.end(), back_inserter( args ), mem_fun( &ObjectCalcer::imp ) ); + + std::string ret = margsparser.selectStatement( args ); + if ( ret.empty() ) return QString::null; + return i18n( ret.c_str() ); +} + +QString MergeObjectConstructor::useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel, + const KigDocument& d, const KigWidget& v ) const +{ + for ( vectype::const_iterator i = mctors.begin(); i != mctors.end(); ++i ) + { + std::vector<ObjectCalcer*> args( sel ); + int w = (*i)->wantArgs( args, d, v ); + if ( w != ArgsParser::Invalid ) return (*i)->useText( o, sel, d, v ); + }; + return QString::null; +} + +QString MergeObjectConstructor::selectStatement( + const std::vector<ObjectCalcer*>& sel, const KigDocument& d, + const KigWidget& w ) const +{ + for ( vectype::const_iterator i = mctors.begin(); i != mctors.end(); ++i ) + { + std::vector<ObjectCalcer*> args( sel ); + int wa = (*i)->wantArgs( args, d, w ); + if ( wa != ArgsParser::Invalid ) return (*i)->selectStatement( sel, d, w ); + }; + return QString::null; +} + +MacroConstructor::MacroConstructor( const ObjectHierarchy& hier, const QString& name, + const QString& desc, const QCString& iconfile ) + : ObjectConstructor(), mhier( hier ), mname( name ), mdesc( desc ), + mbuiltin( false ), miconfile( iconfile ), + mparser( mhier.argParser() ) +{ +} + +MacroConstructor::MacroConstructor( + const std::vector<ObjectCalcer*>& input, const std::vector<ObjectCalcer*>& output, + const QString& name, const QString& description, + const QCString& iconfile ) + : ObjectConstructor(), mhier( input, output ), + mname( name ), mdesc( description ), mbuiltin( false ), + miconfile( iconfile ), + mparser( mhier.argParser() ) +{ +} + +MacroConstructor::~MacroConstructor() +{ +} + +const QString MacroConstructor::descriptiveName() const +{ + return mname; +} + +const QString MacroConstructor::description() const +{ + return mdesc; +} + +const QCString MacroConstructor::iconFileName( const bool canBeNull ) const +{ + return ( miconfile.isNull() && !canBeNull ) ? QCString( "gear" ) : miconfile; +} + +const bool MacroConstructor::isAlreadySelectedOK( const std::vector<ObjectCalcer*>&, const int& ) const +{ + return false; +} + +const int MacroConstructor::wantArgs( const std::vector<ObjectCalcer*>& os, const KigDocument&, + const KigWidget& ) const +{ + return mparser.check( os ); +} + +void MacroConstructor::handleArgs( const std::vector<ObjectCalcer*>& os, KigPart& d, + KigWidget& ) const +{ + std::vector<ObjectCalcer*> args = mparser.parse( os ); + std::vector<ObjectCalcer*> bos = mhier.buildObjects( args, d.document() ); + std::vector<ObjectHolder*> hos; + for ( std::vector<ObjectCalcer*>::iterator i = bos.begin(); + i != bos.end(); ++i ) + { + hos.push_back( new ObjectHolder( *i ) ); + hos.back()->calc( d.document() ); + } + + d.addObjects( hos ); +} + +QString MacroConstructor::selectStatement( + const std::vector<ObjectCalcer*>& sel, const KigDocument&, + const KigWidget& ) const +{ + using namespace std; + Args args; + transform( sel.begin(), sel.end(), back_inserter( args ), + mem_fun( &ObjectCalcer::imp ) ); + std::string ret = mparser.selectStatement( args ); + if ( ret.empty() ) return QString::null; + else return i18n( ret.c_str() ); +} + +QString MacroConstructor::useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel, + const KigDocument&, const KigWidget& + ) const +{ + using namespace std; + Args args; + transform( sel.begin(), sel.end(), back_inserter( args ), + mem_fun( &ObjectCalcer::imp ) ); + std::string ret = mparser.usetext( o.imp(), args ); + if ( ret.empty() ) return QString::null; + else return i18n( ret.c_str() ); +} + +void MacroConstructor::handlePrelim( KigPainter& p, const std::vector<ObjectCalcer*>& sel, + const KigDocument& doc, const KigWidget& + ) const +{ + if ( sel.size() != mhier.numberOfArgs() ) return; + + using namespace std; + Args args; + transform( sel.begin(), sel.end(), back_inserter( args ), + mem_fun( &ObjectCalcer::imp ) ); + args = mparser.parse( args ); + std::vector<ObjectImp*> ret = mhier.calc( args, doc ); + for ( uint i = 0; i < ret.size(); ++i ) + { + ObjectDrawer d; + d.draw( *ret[i], p, true ); + ret[i]->draw( p ); + delete ret[i]; + }; +} + +void SimpleObjectTypeConstructor::plug( KigPart*, KigGUIAction* ) +{ +} + +void MultiObjectTypeConstructor::plug( KigPart*, KigGUIAction* ) +{ +} + +void MergeObjectConstructor::plug( KigPart*, KigGUIAction* ) +{ +} + +void MacroConstructor::plug( KigPart* doc, KigGUIAction* kact ) +{ + if ( mbuiltin ) return; + if ( mhier.numberOfResults() != 1 ) + doc->aMNewOther.append( kact ); + else + { + if ( mhier.idOfLastResult() == SegmentImp::stype() ) + doc->aMNewSegment.append( kact ); + else if ( mhier.idOfLastResult() == PointImp::stype() ) + doc->aMNewPoint.append( kact ); + else if ( mhier.idOfLastResult() == CircleImp::stype() ) + doc->aMNewCircle.append( kact ); + else if ( mhier.idOfLastResult()->inherits( AbstractLineImp::stype() ) ) + // line or ray + doc->aMNewLine.append( kact ); + else if ( mhier.idOfLastResult() == ConicImp::stype() ) + doc->aMNewConic.append( kact ); + else doc->aMNewOther.append( kact ); + }; + doc->aMNewAll.append( kact ); +} + +const ObjectHierarchy& MacroConstructor::hierarchy() const +{ + return mhier; +} + +bool SimpleObjectTypeConstructor::isTransform() const +{ + return mtype->isTransform(); +} + +bool MultiObjectTypeConstructor::isTransform() const +{ + return mtype->isTransform(); +} + +bool MergeObjectConstructor::isTransform() const +{ + bool ret = false; + for ( vectype::const_iterator i = mctors.begin(); i != mctors.end(); ++i ) + ret |= (*i)->isTransform(); + return ret; +} + +bool MacroConstructor::isTransform() const +{ + return false; +} + +void MacroConstructor::setBuiltin( bool builtin ) +{ + mbuiltin = builtin; +} + +bool ObjectConstructor::isIntersection() const +{ + return false; +} + +PropertyObjectConstructor::PropertyObjectConstructor( + const ObjectImpType* imprequirement, const char* usetext, + const char* selectstat, const char* descname, const char* desc, + const char* iconfile, const char* propertyinternalname ) + : StandardConstructorBase( descname, desc, iconfile, mparser ), + mpropinternalname( propertyinternalname ) +{ + ArgsParser::spec argsspec[1]; + argsspec[0].type = imprequirement; + argsspec[0].usetext = usetext; + argsspec[0].selectstat = selectstat; + mparser.initialize( argsspec, 1 ); +} + +PropertyObjectConstructor::~PropertyObjectConstructor() +{ +} + +void PropertyObjectConstructor::drawprelim( + const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, + const KigDocument& d ) const +{ + int index = parents[0]->imp()->propertiesInternalNames().findIndex( mpropinternalname ); + assert ( index != -1 ); + ObjectImp* imp = parents[0]->imp()->property( index, d ); + drawer.draw( *imp, p, true ); + delete imp; +} + +std::vector<ObjectHolder*> PropertyObjectConstructor::build( + const std::vector<ObjectCalcer*>& parents, KigDocument&, + KigWidget& ) const +{ + int index = parents[0]->imp()->propertiesInternalNames().findIndex( mpropinternalname ); + assert( index != -1 ); + std::vector<ObjectHolder*> ret; + ret.push_back( + new ObjectHolder( + new ObjectPropertyCalcer( parents[0], index ) ) ); + return ret; +} + +void PropertyObjectConstructor::plug( KigPart*, KigGUIAction* ) +{ +} + +bool PropertyObjectConstructor::isTransform() const +{ + return false; +} + +bool ObjectConstructor::isTest() const +{ + return false; +} + +BaseConstructMode* ObjectConstructor::constructMode( KigPart& doc ) +{ + return new ConstructMode( doc, this ); +} + +void MacroConstructor::setName( const QString& name ) +{ + mname = name; +} + +void MacroConstructor::setDescription( const QString& desc ) +{ + mdesc = desc; +} + +void MacroConstructor::setIcon( QCString& icon ) +{ + miconfile = icon; +} diff --git a/kig/misc/object_constructor.h b/kig/misc/object_constructor.h new file mode 100644 index 00000000..57261c69 --- /dev/null +++ b/kig/misc/object_constructor.h @@ -0,0 +1,396 @@ +// Copyright (C) 2002-2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_MISC_OBJECT_CONSTRUCTOR_H +#define KIG_MISC_OBJECT_CONSTRUCTOR_H + +#include "argsparser.h" +#include "object_hierarchy.h" + +class KigPainter; +class KigDocument; +class KigGUIAction; +class KigWidget; +class ArgsParserObjectType; +class ObjectType; +class BaseConstructMode; + +class QString; +class QCString; + +/** + * This class represents a way to construct a set of objects from a + * set of other objects. There are some important child classes, like + * MacroConstructor, StandardObjectConstructor etc. ( see below ) + * Actually, it is more generic than that, it provides a way to do + * _something_ with a set of objects, but for now, i only use it to + * construct objects. Maybe some day, i'll find something more + * interesting to do with it, who knows... ;) + */ +class ObjectConstructor +{ +public: + virtual ~ObjectConstructor(); + + virtual const QString descriptiveName() const = 0; + virtual const QString description() const = 0; + virtual const QCString iconFileName( const bool canBeNull = false ) const = 0; + + /** + * the following function is called in case of duplication of arguments + * and returns true if this is acceptable; this will return false for + * typical objects + */ + virtual const bool isAlreadySelectedOK( const std::vector<ObjectCalcer*>& os, + const int& ) const = 0; + /** + * can this constructor do something useful with \p os ? return + * ArgsParser::Complete, Valid or NotGood + */ + virtual const int wantArgs( const std::vector<ObjectCalcer*>& os, + const KigDocument& d, + const KigWidget& v + ) const = 0; + + /** + * do something fun with \p os .. This func is only called if wantArgs + * returned Complete.. handleArgs should <i>not</i> do any + * drawing.. after somebody calls this function, he should + * redrawScreen() himself.. + */ + virtual void handleArgs( const std::vector<ObjectCalcer*>& os, + KigPart& d, + KigWidget& v + ) const = 0; + + /** + * return a string describing what you would use \p o for if it were + * selected... \p o should be part of \p sel . + */ + virtual QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel, + const KigDocument& d, const KigWidget& v + ) const = 0; + + /** + * return a string describing what argument you want next, if the + * given selection of objects were selected. + */ + virtual QString selectStatement( + const std::vector<ObjectCalcer*>& sel, const KigDocument& d, + const KigWidget& w ) const = 0; + + /** + * show a preliminary version of what you would do when \ref handleArgs + * would be called.. E.g. if this constructor normally constructs a + * locus through some 5 points, then it will try to draw a locus + * through whatever number of points it gets.. + */ + virtual void handlePrelim( KigPainter& p, + const std::vector<ObjectCalcer*>& sel, + const KigDocument& d, + const KigWidget& v + ) const = 0; + + virtual void plug( KigPart* doc, KigGUIAction* kact ) = 0; + + virtual bool isTransform() const = 0; + virtual bool isTest() const; + virtual bool isIntersection() const; + + /** + * Which construct mode should be used for this ObjectConstructor. + * In fact, this is not a pretty design. The Kig + * GUIAction-ObjectConstructor stuff should be reworked into a + * general GUIAction, which just models something which can be + * executed given a certain number of arguments. The code for + * drawPrelim and such should all be in the ConstructMode, and the + * new GUIAction should just start the correct KigMode with the + * correct arguments. + * + * This function is only overridden in TestConstructor. + */ + virtual BaseConstructMode* constructMode( KigPart& doc ); +}; + +/** + * This class provides wraps ObjectConstructor in a more simple + * interface for the most common object types.. + */ +class StandardConstructorBase + : public ObjectConstructor +{ + const char* mdescname; + const char* mdesc; + const char* miconfile; + const ArgsParser& margsparser; +public: + StandardConstructorBase( const char* descname, + const char* desc, + const char* iconfile, + const ArgsParser& parser ); + + virtual ~StandardConstructorBase(); + + const QString descriptiveName() const; + const QString description() const; + const QCString iconFileName( const bool canBeNull = false ) const; + + const bool isAlreadySelectedOK( const std::vector<ObjectCalcer*>& os, + const int& ) const; + virtual const int wantArgs( + const std::vector<ObjectCalcer*>& os, const KigDocument& d, + const KigWidget& v + ) const; + + void handleArgs( const std::vector<ObjectCalcer*>& os, + KigPart& d, + KigWidget& v + ) const; + + void handlePrelim( KigPainter& p, const std::vector<ObjectCalcer*>& sel, + const KigDocument& d, const KigWidget& v + ) const; + + virtual void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, + const KigDocument& ) const = 0; + + QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel, + const KigDocument& d, const KigWidget& v ) const; + + QString selectStatement( + const std::vector<ObjectCalcer*>& sel, const KigDocument& d, + const KigWidget& w ) const; + + virtual std::vector<ObjectHolder*> build( + const std::vector<ObjectCalcer*>& os, + KigDocument& d, KigWidget& w + ) const = 0; +}; + +/** + * A standard implementation of StandardConstructorBase for simple + * types.. + */ +class SimpleObjectTypeConstructor + : public StandardConstructorBase +{ + const ArgsParserObjectType* mtype; +public: + SimpleObjectTypeConstructor( + const ArgsParserObjectType* t, const char* descname, + const char* desc, const char* iconfile ); + + ~SimpleObjectTypeConstructor(); + + void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, + const KigDocument& ) const; + + std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, + KigDocument& d, + KigWidget& w ) const; + + void plug( KigPart* doc, KigGUIAction* kact ); + + bool isTransform() const; +}; + +/** + * A standard implementation of StandardConstructorBase for property + * objects... + */ +class PropertyObjectConstructor + : public StandardConstructorBase +{ + ArgsParser mparser; + const char* mpropinternalname; +public: + PropertyObjectConstructor( + const ObjectImpType* imprequirement, const char* usetext, + const char* selectstat, const char* descname, const char* desc, + const char* iconfile, const char* propertyinternalname ); + + ~PropertyObjectConstructor(); + + void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, + const KigDocument& ) const; + + std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, + KigDocument& d, KigWidget& w ) const; + + void plug( KigPart* doc, KigGUIAction* kact ); + + bool isTransform() const; +}; + +/** + * This class is the equivalent of \ref SimpleObjectTypeConstructor + * for object types that are constructed in groups of more than one. + * For example, the intersection of a circle and line in general + * produces two points, in general. Internally, we differentiate + * betweem them by passing them a parameter of ( in this case ) 1 or + * -1. There are still other object types that work the same, and + * they all require this sort of parameter. + * E.g. CubicLineIntersectionType takes a parameter between 1 and 3. + * This class knows about that, and constructs the objects along this + * scheme.. + */ +class MultiObjectTypeConstructor + : public StandardConstructorBase +{ + const ArgsParserObjectType* mtype; + std::vector<int> mparams; + ArgsParser mparser; +public: + MultiObjectTypeConstructor( + const ArgsParserObjectType* t, const char* descname, + const char* desc, const char* iconfile, + const std::vector<int>& params ); + MultiObjectTypeConstructor( + const ArgsParserObjectType* t, const char* descname, + const char* desc, const char* iconfile, + int a, int b, int c = -999, int d = -999 ); + ~MultiObjectTypeConstructor(); + + void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, + const KigDocument& ) const; + + std::vector<ObjectHolder*> build( + const std::vector<ObjectCalcer*>& os, + KigDocument& d, KigWidget& w ) const; + + void plug( KigPart* doc, KigGUIAction* kact ); + + bool isTransform() const; +}; + +/** + * This class is a collection of some other ObjectConstructors, that + * makes them appear to the user as a single ObjectConstructor. It is + * e.g. used for the "intersection" constructor. + */ +class MergeObjectConstructor + : public ObjectConstructor +{ + const char* mdescname; + const char* mdesc; + const char* miconfilename; + typedef std::vector<ObjectConstructor*> vectype; + vectype mctors; +public: + MergeObjectConstructor( const char* descname, const char* desc, + const char* iconfilename ); + ~MergeObjectConstructor(); + + void merge( ObjectConstructor* e ); + + const QString descriptiveName() const; + const QString description() const; + const QCString iconFileName( const bool canBeNull = false ) const; + + const bool isAlreadySelectedOK( const std::vector<ObjectCalcer*>& os, + const int& ) const; + const int wantArgs( const std::vector<ObjectCalcer*>& os, + const KigDocument& d, + const KigWidget& v + ) const; + + QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel, + const KigDocument& d, const KigWidget& v ) const; + + QString selectStatement( + const std::vector<ObjectCalcer*>& sel, const KigDocument& d, + const KigWidget& w ) const; + + void handleArgs( const std::vector<ObjectCalcer*>& os, KigPart& d, KigWidget& v ) const; + + void handlePrelim( KigPainter& p, const std::vector<ObjectCalcer*>& sel, + const KigDocument& d, const KigWidget& v ) const; + + void plug( KigPart* doc, KigGUIAction* kact ); + + bool isTransform() const; +}; + +/** + * MacroConstructor is a class that represents Kig macro's: these are + * constructed by the user, and defined by a set of input and a set of + * output objects. The macro-constructor saves the way in which the + * output objects have been built from the input objects, and when + * given similar input objects, it will produce objects in the given + * way. The data is saved in a \ref ObjectHierarchy. + */ +class MacroConstructor + : public ObjectConstructor +{ + ObjectHierarchy mhier; + QString mname; + QString mdesc; + bool mbuiltin; + QCString miconfile; + ArgsParser mparser; +public: + MacroConstructor( const std::vector<ObjectCalcer*>& input, const std::vector<ObjectCalcer*>& output, + const QString& name, const QString& description, + const QCString& iconfile = 0 ); + MacroConstructor( const ObjectHierarchy& hier, const QString& name, + const QString& desc, + const QCString& iconfile = 0 ); + ~MacroConstructor(); + + const ObjectHierarchy& hierarchy() const; + + const QString descriptiveName() const; + const QString description() const; + const QCString iconFileName( const bool canBeNull = false ) const; + + const bool isAlreadySelectedOK( const std::vector<ObjectCalcer*>& os, + const int& ) const; + const int wantArgs( const std::vector<ObjectCalcer*>& os, const KigDocument& d, + const KigWidget& v ) const; + + void handleArgs( const std::vector<ObjectCalcer*>& os, KigPart& d, + KigWidget& v ) const; + + QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel, + const KigDocument& d, const KigWidget& v + ) const; + + QString selectStatement( + const std::vector<ObjectCalcer*>& sel, const KigDocument& d, + const KigWidget& w ) const; + + void handlePrelim( KigPainter& p, const std::vector<ObjectCalcer*>& sel, + const KigDocument& d, const KigWidget& v + ) const; + + void plug( KigPart* doc, KigGUIAction* kact ); + + void setBuiltin( bool builtin ); + + /** + * is this the ctor for a transformation type. We want to know this + * cause transform types are shown separately in an object's RMB + * menu.. + */ + bool isTransform() const; + + void setName( const QString& name ); + void setDescription( const QString& desc ); + void setIcon( QCString& icon ); +}; + +#endif diff --git a/kig/misc/object_hierarchy.cc b/kig/misc/object_hierarchy.cc new file mode 100644 index 00000000..9102051a --- /dev/null +++ b/kig/misc/object_hierarchy.cc @@ -0,0 +1,774 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "object_hierarchy.h" + +#include "../objects/object_holder.h" +#include "../objects/other_type.h" +#include "../objects/object_imp.h" +#include "../objects/object_imp_factory.h" +#include "../objects/object_type_factory.h" +#include "../objects/bogus_imp.h" +#include "../objects/transform_types.h" +#include "../objects/object_type.h" + +#include <kglobal.h> +#include <qdom.h> + +class ObjectHierarchy::Node +{ +public: + enum { ID_PushStack, ID_ApplyType, ID_FetchProp }; + virtual int id() const = 0; + + virtual ~Node(); + virtual Node* copy() const = 0; + + virtual void apply( std::vector<const ObjectImp*>& stack, int loc, + const KigDocument& ) const = 0; + + virtual void apply( std::vector<ObjectCalcer*>& stack, int loc ) const = 0; + + // this function is used to check whether the final objects depend + // on the given objects. The dependsstack contains a set of + // booleans telling which parts of the hierarchy certainly depend on + // the given objects. In this function, the node should check + // whether any of its parents have true set, and if so, set its own + // value to true. + virtual void checkDependsOnGiven( std::vector<bool>& dependsstack, int loc ) const = 0; + // this function is used to check whether the given objects are all + // used by one or more of the final objects. The usedstack contains + // a set of booleans telling which parts of the hierarchy are + // certainly ancestors of the final objects. In this function, the + // node should set all of its parents' booleans to true. + virtual void checkArgumentsUsed( std::vector<bool>& usedstack ) const = 0; +}; + +ObjectHierarchy::Node::~Node() +{ +} + +class PushStackNode + : public ObjectHierarchy::Node +{ + ObjectImp* mimp; +public: + PushStackNode( ObjectImp* imp ) : mimp( imp ) {} + ~PushStackNode(); + + const ObjectImp* imp() const { return mimp; } + + int id() const; + Node* copy() const; + void apply( std::vector<const ObjectImp*>& stack, + int loc, const KigDocument& ) const; + void apply( std::vector<ObjectCalcer*>& stack, int loc ) const; + + void checkDependsOnGiven( std::vector<bool>& dependsstack, int loc ) const; + void checkArgumentsUsed( std::vector<bool>& usedstack ) const; +}; + +void PushStackNode::checkArgumentsUsed( std::vector<bool>& ) const +{ +} + +void PushStackNode::apply( std::vector<ObjectCalcer*>& stack, int loc ) const +{ + stack[loc] = new ObjectConstCalcer( mimp->copy() ); +} + +void PushStackNode::checkDependsOnGiven( std::vector<bool>&, int ) const { + // pushstacknode depends on nothing.. + return; +} + +int PushStackNode::id() const { return ID_PushStack; } + +PushStackNode::~PushStackNode() +{ + delete mimp; +} + +ObjectHierarchy::Node* PushStackNode::copy() const +{ + return new PushStackNode( mimp->copy() ); +} + +void PushStackNode::apply( std::vector<const ObjectImp*>& stack, + int loc, const KigDocument& ) const +{ + stack[loc] = mimp->copy(); +} + +class ApplyTypeNode + : public ObjectHierarchy::Node +{ + const ObjectType* mtype; + std::vector<int> mparents; +public: + ApplyTypeNode( const ObjectType* type, const std::vector<int>& parents ) + : mtype( type ), mparents( parents ) {} + ~ApplyTypeNode(); + Node* copy() const; + + const ObjectType* type() const { return mtype; } + const std::vector<int>& parents() const { return mparents; } + + int id() const; + void apply( std::vector<const ObjectImp*>& stack, + int loc, const KigDocument& ) const; + void apply( std::vector<ObjectCalcer*>& stack, int loc ) const; + + void checkDependsOnGiven( std::vector<bool>& dependsstack, int loc ) const; + void checkArgumentsUsed( std::vector<bool>& usedstack ) const; +}; + +int ApplyTypeNode::id() const { return ID_ApplyType; } + +void ApplyTypeNode::checkArgumentsUsed( std::vector<bool>& usedstack ) const +{ + for ( uint i = 0; i < mparents.size(); ++i ) + { + usedstack[mparents[i]] = true; + } +} + +void ApplyTypeNode::checkDependsOnGiven( std::vector<bool>& dependsstack, int loc ) const +{ + bool result = false; + for ( uint i = 0; i < mparents.size(); ++i ) + if ( dependsstack[mparents[i]] == true ) result = true; + dependsstack[loc] = result; +} + +ApplyTypeNode::~ApplyTypeNode() +{ +} + +ObjectHierarchy::Node* ApplyTypeNode::copy() const +{ + return new ApplyTypeNode( mtype, mparents ); +} + +void ApplyTypeNode::apply( std::vector<ObjectCalcer*>& stack, int loc ) const +{ + std::vector<ObjectCalcer*> parents; + for ( uint i = 0; i < mparents.size(); ++i ) + parents.push_back( stack[ mparents[i] ] ); + stack[loc] = new ObjectTypeCalcer( mtype, parents ); +} + +void ApplyTypeNode::apply( std::vector<const ObjectImp*>& stack, + int loc, const KigDocument& doc ) const +{ + Args args; + for ( uint i = 0; i < mparents.size(); ++i ) + args.push_back( stack[mparents[i]] ); + args = mtype->sortArgs( args ); + stack[loc] = mtype->calc( args, doc ); +} + +class FetchPropertyNode + : public ObjectHierarchy::Node +{ + mutable int mpropid; + int mparent; + const QCString mname; +public: + // propid is a cache of the location of name in the parent's + // propertiesInternalNames(), just as it is in PropertyObject. We + // don't want to ever save this value, since we cannot guarantee it + // remains consistent if we add properties some place.. + FetchPropertyNode( const int parent, const QCString& name, const int propid = -1 ) + : mpropid( propid ), mparent( parent ), mname( name ) {} + ~FetchPropertyNode(); + Node* copy() const; + + void checkDependsOnGiven( std::vector<bool>& dependsstack, int loc ) const; + void checkArgumentsUsed( std::vector<bool>& usedstack ) const; + int parent() const { return mparent; } + const QCString& propinternalname() const { return mname; } + + int id() const; + void apply( std::vector<const ObjectImp*>& stack, + int loc, const KigDocument& ) const; + void apply( std::vector<ObjectCalcer*>& stack, int loc ) const; +}; + +FetchPropertyNode::~FetchPropertyNode() +{ +} + +void FetchPropertyNode::checkArgumentsUsed( std::vector<bool>& usedstack ) const +{ + usedstack[mparent] = true; +} + +void FetchPropertyNode::checkDependsOnGiven( std::vector<bool>& dependsstack, int loc ) const +{ + dependsstack[loc] = dependsstack[mparent]; +} + +ObjectHierarchy::Node* FetchPropertyNode::copy() const +{ + return new FetchPropertyNode( mparent, mname, mpropid ); +} + +int FetchPropertyNode::id() const +{ + return ID_FetchProp; +} + +void FetchPropertyNode::apply( std::vector<const ObjectImp*>& stack, + int loc, const KigDocument& d ) const +{ + assert( stack[mparent] ); + if ( mpropid == -1 ) mpropid = stack[mparent]->propertiesInternalNames().findIndex( mname ); + if ( mpropid != -1 ) + stack[loc] = stack[mparent]->property( mpropid, d ); + else + stack[loc] = new InvalidImp(); +} + +void FetchPropertyNode::apply( std::vector<ObjectCalcer*>& stack, int loc ) const +{ + if ( mpropid == -1 ) + mpropid = stack[mparent]->imp()->propertiesInternalNames().findIndex( mname ); + assert( mpropid != -1 ); + stack[loc] = new ObjectPropertyCalcer( stack[mparent], mpropid ); +} + +std::vector<ObjectImp*> ObjectHierarchy::calc( const Args& a, const KigDocument& doc ) const +{ + assert( a.size() == mnumberofargs ); + for ( uint i = 0; i < a.size(); ++i ) + assert( a[i]->inherits( margrequirements[i] ) ); + + std::vector<const ObjectImp*> stack; + stack.resize( mnodes.size() + mnumberofargs, 0 ); + std::copy( a.begin(), a.end(), stack.begin() ); + for( uint i = 0; i < mnodes.size(); ++i ) + { + mnodes[i]->apply( stack, mnumberofargs + i, doc ); + }; + for ( uint i = mnumberofargs; i < stack.size() - mnumberofresults; ++i ) + delete stack[i]; + if ( stack.size() < mnumberofargs + mnumberofresults ) + { + std::vector<ObjectImp*> ret; + ret.push_back( new InvalidImp ); + return ret; + } + else + { + std::vector<ObjectImp*> ret; + for ( uint i = stack.size() - mnumberofresults; i < stack.size(); ++i ) + ret.push_back( const_cast<ObjectImp*>( stack[i] ) ); + return ret; + }; +} + +int ObjectHierarchy::visit( const ObjectCalcer* o, std::map<const ObjectCalcer*, int>& seenmap, + bool needed, bool neededatend ) +{ + using namespace std; + + std::map<const ObjectCalcer*, int>::iterator smi = seenmap.find( o ); + if ( smi != seenmap.end() ) + { + if ( neededatend ) + { + // neededatend means that this object is one of the resultant + // objects. Therefore, its node has to appear at the end, + // because that's where we expect it.. We therefore copy it + // there using CopyObjectType.. + int ret = mnumberofargs + mnodes.size(); + std::vector<int> parents; + parents.push_back( smi->second ); + mnodes.push_back( new ApplyTypeNode( CopyObjectType::instance(), parents ) ); + return ret; + } + else return smi->second; + } + + std::vector<ObjectCalcer*> p( o->parents() ); + // we check if o descends from the given objects.. + bool descendsfromgiven = false; + std::vector<int> parents; + parents.resize( p.size(), -1 ); + for ( uint i = 0; i < p.size(); ++i ) + { + int v = visit( p[i], seenmap, false ); + parents[i] = v; + descendsfromgiven |= (v != -1); + }; + + if ( ! descendsfromgiven && ! ( needed && o->imp()->isCache() ) ) + { + if ( needed ) + { + assert( ! o->imp()->isCache() ); + // o is an object that does not depend on the given objects, but + // is needed by other objects, so we just have to just save its + // current value here. + Node* node = new PushStackNode( o->imp()->copy() ); + mnodes.push_back( node ); + int ret = mnodes.size() + mnumberofargs - 1; + seenmap[o] = ret; + return ret; + } + else + return -1; + }; + + return storeObject( o, p, parents, seenmap ); +} + +ObjectHierarchy::~ObjectHierarchy() +{ + for ( uint i = 0; i < mnodes.size(); ++i ) delete mnodes[i]; +} + +ObjectHierarchy::ObjectHierarchy( const ObjectHierarchy& h ) + : mnumberofargs( h.mnumberofargs ), mnumberofresults( h.mnumberofresults ), + margrequirements( h.margrequirements ), musetexts( h.musetexts ), + mselectstatements( h.mselectstatements ) +{ + mnodes.reserve( h.mnodes.size() ); + for ( uint i = 0; i < h.mnodes.size(); ++i ) + mnodes.push_back( h.mnodes[i]->copy() ); +} + +ObjectHierarchy ObjectHierarchy::withFixedArgs( const Args& a ) const +{ + assert( a.size() <= mnumberofargs ); + ObjectHierarchy ret( *this ); + + ret.mnumberofargs -= a.size(); + ret.margrequirements.resize( ret.mnumberofargs ); + + std::vector<Node*> newnodes( mnodes.size() + a.size() ); + std::vector<Node*>::iterator newnodesiter = newnodes.begin(); + for ( uint i = 0; i < a.size(); ++i ) + { + assert( ! a[i]->isCache() ); + *newnodesiter++ = new PushStackNode( a[i]->copy() ); + }; + std::copy( ret.mnodes.begin(), ret.mnodes.end(), newnodesiter ); + ret.mnodes = newnodes; + + return ret; +} + +void ObjectHierarchy::init( const std::vector<ObjectCalcer*>& from, const std::vector<ObjectCalcer*>& to ) +{ + mnumberofargs = from.size(); + mnumberofresults = to.size(); + margrequirements.resize( from.size(), ObjectImp::stype() ); + musetexts.resize( margrequirements.size(), "" ); + std::map<const ObjectCalcer*, int> seenmap; + for ( uint i = 0; i < from.size(); ++i ) + seenmap[from[i]] = i; + for ( std::vector<ObjectCalcer*>::const_iterator i = to.begin(); i != to.end(); ++i ) + { + std::vector<ObjectCalcer*> parents = (*i)->parents(); + for ( std::vector<ObjectCalcer*>::const_iterator j = parents.begin(); + j != parents.end(); ++j ) + visit( *j, seenmap, true ); + } + for ( std::vector<ObjectCalcer*>::const_iterator i = to.begin(); i != to.end(); ++i ) + visit( *i, seenmap, true, true ); + + mselectstatements.resize( margrequirements.size(), "" ); +} + +ObjectHierarchy::ObjectHierarchy( const std::vector<ObjectCalcer*>& from, const ObjectCalcer* to ) +{ + std::vector<ObjectCalcer*> tov; + tov.push_back( const_cast<ObjectCalcer*>( to ) ); + init( from, tov ); +} + +ObjectHierarchy::ObjectHierarchy( const std::vector<ObjectCalcer*>& from, const std::vector<ObjectCalcer*>& to ) +{ + init( from, to ); +} + +void ObjectHierarchy::serialize( QDomElement& parent, QDomDocument& doc ) const +{ + int id = 1; + for ( uint i = 0; i < mnumberofargs; ++i ) + { + QDomElement e = doc.createElement( "input" ); + e.setAttribute( "id", id++ ); + e.setAttribute( "requirement", margrequirements[i]->internalName() ); + // we don't save these atm, since the user can't define them. + // we only load them from builtin macro's. +// QDomElement ut = doc.createElement( "UseText" ); +// ut.appendChild( doc.createTextNode( QString::fromLatin1(musetexts[i].c_str() ) ) ); +// e.appendChild( ut ); +// QDomElement ss = doc.createElement( "SelectStatement" ); +// ss.appendChild( doc.createTextNode( QString::fromLatin1(mselectstatements[i].c_str() ) ) ); +// e.appendChild( ss ); + parent.appendChild( e ); + } + + for ( uint i = 0; i < mnodes.size(); ++i ) + { + bool result = mnodes.size() - ( id - mnumberofargs - 1 ) <= mnumberofresults; + QDomElement e = doc.createElement( result ? "result" : "intermediate" ); + e.setAttribute( "id", id++ ); + + if ( mnodes[i]->id() == Node::ID_ApplyType ) + { + const ApplyTypeNode* node = static_cast<const ApplyTypeNode*>( mnodes[i] ); + e.setAttribute( "action", "calc" ); + e.setAttribute( "type", QString::fromLatin1( node->type()->fullName() ) ); + for ( uint i = 0; i < node->parents().size(); ++i ) + { + int parent = node->parents()[i] + 1; + QDomElement arge = doc.createElement( "arg" ); + arge.appendChild( doc.createTextNode( QString::number( parent ) ) ); + e.appendChild( arge ); + }; + } + else if ( mnodes[i]->id() == Node::ID_FetchProp ) + { + const FetchPropertyNode* node = static_cast<const FetchPropertyNode*>( mnodes[i] ); + e.setAttribute( "action", "fetch-property" ); + e.setAttribute( "property", node->propinternalname() ); + QDomElement arge = doc.createElement( "arg" ); + arge.appendChild( doc.createTextNode( QString::number( node->parent() + 1 ) ) ); + e.appendChild( arge ); + } + else + { + assert( mnodes[i]->id() == ObjectHierarchy::Node::ID_PushStack ); + const PushStackNode* node = static_cast<const PushStackNode*>( mnodes[i] ); + e.setAttribute( "action", "push" ); + QString type = ObjectImpFactory::instance()->serialize( *node->imp(), e, doc ); + e.setAttribute( "type", type ); + }; + + parent.appendChild( e ); + }; +} + +ObjectHierarchy::ObjectHierarchy() + : mnumberofargs( 0 ), mnumberofresults( 0 ) +{ +} + +ObjectHierarchy* ObjectHierarchy::buildSafeObjectHierarchy( const QDomElement& parent, QString& error ) +{ +#define KIG_GENERIC_PARSE_ERROR \ + { \ + error = i18n( "An error was encountered at line %1 in file %2." ) \ + .arg( __LINE__ ).arg( __FILE__ ); \ + return 0; \ + } + + ObjectHierarchy* obhi = new ObjectHierarchy(); + + bool ok = true; + QString tmp; + QDomElement e = parent.firstChild().toElement(); + for (; !e.isNull(); e = e.nextSibling().toElement() ) + { + if ( e.tagName() != "input" ) break; + + tmp = e.attribute( "id" ); + uint id = tmp.toInt( &ok ); + if ( !ok ) KIG_GENERIC_PARSE_ERROR; + + obhi->mnumberofargs = kMax( id, obhi->mnumberofargs ); + + tmp = e.attribute( "requirement" ); + const ObjectImpType* req = ObjectImpType::typeFromInternalName( tmp.latin1() ); + if ( req == 0 ) req = ObjectImp::stype(); // sucks, i know.. + obhi->margrequirements.resize( obhi->mnumberofargs, ObjectImp::stype() ); + obhi->musetexts.resize( obhi->mnumberofargs, "" ); + obhi->mselectstatements.resize( obhi->mnumberofargs, "" ); + obhi->margrequirements[id - 1] = req; + obhi->musetexts[id - 1] = req->selectStatement(); + QDomElement esub = e.firstChild().toElement(); + for ( ; !esub.isNull(); esub = esub.nextSibling().toElement() ) + { + if ( esub.tagName() == "UseText" ) + { + obhi->musetexts[id - 1] = esub.text().latin1(); + } + else if ( esub.tagName() == "SelectStatement" ) + { + obhi->mselectstatements[id - 1] = esub.text().latin1(); + } + else + { + // broken file ? ignore... + } + } + } + for (; !e.isNull(); e = e.nextSibling().toElement() ) + { + bool result = e.tagName() == "result"; + if ( result ) ++obhi->mnumberofresults; + + tmp = e.attribute( "id" ); + int id = tmp.toInt( &ok ); + if ( !ok ) KIG_GENERIC_PARSE_ERROR; + + tmp = e.attribute( "action" ); + Node* newnode = 0; + if ( tmp == "calc" ) + { + // ApplyTypeNode + QCString typen = e.attribute( "type" ).latin1(); + const ObjectType* type = ObjectTypeFactory::instance()->find( typen ); + if ( ! type ) + { + error = i18n( "This Kig file uses an object of type \"%1\", " + "which this Kig version does not support." + "Perhaps you have compiled Kig without support " + "for this object type," + "or perhaps you are using an older Kig version." ).arg( typen ); + return 0; + } + + std::vector<int> parents; + for ( QDomNode p = e.firstChild(); !p.isNull(); p = p.nextSibling() ) + { + QDomElement q = p.toElement(); + if ( q.isNull() ) KIG_GENERIC_PARSE_ERROR; // see above + if ( q.tagName() != "arg" ) KIG_GENERIC_PARSE_ERROR; + int pid = q.text().toInt(&ok ); + if ( !ok ) KIG_GENERIC_PARSE_ERROR; + parents.push_back( pid - 1 ); + }; + newnode = new ApplyTypeNode( type, parents ); + } + else if ( tmp == "fetch-property" ) + { + // FetchPropertyNode + QCString propname = e.attribute( "property" ).latin1(); + QDomElement arge = e.firstChild().toElement(); + int parent = arge.text().toInt( &ok ); + if ( !ok ) KIG_GENERIC_PARSE_ERROR; + newnode = new FetchPropertyNode( parent - 1, propname ); + } + else + { + // PushStackNode + if ( e.attribute( "action" ) != "push" ) KIG_GENERIC_PARSE_ERROR; + QString typen = e.attribute( "type" ); + if ( typen.isNull() ) KIG_GENERIC_PARSE_ERROR; + ObjectImp* imp = ObjectImpFactory::instance()->deserialize( typen, e, error ); + if ( ( ! imp ) && !error.isEmpty() ) return 0; + newnode = new PushStackNode( imp ); + }; + obhi->mnodes.resize( kMax( size_t(id - obhi->mnumberofargs), obhi->mnodes.size() ) ); + obhi->mnodes[id - obhi->mnumberofargs - 1] = newnode; + }; + + // if we are here, all went fine + return obhi; +} + +ArgsParser ObjectHierarchy::argParser() const +{ + std::vector<ArgsParser::spec> specs; + for ( uint i = 0; i < margrequirements.size(); ++i ) + { + const ObjectImpType* req = margrequirements[i]; + ArgsParser::spec spec; + spec.type = req; + spec.usetext = musetexts[i]; + spec.selectstat = mselectstatements[i]; + specs.push_back( spec ); + }; + return ArgsParser( specs ); +} + +std::vector<ObjectCalcer*> ObjectHierarchy::buildObjects( const std::vector<ObjectCalcer*>& os, const KigDocument& doc ) const +{ + assert( os.size() == mnumberofargs ); + for ( uint i = 0; i < os.size(); ++i ) + assert( os[i]->imp()->inherits( margrequirements[i] ) ); + + std::vector<ObjectCalcer*> stack; + stack.resize( mnodes.size() + mnumberofargs, 0 ); + std::copy( os.begin(), os.end(), stack.begin() ); + + for( uint i = 0; i < mnodes.size(); ++i ) + { + mnodes[i]->apply( stack, mnumberofargs + i ); + stack[mnumberofargs + i]->calc( doc ); + }; + + std::vector<ObjectCalcer*> ret( stack.end() - mnumberofresults, stack.end() ); + + return ret; +} + +const ObjectImpType* ObjectHierarchy::idOfLastResult() const +{ + const Node* n = mnodes.back(); + if ( n->id() == Node::ID_PushStack ) + return static_cast<const PushStackNode*>( n )->imp()->type(); + else if ( n->id() == Node::ID_FetchProp ) + return ObjectImp::stype(); + else + return static_cast<const ApplyTypeNode*>( n )->type()->resultId(); +} + +ObjectHierarchy ObjectHierarchy::transformFinalObject( const Transformation& t ) const +{ + assert( mnumberofresults == 1 ); + ObjectHierarchy ret( *this ); + ret.mnodes.push_back( new PushStackNode( new TransformationImp( t ) ) ); + + std::vector<int> parents; + parents.push_back( ret.mnodes.size() - 1); + parents.push_back( ret.mnodes.size() ); + const ObjectType* type = ApplyTransformationObjectType::instance(); + ret.mnodes.push_back( new ApplyTypeNode( type, parents ) ); + return ret; +} + +bool operator==( const ObjectHierarchy& lhs, const ObjectHierarchy& rhs ) +{ + if ( ! ( lhs.mnumberofargs == rhs.mnumberofargs && + lhs.mnumberofresults == rhs.mnumberofresults && + lhs.margrequirements == rhs.margrequirements && + lhs.mnodes.size() == rhs.mnodes.size() ) ) + return false; + + // this isn't entirely correct, but it will do, because we don't + // really want to know whether the hierarchies are different, but + // whether rhs has changed with regard to lhs.. + for ( uint i = 0; i < lhs.mnodes.size(); ++i ) + if ( lhs.mnodes[i] != lhs.mnodes[i] ) + return false; + + return true; +} + +bool ObjectHierarchy::resultDoesNotDependOnGiven() const +{ + std::vector<bool> dependsstack( mnodes.size() + mnumberofargs, false ); + + for ( uint i = 0; i < mnumberofargs; ++i ) + dependsstack[i] = true; + for ( uint i = 0; i < mnodes.size(); ++i ) + mnodes[i]->checkDependsOnGiven( dependsstack, i + mnumberofargs ); + for ( uint i = dependsstack.size() - mnumberofresults; i < dependsstack.size(); ++i ) + if ( !dependsstack[i] ) + return true; + return false; +} + +// returns the "minimum" of a and b ( in the partially ordered set of +// ObjectImpType's, using the inherits member function as comparison, +// if you for some reason like this sort of non-sense ;) ). This +// basically means: return the type that inherits the other type, +// because if another type inherits the lowermost type, then it will +// also inherit the other.. +const ObjectImpType* lowermost( const ObjectImpType* a, const ObjectImpType* b ) +{ + if ( a->inherits( b ) ) return a; + assert( b->inherits( a ) ); + return b; +} + +// this function is part of the visit procedure really. It is +// factored out, because it recurses for cache ObjectImp's. What this +// does is, it makes sure that object o is calcable, by putting +// appropriate Node's in mnodes.. po is o->parents() and pl contains +// the location of objects that are already in mnodes and -1 +// otherwise.. -1 means we have to store their ObjectImp, unless +// they're cache ObjectImp's etc. +int ObjectHierarchy::storeObject( const ObjectCalcer* o, const std::vector<ObjectCalcer*>& po, std::vector<int>& pl, + std::map<const ObjectCalcer*, int>& seenmap ) +{ + for ( uint i = 0; i < po.size(); ++i ) + { + if ( pl[i] == -1 ) + { + // we can't store cache ObjectImp's.. + if ( po[i]->imp()->isCache() ) + { + pl[i] = visit( po[i], seenmap, true, false ); + } + else + { + Node* argnode = new PushStackNode( po[i]->imp()->copy() ); + mnodes.push_back( argnode ); + int argloc = mnumberofargs + mnodes.size() - 1; + seenmap[po[i]] = argloc; + pl[i] = argloc; + }; + } + else if ( (uint) pl[i] < mnumberofargs ) + { + ObjectCalcer* parent = o->parents()[i]; + std::vector<ObjectCalcer*> opl = o->parents(); + + margrequirements[pl[i]] = + lowermost( margrequirements[pl[i]], + o->impRequirement( parent, opl ) ); + musetexts[pl[i]] = margrequirements[pl[i]]->selectStatement(); + }; + }; + if ( dynamic_cast<const ObjectTypeCalcer*>( o ) ) + mnodes.push_back( new ApplyTypeNode( static_cast<const ObjectTypeCalcer*>( o )->type(), pl ) ); + else if ( dynamic_cast<const ObjectPropertyCalcer*>( o ) ) + { + assert( pl.size() == 1 ); + int parent = pl.front(); + ObjectCalcer* op = po.front(); + assert( op ); + uint propid = static_cast<const ObjectPropertyCalcer*>( o )->propId(); + assert( propid < op->imp()->propertiesInternalNames().size() ); + mnodes.push_back( new FetchPropertyNode( parent, op->imp()->propertiesInternalNames()[propid], propid ) ); + } + else + assert( false ); + seenmap[o] = mnumberofargs + mnodes.size() - 1; + return mnumberofargs + mnodes.size() - 1; +} + +ObjectHierarchy::ObjectHierarchy( const ObjectCalcer* from, const ObjectCalcer* to ) +{ + std::vector<ObjectCalcer*> fromv; + fromv.push_back( const_cast<ObjectCalcer*>( from ) ); + std::vector<ObjectCalcer*> tov; + tov.push_back( const_cast<ObjectCalcer*>( to ) ); + init( fromv, tov ); +} + +bool ObjectHierarchy::allGivenObjectsUsed() const +{ + std::vector<bool> usedstack( mnodes.size() + mnumberofargs, false ); + for ( uint i = mnodes.size() - mnumberofresults; i < mnodes.size(); ++i ) + usedstack[i + mnumberofargs] = true; + for ( int i = mnodes.size() - 1; i >= 0; --i ) + if ( usedstack[i + mnumberofargs] ) + mnodes[i]->checkArgumentsUsed( usedstack ); + for ( uint i = 0; i < mnumberofargs; ++i ) + if ( ! usedstack[i] ) return false; + return true; +} + diff --git a/kig/misc/object_hierarchy.h b/kig/misc/object_hierarchy.h new file mode 100644 index 00000000..3133dc7c --- /dev/null +++ b/kig/misc/object_hierarchy.h @@ -0,0 +1,111 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_MISC_OBJECT_HIERARCHY_H +#define KIG_MISC_OBJECT_HIERARCHY_H + +#include "../objects/common.h" + +#include <map> +#include <vector> +#include <string> + +class ObjectImpType; +class ArgsParser; + +class ObjectHierarchy +{ +public: + class Node; +private: + std::vector<Node*> mnodes; + uint mnumberofargs; + uint mnumberofresults; + std::vector<const ObjectImpType*> margrequirements; + std::vector<std::string> musetexts; + std::vector<std::string> mselectstatements; + + // these two are really part of the constructor... + int visit( const ObjectCalcer* o, std::map<const ObjectCalcer*, int>&, + bool needed, bool neededatend = false); + int storeObject( const ObjectCalcer*, const std::vector<ObjectCalcer*>& po, std::vector<int>& pl, + std::map<const ObjectCalcer*, int>& seenmap ); + + friend bool operator==( const ObjectHierarchy& lhs, const ObjectHierarchy& rhs ); + + void init( const std::vector<ObjectCalcer*>& from, const std::vector<ObjectCalcer*>& to ); + + /** + * this constructor is private since it should be used only by the static + * constructor buildSafeObjectHierarchy + * + * \see ObjectHierarchy::buildSafeObjectHierarchy + */ + ObjectHierarchy(); + +public: + ObjectHierarchy( const ObjectCalcer* from, const ObjectCalcer* to ); + ObjectHierarchy( const std::vector<ObjectCalcer*>& from, const ObjectCalcer* to ); + ObjectHierarchy( const std::vector<ObjectCalcer*>& from, const std::vector<ObjectCalcer*>& to ); + ObjectHierarchy( const ObjectHierarchy& h ); + ~ObjectHierarchy(); + + /** + * this creates a new ObjectHierarchy, that takes a.size() less + * arguments, but uses copies of the ObjectImp's in \p a instead.. + */ + ObjectHierarchy withFixedArgs( const Args& a ) const; + + std::vector<ObjectImp*> calc( const Args& a, const KigDocument& doc ) const; + + /** + * saves the ObjectHierarchy data in children xml tags of \p parent .. + */ + void serialize( QDomElement& parent, QDomDocument& doc ) const; + /** + * Deserialize the ObjectHierarchy data from the xml element \p parent .. + * Since this operation can fail for some reasons, we provide it as a + * static to return 0 in case of error. + */ + static ObjectHierarchy* buildSafeObjectHierarchy( const QDomElement& parent, QString& error ); +// ObjectHierarchy( const QDomElement& parent ); + + /** + * build a set of objects that interdepend according to this + * ObjectHierarchy.. Only the result objects are returned. Helper + * objects that connect the given objects with the returned objects, + * can only be found by following the returned objects' parents() + * methods.. + */ + std::vector<ObjectCalcer*> buildObjects( const std::vector<ObjectCalcer*>& os, const KigDocument& ) const; + + ArgsParser argParser() const; + + uint numberOfArgs() const { return mnumberofargs; } + uint numberOfResults() const { return mnumberofresults; } + + const ObjectImpType* idOfLastResult() const; + + bool resultDoesNotDependOnGiven() const; + bool allGivenObjectsUsed() const; + + ObjectHierarchy transformFinalObject( const Transformation& t ) const; +}; + +bool operator==( const ObjectHierarchy& lhs, const ObjectHierarchy& rhs ); + +#endif diff --git a/kig/misc/rect.cc b/kig/misc/rect.cc new file mode 100644 index 00000000..dc28de82 --- /dev/null +++ b/kig/misc/rect.cc @@ -0,0 +1,308 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "rect.h" +#include "common.h" + +bool operator==( const Rect& r, const Rect& s ) +{ + return ( r.bottomLeft() == s.bottomLeft() + && r.width() == s.width() + && r.height() == s.height() ); +} + +kdbgstream& operator<<( kdbgstream& s, const Rect& t ) +{ + s << "left: " << t.left() + << "bottom: " << t.bottom() + << "right: " << t.right() + << "top: " << t.top() + << endl; + return s; +} + +Rect::Rect( const Coordinate bottomLeft, const Coordinate topRight ) + : mBottomLeft(bottomLeft) +{ + mwidth = topRight.x - bottomLeft.x; + mheight = topRight.y - bottomLeft.y; + normalize(); +} + +Rect::Rect( const Coordinate p, const double width, const double height ) + : mBottomLeft(p), + mwidth(width), + mheight(height) +{ + normalize(); +} + +Rect::Rect( double xa, double ya, double width, double height ) + : mBottomLeft( xa, ya ), + mwidth( width ), + mheight( height ) +{ + normalize(); +} + +Rect::Rect( const Rect& r ) + : mBottomLeft (r.mBottomLeft), + mwidth(r.mwidth), + mheight(r.mheight) +{ + normalize(); +} + +Rect::Rect() + : mwidth(0), + mheight(0) +{ +} + +void Rect::setBottomLeft( const Coordinate p ) +{ + mBottomLeft = p; +} + +void Rect::setBottomRight( const Coordinate p ) +{ + mBottomLeft = p - Coordinate(mwidth,0); +} + +void Rect::setTopRight( const Coordinate p ) +{ + mBottomLeft = p - Coordinate(mwidth, mheight); +} + +void Rect::setCenter( const Coordinate p ) +{ + mBottomLeft = p - Coordinate(mwidth, mheight)/2; +} + +void Rect::setLeft( const double p ) +{ + double r = right(); + mBottomLeft.x = p; + setRight( r ); +} + +void Rect::setRight( const double p ) +{ + mwidth = p - left(); +} + +void Rect::setBottom( const double p ) +{ + double t = top(); + mBottomLeft.y = p; + setTop( t ); +} + +void Rect::setTop( const double p ) +{ + mheight = p - bottom(); +} + +void Rect::setWidth( const double w ) +{ + mwidth = w; +} + +void Rect::setHeight( const double h ) +{ + mheight = h; +} + +void Rect::normalize() +{ + if ( mwidth < 0 ) + { + mBottomLeft.x += mwidth; + mwidth = -mwidth; + }; + if ( mheight < 0 ) + { + mBottomLeft.y += mheight; + mheight = -mheight; + }; +} + +void Rect::moveBy( const Coordinate p ) +{ + mBottomLeft += p; +} + +void Rect::scale( const double r ) +{ + mwidth *= r; + mheight *= r; +} + + +QRect Rect::toQRect() const +{ + return QRect(mBottomLeft.toQPoint(), topRight().toQPoint()); +} + +Coordinate Rect::bottomLeft() const +{ + return mBottomLeft; +} + +Coordinate Rect::bottomRight() const +{ + return mBottomLeft + Coordinate(mwidth, 0); +} + +Coordinate Rect::topLeft() const +{ + return mBottomLeft + Coordinate(0, mheight); +} + +Coordinate Rect::topRight() const +{ + return mBottomLeft + Coordinate(mwidth, mheight); +} + +Coordinate Rect::center() const +{ + return mBottomLeft + Coordinate(mwidth, mheight)/2; +} + +double Rect::left() const +{ + return mBottomLeft.x; +} +double Rect::right() const +{ + return left() + mwidth; +} +double Rect::bottom() const +{ + return mBottomLeft.y; +} + +double Rect::top() const +{ + return bottom() + mheight; +} + +double Rect::width() const +{ + return mwidth; +} + +double Rect::height() const +{ + return mheight; +} + +bool Rect::contains( const Coordinate& p, double allowed_miss ) const +{ + return p.x - left() >= - allowed_miss && + p.y - bottom() >= - allowed_miss && + p.x - left() - width() <= allowed_miss && + p.y - bottom() - height() <= allowed_miss; +} + +bool Rect::contains( const Coordinate& p ) const +{ + return p.x >= left() && + p.y >= bottom() && + p.x - left() <= width() && + p.y - bottom() <= height(); +} + +bool Rect::intersects( const Rect& p ) const +{ + // never thought it was this simple :) + if( p.left() < left() && p.right() < left()) return false; + if( p.left() > right() && p.right() > right()) return false; + if( p.bottom() < bottom() && p.top() < bottom()) return false; + if( p.bottom() > top() && p.top() > top()) return false; + return true; +} + +void Rect::setContains( Coordinate p ) +{ + normalize(); + if( p.x < left() ) setLeft( p.x ); + if( p.x > right() ) setRight(p.x); + if( p.y < bottom() ) setBottom( p.y ); + if( p.y > top() ) setTop( p.y ); +} + +Rect Rect::normalized() const +{ + Rect t = *this; + (void) t.normalize(); + return t; +} + +Rect Rect::fromQRect( const QRect& r ) +{ + return Rect( r.left(), r.top(), r.right(), r.bottom() ); +} + +void Rect::setTopLeft( const Coordinate p ) +{ + Coordinate bl = Coordinate( p.x, p.y - mheight ); + setBottomLeft( bl ); +} + +Rect operator|( const Rect& lhs, const Rect& rhs ) +{ + Rect r( lhs ); + r |= rhs; + return r; +} + +void Rect::eat( const Rect& r ) +{ + setLeft( kigMin( left(), r.left() ) ); + setRight( kigMax( right(), r.right() ) ); + setBottom( kigMin( bottom(), r.bottom() ) ); + setTop( kigMax( top(), r.top() ) ); +} + +Rect Rect::matchShape( const Rect& rhs, bool shrink ) const +{ + Rect ret = *this; + Coordinate c = center(); + double v = width()/height(); // current ratio + double w = rhs.width()/rhs.height(); // wanted ratio + + // we don't show less than r, if the dimensions don't match, we + // extend r into some dimension... + if( ( v > w ) ^ shrink ) + ret.setHeight( ret.width() / w ); + else + ret.setWidth( ret.height() * w ); + + ret.setCenter(c); + return ret.normalized(); +} + +bool Rect::valid() +{ + return mBottomLeft.valid() && mwidth != double_inf && mheight != double_inf; +} + +Rect Rect::invalidRect() +{ + return Rect( Coordinate::invalidCoord(), double_inf, double_inf ); +} diff --git a/kig/misc/rect.h b/kig/misc/rect.h new file mode 100644 index 00000000..a222d1ab --- /dev/null +++ b/kig/misc/rect.h @@ -0,0 +1,140 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + + +#ifndef RECT_H +#define RECT_H + +#include "coordinate.h" + +#include <qrect.h> +#include <kdebug.h> + +/** + * like Coordinate is a QPoint replacement with doubles, this is a + * QRect replacement with doubles... + */ +class Rect +{ +public: + /** + * constructors... + */ + Rect( const Coordinate bottomLeft, const Coordinate topRight ); + Rect( const Coordinate bottomLeft, const double width, const double height ); + Rect( double xa, double ya, double width, double height ); + Rect( const Rect& r ); + Rect(); + static Rect invalidRect(); + + + bool valid(); + + void setBottomLeft( const Coordinate p ); + void setTopLeft( const Coordinate p ); + void setTopRight( const Coordinate p ); + void setBottomRight( const Coordinate p ); + void setCenter( const Coordinate p ); + void setLeft( const double p); + void setRight( const double p); + void setTop( const double p ); + void setBottom( const double p ); + void setWidth( const double w ); + void setHeight( const double h ); + /** + * this makes sure width and height are > 0 ... + */ + void normalize(); + /** + * this makes sure p is in the rect, extending it if necessary... + */ + void setContains( Coordinate p ); + /** + * moves the rect while keeping the size constant... + */ + void moveBy( const Coordinate p ); + /** + * synonym for moveBy... + */ + Rect& operator+=( const Coordinate p ) { moveBy(p); return *this; } + /** + * scale: only the size changes, topLeft is kept where it is... + */ + void scale( const double r ); + /** + * synonym for scale... + */ + Rect& operator*=( const double r ) { scale(r); return *this; } + Rect& operator/=( const double r ) { scale(1/r); return *this; } + + /** + * This expands the rect so that it contains r. It has friends + * '|=' and '|' below... + */ + void eat( const Rect& r ); + + /** + * synonym for eat.. + */ + Rect& operator|=( const Rect& rhs ) { eat( rhs ); return *this; } + + /** + * return a rect which is a copy of this rect, but has an aspect + * ratio equal to rhs's one.. if \p shrink is true, the rect will be + * shrunk, otherwise extended.. The center of the new rect is the + * same as this rect's center.. + */ + Rect matchShape( const Rect& rhs, bool shrink = false ) const; + + QRect toQRect() const; + Coordinate bottomLeft() const; + Coordinate bottomRight() const; + Coordinate topLeft() const; + Coordinate topRight() const; + Coordinate center() const; + double left() const; + double right() const; + double bottom() const; + double top() const; + double width() const; + double height() const; + bool contains( const Coordinate& p ) const; + bool contains( const Coordinate& p, double allowed_miss ) const; + bool intersects( const Rect& p ) const; + Rect normalized() const; + friend kdbgstream& operator<<( kdbgstream& s, const Rect& t ); + + static Rect fromQRect( const QRect& ); +protected: + Coordinate mBottomLeft; + double mwidth; + double mheight; +}; + +bool operator==( const Rect& r, const Rect& s ); +kdbgstream& operator<<( kdbgstream& s, const Rect& t ); +/** + * this operator returns a Rect that contains both the given + * rects.. + */ +Rect operator|( const Rect& lhs, const Rect& rhs ); + +#endif + diff --git a/kig/misc/screeninfo.cc b/kig/misc/screeninfo.cc new file mode 100644 index 00000000..c1418876 --- /dev/null +++ b/kig/misc/screeninfo.cc @@ -0,0 +1,92 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "screeninfo.h" + +#include <cmath> + +ScreenInfo::ScreenInfo( const Rect& docRect, const QRect& viewRect ) + : mkrect( docRect.normalized() ), mqrect( viewRect.normalize() ) +{ +} + +Rect ScreenInfo::fromScreen( const QRect& r ) const +{ + return Rect( + fromScreen( r.topLeft() ), + fromScreen( r.bottomRight() ) + ).normalized(); +} + +Coordinate ScreenInfo::fromScreen( const QPoint& p ) const +{ + // invert the y-axis: 0 is at the bottom ! + Coordinate t( p.x(), mqrect.height() - p.y() ); + t *= mkrect.width(); + t /= mqrect.width(); + return t + mkrect.bottomLeft(); +} + +QPoint ScreenInfo::toScreen( const Coordinate& p ) const +{ + Coordinate t = p - mkrect.bottomLeft(); + t *= mqrect.width(); + t /= mkrect.width(); + // invert the y-axis: 0 is at the bottom ! + return QPoint( (int) t.x, mqrect.height() - (int) t.y ); +} + +QRect ScreenInfo::toScreen( const Rect& r ) const +{ + return QRect( + toScreen( r.bottomLeft() ), + toScreen( r.topRight() ) + ).normalize(); +} + +double ScreenInfo::pixelWidth() const +{ + Coordinate a = fromScreen( QPoint( 0, 0 ) ); + Coordinate b = fromScreen( QPoint( 0, 1000 ) ); + return std::fabs( b.y - a.y ) / 1000; +} + +const Rect& ScreenInfo::shownRect() const +{ + return mkrect; +} + +void ScreenInfo::setShownRect( const Rect& r ) +{ + mkrect = r; +} + +const QRect ScreenInfo::viewRect() const +{ + return mqrect; +} + +void ScreenInfo::setViewRect( const QRect& r ) +{ + mqrect = r; +} + +double ScreenInfo::normalMiss( int width ) const +{ + int twidth = width == -1 ? 1 : width; + return (twidth+2)*pixelWidth(); +} diff --git a/kig/misc/screeninfo.h b/kig/misc/screeninfo.h new file mode 100644 index 00000000..b7f94c49 --- /dev/null +++ b/kig/misc/screeninfo.h @@ -0,0 +1,57 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef SCREENINFO_H +#define SCREENINFO_H + +#include <qrect.h> + +#include "rect.h" + +/** + * ScreenInfo is a simple utility class that maps a region of the + * document onto a region of the screen. It is used by both + * KigPainter and KigWidget, and the objects use it in their calc() + * method... + */ +class ScreenInfo +{ + Rect mkrect; + QRect mqrect; +public: + ScreenInfo( const Rect& docRect, const QRect& viewRect ); + + Coordinate fromScreen( const QPoint& p ) const; + Rect fromScreen( const QRect& r ) const; + + QPoint toScreen( const Coordinate& p ) const; + QRect toScreen( const Rect& r ) const; + + double pixelWidth() const; + + double normalMiss( int width ) const; + + const Rect& shownRect() const; + + void setShownRect( const Rect& r ); + + const QRect viewRect() const; + + void setViewRect( const QRect& r ); +}; + +#endif diff --git a/kig/misc/special_constructors.cc b/kig/misc/special_constructors.cc new file mode 100644 index 00000000..04c8a097 --- /dev/null +++ b/kig/misc/special_constructors.cc @@ -0,0 +1,1628 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "special_constructors.h" + +#include "calcpaths.h" +#include "common.h" +#include "conic-common.h" +#include "guiaction.h" +#include "kigpainter.h" + +#include "../kig/kig_part.h" +#include "../modes/construct_mode.h" +#include "../objects/bogus_imp.h" +#include "../objects/centerofcurvature_type.h" +#include "../objects/circle_imp.h" +#include "../objects/conic_imp.h" +#include "../objects/conic_types.h" +#include "../objects/cubic_imp.h" +#include "../objects/intersection_types.h" +#include "../objects/inversion_type.h" +#include "../objects/line_imp.h" +#include "../objects/line_type.h" +#include "../objects/locus_imp.h" +#include "../objects/object_calcer.h" +#include "../objects/object_drawer.h" +#include "../objects/object_factory.h" +#include "../objects/object_holder.h" +#include "../objects/object_imp.h" +#include "../objects/object_type.h" +#include "../objects/other_imp.h" +#include "../objects/other_type.h" +#include "../objects/point_imp.h" +#include "../objects/point_type.h" +#include "../objects/polygon_imp.h" +#include "../objects/polygon_type.h" +#include "../objects/tangent_type.h" +#include "../objects/text_imp.h" +#include "../objects/transform_types.h" + +#include <qpen.h> + +#include <klocale.h> + +#include <algorithm> +#include <functional> + +class ConicConicIntersectionConstructor + : public StandardConstructorBase +{ +protected: + ArgsParser mparser; +public: + ConicConicIntersectionConstructor(); + ~ConicConicIntersectionConstructor(); + + void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, + const KigDocument& ) const; + std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, KigDocument& d, KigWidget& w ) const; + void plug( KigPart* doc, KigGUIAction* kact ); + + bool isTransform() const; +}; + +class ConicLineIntersectionConstructor + : public MultiObjectTypeConstructor +{ +public: + ConicLineIntersectionConstructor(); + ~ConicLineIntersectionConstructor(); +}; + +class ArcLineIntersectionConstructor + : public MultiObjectTypeConstructor +{ +public: + ArcLineIntersectionConstructor(); + ~ArcLineIntersectionConstructor(); +}; + +ConicRadicalConstructor::ConicRadicalConstructor() + : StandardConstructorBase( + I18N_NOOP( "Radical Lines for Conics" ), + I18N_NOOP( "The lines constructed through the intersections " + "of two conics. This is also defined for " + "non-intersecting conics." ), + "conicsradicalline", mparser ), + mtype( ConicRadicalType::instance() ), + mparser( mtype->argsParser().without( IntImp::stype() ) ) +{ +} + +ConicRadicalConstructor::~ConicRadicalConstructor() +{ +} + +void ConicRadicalConstructor::drawprelim( + const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, const KigDocument& doc ) const +{ + if ( parents.size() == 2 && parents[0]->imp()->inherits( ConicImp::stype() ) && + parents[1]->imp()->inherits( ConicImp::stype() ) ) + { + Args args; + std::transform( parents.begin(), parents.end(), + std::back_inserter( args ), std::mem_fun( &ObjectCalcer::imp ) ); + for ( int i = -1; i < 2; i += 2 ) + { + IntImp root( i ); + IntImp zeroindex( 1 ); + args.push_back( &root ); + args.push_back( &zeroindex ); + ObjectImp* data = mtype->calc( args, doc ); + drawer.draw( *data, p, true ); + delete data; data = 0; + args.pop_back(); + args.pop_back(); + }; + }; +} + +std::vector<ObjectHolder*> ConicRadicalConstructor::build( const std::vector<ObjectCalcer*>& os, KigDocument&, KigWidget& ) const +{ + using namespace std; + std::vector<ObjectHolder*> ret; + ObjectCalcer* zeroindexcalcer = new ObjectConstCalcer( new IntImp( 1 ) ); + for ( int i = -1; i < 2; i += 2 ) + { + std::vector<ObjectCalcer*> args; + std::copy( os.begin(), os.end(), back_inserter( args ) ); + args.push_back( new ObjectConstCalcer( new IntImp( i ) ) ); + // we use only one zeroindex dataobject, so that if you switch one + // radical line around, then the other switches along.. + args.push_back( zeroindexcalcer ); + ret.push_back( + new ObjectHolder( new ObjectTypeCalcer( mtype, args ) ) ); + }; + return ret; +} + +static const struct ArgsParser::spec argsspecpp[] = +{ + { PointImp::stype(), I18N_NOOP( "Moving Point" ), + I18N_NOOP( "Select the moving point, which will be moved around while drawing the locus..." ), false }, + { PointImp::stype(), I18N_NOOP( "Following Point" ), + I18N_NOOP( "Select the following point, whose locations the locus will be drawn through..." ), true } +}; + +LocusConstructor::LocusConstructor() + : StandardConstructorBase( I18N_NOOP( "Locus" ), I18N_NOOP( "A locus" ), + "locus", margsparser ), + margsparser( argsspecpp, 2 ) +{ +} + +LocusConstructor::~LocusConstructor() +{ +} + +void LocusConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, + const KigDocument& ) const +{ + // this function is rather ugly, but it is necessary to do it this + // way in order to play nice with Kig's design.. + + if ( parents.size() != 2 ) return; + const ObjectTypeCalcer* constrained = dynamic_cast<ObjectTypeCalcer*>( parents.front() ); + const ObjectCalcer* moving = parents.back(); + if ( ! constrained || ! constrained->type()->inherits( ObjectType::ID_ConstrainedPointType ) ) + { + // moving is in fact the constrained point.. swap them.. + moving = parents.front(); + constrained = dynamic_cast<const ObjectTypeCalcer*>( parents.back() ); + assert( constrained ); + }; + assert( constrained->type()->inherits( ObjectType::ID_ConstrainedPointType ) ); + + const ObjectImp* oimp = constrained->parents().back()->imp(); + if( !oimp->inherits( CurveImp::stype() ) ) + oimp = constrained->parents().front()->imp(); + assert( oimp->inherits( CurveImp::stype() ) ); + const CurveImp* cimp = static_cast<const CurveImp*>( oimp ); + + ObjectHierarchy hier( constrained, moving ); + + LocusImp limp( cimp->copy(), hier ); + drawer.draw( limp, p, true ); +} + +const int LocusConstructor::wantArgs( + const std::vector<ObjectCalcer*>& os, const KigDocument&, const KigWidget& + ) const +{ + int ret = margsparser.check( os ); + if ( ret == ArgsParser::Invalid ) return ret; + else if ( os.size() != 2 ) return ret; + if ( dynamic_cast<ObjectTypeCalcer*>( os.front() ) && + static_cast<ObjectTypeCalcer*>( os.front() )->type()->inherits( ObjectType::ID_ConstrainedPointType ) ) + { + std::set<ObjectCalcer*> children = getAllChildren( os.front() ); + return children.find( os.back() ) != children.end() ? ret : ArgsParser::Invalid; + } + if ( dynamic_cast<ObjectTypeCalcer*>( os.back() ) && + static_cast<ObjectTypeCalcer*>( os.back() )->type()->inherits( ObjectType::ID_ConstrainedPointType ) ) + { + std::set<ObjectCalcer*> children = getAllChildren( os.back() ); + return children.find( os.front() ) != children.end() ? ret : ArgsParser::Invalid; + } + return ArgsParser::Invalid; +} + +std::vector<ObjectHolder*> LocusConstructor::build( const std::vector<ObjectCalcer*>& parents, KigDocument&, KigWidget& ) const +{ + std::vector<ObjectHolder*> ret; + assert( parents.size() == 2 ); + + ObjectTypeCalcer* constrained = dynamic_cast<ObjectTypeCalcer*>( parents.front() ); + ObjectCalcer* moving = parents.back(); + if ( ! constrained || ! constrained->type()->inherits( ObjectType::ID_ConstrainedPointType ) ) + { + // moving is in fact the constrained point.. swap them.. + moving = parents.front(); + constrained = dynamic_cast<ObjectTypeCalcer*>( parents.back() ); + assert( constrained ); + }; + assert( constrained->type()->inherits( ObjectType::ID_ConstrainedPointType ) ); + + ret.push_back( ObjectFactory::instance()->locus( constrained, moving ) ); + return ret; +} + +QString LocusConstructor::useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& os, + const KigDocument&, const KigWidget& ) const +{ + if ( dynamic_cast<const ObjectTypeCalcer*>( &o ) && + static_cast<const ObjectTypeCalcer&>( o ).type()->inherits( ObjectType::ID_ConstrainedPointType ) && + ( os.empty() || !dynamic_cast<ObjectTypeCalcer*>( os[0] ) || + !static_cast<const ObjectTypeCalcer*>( os[0] )->type()->inherits( ObjectType::ID_ConstrainedPointType ) ) + ) return i18n( "Moving Point" ); + else return i18n( "Dependent Point" ); +} + +void ConicRadicalConstructor::plug( KigPart*, KigGUIAction* ) +{ +} + +void LocusConstructor::plug( KigPart*, KigGUIAction* ) +{ +} + +bool ConicRadicalConstructor::isTransform() const +{ + return mtype->isTransform(); +} + +bool LocusConstructor::isTransform() const +{ + return false; +} + +/* + * generic polygon constructor + */ + +PolygonBNPTypeConstructor::PolygonBNPTypeConstructor() + : mtype( PolygonBNPType::instance() ) +{ +} + +PolygonBNPTypeConstructor::~PolygonBNPTypeConstructor() +{ +} + +const QString PolygonBNPTypeConstructor::descriptiveName() const +{ + return i18n("Polygon by Its Vertices"); +} + +const QString PolygonBNPTypeConstructor::description() const +{ + return i18n("Construct a polygon by giving its vertices"); +} + +const QCString PolygonBNPTypeConstructor::iconFileName( const bool ) const +{ + return "kig_polygon"; +} + +const bool PolygonBNPTypeConstructor::isAlreadySelectedOK( + const std::vector<ObjectCalcer*>& os, const int& pos ) const +{ + if ( pos == 0 && os.size() >= 3 ) return true; + return false; +} + +const int PolygonBNPTypeConstructor::wantArgs( const std::vector<ObjectCalcer*>& os, + const KigDocument&, + const KigWidget& ) const +{ + int count=os.size() - 1; + + for ( int i = 0; i <= count; i++ ) + { + if ( ! ( os[i]->imp()->inherits( PointImp::stype() ) ) ) return ArgsParser::Invalid; + } + if ( count < 3 ) return ArgsParser::Valid; + if ( os[0] == os[count] ) return ArgsParser::Complete; + return ArgsParser::Valid; +} + +void PolygonBNPTypeConstructor::handleArgs( + const std::vector<ObjectCalcer*>& os, KigPart& d, + KigWidget& v ) const +{ + std::vector<ObjectHolder*> bos = build( os, d.document(), v ); + for ( std::vector<ObjectHolder*>::iterator i = bos.begin(); + i != bos.end(); ++i ) + { + (*i)->calc( d.document() ); + } + + d.addObjects( bos ); +} + +void PolygonBNPTypeConstructor::handlePrelim( + KigPainter& p, const std::vector<ObjectCalcer*>& os, + const KigDocument& d, const KigWidget& + ) const +{ + uint count = os.size(); + if ( count < 2 ) return; + + for ( uint i = 0; i < count; i++ ) + { + assert ( os[i]->imp()->inherits( PointImp::stype() ) ); + } + + std::vector<ObjectCalcer*> args = os; + p.setBrushStyle( Qt::NoBrush ); + p.setBrushColor( Qt::red ); + p.setPen( QPen ( Qt::red, 1) ); + p.setWidth( -1 ); // -1 means the default width for the object being + // drawn.. + + ObjectDrawer drawer( Qt::red ); + drawprelim( drawer, p, args, d ); +} + +QString PolygonBNPTypeConstructor::useText( const ObjectCalcer&, const std::vector<ObjectCalcer*>& os, + const KigDocument&, const KigWidget& ) const +{ + if ( os.size() > 3 ) + return i18n("... with this vertex (click on the first vertex to terminate construction)"); + else return i18n("Construct a polygon with this vertex"); +} + +QString PolygonBNPTypeConstructor::selectStatement( + const std::vector<ObjectCalcer*>&, const KigDocument&, + const KigWidget& ) const +{ + return i18n("Select a point to be a vertex of the new polygon..."); +} + +void PolygonBNPTypeConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, + const KigDocument& ) const +{ + if ( parents.size() < 2 ) return; + + std::vector<Coordinate> points; + + for ( uint i = 0; i < parents.size(); ++i ) + { + const Coordinate vertex = + static_cast<const PointImp*>( parents[i]->imp() )->coordinate(); + points.push_back( vertex ); + } + + if ( parents.size() == 2 ) + { + SegmentImp segment = SegmentImp( points[0], points[1] ); + drawer.draw( segment, p, true ); + } else { + PolygonImp polygon = PolygonImp( points ); + drawer.draw( polygon, p, true ); + } +} + +std::vector<ObjectHolder*> PolygonBNPTypeConstructor::build( const std::vector<ObjectCalcer*>& parents, KigDocument&, KigWidget& ) const +{ + uint count = parents.size() - 1; + assert ( count >= 3 ); + std::vector<ObjectCalcer*> args; + for ( uint i = 0; i < count; ++i ) args.push_back( parents[i] ); + ObjectTypeCalcer* calcer = new ObjectTypeCalcer( mtype, args ); + ObjectHolder* h = new ObjectHolder( calcer ); + std::vector<ObjectHolder*> ret; + ret.push_back( h ); + return ret; +} + +void PolygonBNPTypeConstructor::plug( KigPart*, KigGUIAction* ) +{ +} + +bool PolygonBNPTypeConstructor::isTransform() const +{ + return false; +} + +/* + * construction of polygon vertices + */ + +static const struct ArgsParser::spec argsspecpv[] = +{ + { PolygonImp::stype(), I18N_NOOP( "Polygon" ), + I18N_NOOP( "Construct the vertices of this polygon..." ), true } +}; + +PolygonVertexTypeConstructor::PolygonVertexTypeConstructor() + : StandardConstructorBase( I18N_NOOP( "Vertices of a Polygon" ), + I18N_NOOP( "The vertices of a polygon." ), + "polygonvertices", margsparser ), + mtype( PolygonVertexType::instance() ), + margsparser( argsspecpv, 1 ) +{ +} + +PolygonVertexTypeConstructor::~PolygonVertexTypeConstructor() +{ +} + +void PolygonVertexTypeConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, + const KigDocument& ) const +{ + if ( parents.size() != 1 ) return; + + const PolygonImp* polygon = dynamic_cast<const PolygonImp*>( parents.front()->imp() ); + const std::vector<Coordinate> points = polygon->points(); + + int sides = points.size(); + for ( int i = 0; i < sides; ++i ) + { + PointImp point = PointImp( points[i] ); + drawer.draw( point, p, true ); + } +} + +std::vector<ObjectHolder*> PolygonVertexTypeConstructor::build( const std::vector<ObjectCalcer*>& parents, KigDocument&, KigWidget& ) const +{ + std::vector<ObjectHolder*> ret; + assert( parents.size() == 1 ); + const PolygonImp* polygon = dynamic_cast<const PolygonImp*>( parents.front()->imp() ); + const std::vector<Coordinate> points = polygon->points(); + + int sides = points.size(); + + for ( int i = 0; i < sides; ++i ) + { + ObjectConstCalcer* d = new ObjectConstCalcer( new IntImp( i ) ); + std::vector<ObjectCalcer*> args( parents ); + args.push_back( d ); + ret.push_back( new ObjectHolder( new ObjectTypeCalcer( mtype, args ) ) ); + } + return ret; +} + +void PolygonVertexTypeConstructor::plug( KigPart*, KigGUIAction* ) +{ +} + +bool PolygonVertexTypeConstructor::isTransform() const +{ + return false; +} + +/* + * construction of polygon sides + */ + +static const struct ArgsParser::spec argsspecps[] = +{ + { PolygonImp::stype(), I18N_NOOP( "Polygon" ), + I18N_NOOP( "Construct the sides of this polygon..." ), false } +}; + +PolygonSideTypeConstructor::PolygonSideTypeConstructor() + : StandardConstructorBase( I18N_NOOP( "Sides of a Polygon" ), + I18N_NOOP( "The sides of a polygon." ), + "polygonsides", margsparser ), + mtype( PolygonSideType::instance() ), + margsparser( argsspecps, 1 ) +{ +} + +PolygonSideTypeConstructor::~PolygonSideTypeConstructor() +{ +} + +void PolygonSideTypeConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, + const KigDocument& ) const +{ + if ( parents.size() != 1 ) return; + + const PolygonImp* polygon = dynamic_cast<const PolygonImp*>( parents.front()->imp() ); + const std::vector<Coordinate> points = polygon->points(); + + uint sides = points.size(); + for ( uint i = 0; i < sides; ++i ) + { + uint nexti = ( i + 1 < sides )?(i + 1):0; + SegmentImp segment = SegmentImp( points[i], points[nexti] ); + drawer.draw( segment, p, true ); + } +} + +std::vector<ObjectHolder*> PolygonSideTypeConstructor::build( const std::vector<ObjectCalcer*>& parents, KigDocument&, KigWidget& ) const +{ + std::vector<ObjectHolder*> ret; + assert( parents.size() == 1 ); + const PolygonImp* polygon = dynamic_cast<const PolygonImp*>( parents.front()->imp() ); + const std::vector<Coordinate> points = polygon->points(); + + uint sides = points.size(); + + for ( uint i = 0; i < sides; ++i ) + { + ObjectConstCalcer* d = new ObjectConstCalcer( new IntImp( i ) ); + std::vector<ObjectCalcer*> args( parents ); + args.push_back( d ); + ret.push_back( new ObjectHolder( new ObjectTypeCalcer( mtype, args ) ) ); + } + return ret; +} + +void PolygonSideTypeConstructor::plug( KigPart*, KigGUIAction* ) +{ +} + +bool PolygonSideTypeConstructor::isTransform() const +{ + return false; +} + +/* + * polygon by center and vertex + */ + +PolygonBCVConstructor::PolygonBCVConstructor() + : mtype( PolygonBCVType::instance() ) +{ +} + +PolygonBCVConstructor::~PolygonBCVConstructor() +{ +} + +const QString PolygonBCVConstructor::descriptiveName() const +{ + return i18n("Regular Polygon with Given Center"); +} + +const QString PolygonBCVConstructor::description() const +{ + return i18n("Construct a regular polygon with a given center and vertex"); +} + +const QCString PolygonBCVConstructor::iconFileName( const bool ) const +{ + return "hexagonbcv"; +} + +const bool PolygonBCVConstructor::isAlreadySelectedOK( + const std::vector<ObjectCalcer*>&, const int& ) const +{ + return false; +} + +const int PolygonBCVConstructor::wantArgs( const std::vector<ObjectCalcer*>& os, + const KigDocument&, + const KigWidget& ) const +{ + if ( os.size() > 3 ) return ArgsParser::Invalid; + + uint imax = ( os.size() <= 2) ? os.size() : 2; + for ( uint i = 0; i < imax; ++i ) + if ( ! ( os[i]->imp()->inherits( PointImp::stype() ) ) ) return ArgsParser::Invalid; + + if ( os.size() < 3 ) return ArgsParser::Valid; + + if ( ! ( os[2]->imp()->inherits( BogusPointImp::stype() ) ) ) + return ArgsParser::Invalid; + + return ArgsParser::Complete; +} + +void PolygonBCVConstructor::handleArgs( + const std::vector<ObjectCalcer*>& os, KigPart& d, + KigWidget& v ) const +{ + std::vector<ObjectHolder*> bos = build( os, d.document(), v ); + for ( std::vector<ObjectHolder*>::iterator i = bos.begin(); + i != bos.end(); ++i ) + { + (*i)->calc( d.document() ); + } + + d.addObjects( bos ); +} + +void PolygonBCVConstructor::handlePrelim( + KigPainter& p, const std::vector<ObjectCalcer*>& os, + const KigDocument& d, const KigWidget& + ) const +{ + if ( os.size() < 2 ) return; + + for ( uint i = 0; i < 2; i++ ) + { + assert ( os[i]->imp()->inherits( PointImp::stype() ) ); + } + + Coordinate c = static_cast<const PointImp*>( os[0]->imp() )->coordinate(); + Coordinate v = static_cast<const PointImp*>( os[1]->imp() )->coordinate(); + + int nsides = 6; + bool moreinfo = false; + int winding = 0; // 0 means allow winding > 1 + if ( os.size() == 3 ) + { + assert ( os[2]->imp()->inherits( BogusPointImp::stype() ) ); + Coordinate cntrl = static_cast<const PointImp*>( os[2]->imp() )->coordinate(); + nsides = computeNsides( c, v, cntrl, winding ); + moreinfo = true; + } + + std::vector<ObjectCalcer*> args; + args.push_back( os[0] ); + args.push_back( os[1] ); + ObjectConstCalcer* ns = new ObjectConstCalcer( new IntImp( nsides ) ); + args.push_back( ns ); + if ( winding > 1 ) + { + ns = new ObjectConstCalcer( new IntImp( winding ) ); + args.push_back( ns ); + } + + p.setBrushStyle( Qt::NoBrush ); + p.setBrushColor( Qt::red ); + p.setPen( QPen ( Qt::red, 1) ); + p.setWidth( -1 ); // -1 means the default width for the object being + // drawn.. + + ObjectDrawer drawer( Qt::red ); + drawprelim( drawer, p, args, d ); + if ( moreinfo ) + { + p.setPointStyle( 1 ); + p.setWidth( 6 ); + double ro = 1.0/(2.5); + Coordinate where = getRotatedCoord( c, (1-ro)*c+ro*v, 4*M_PI/5.0 ); + PointImp ptn = PointImp( where ); + TextImp text = TextImp( "(5,2)", where, false ); + ptn.draw( p ); + text.draw( p ); + for ( int i = 3; i < 9; ++i ) + { + where = getRotatedCoord( c, v, 2.0*M_PI/i ); + ptn = PointImp( where ); + ptn.draw( p ); + if ( i > 5 ) continue; + text = TextImp( QString( "(%1)" ).arg(i), where, false ); + text.draw( p ); + } + p.setStyle( Qt::DotLine ); + p.setWidth( 1 ); + double radius = ( v - c ).length(); + CircleImp circle = CircleImp( c, radius ); + circle.draw( p ); + for ( int i = 2; i < 5; i++ ) + { + ro = 1.0/(i+0.5); + CircleImp circle = CircleImp( c, ro*radius ); + circle.draw( p ); + } + } + delete_all( args.begin() + 2, args.end() ); +} + +std::vector<ObjectHolder*> PolygonBCVConstructor::build( const std::vector<ObjectCalcer*>& parents, KigDocument&, KigWidget& ) const +{ + assert ( parents.size() == 3 ); + std::vector<ObjectCalcer*> args; + + Coordinate c = static_cast<const PointImp*>( parents[0]->imp() )->coordinate(); + Coordinate v = static_cast<const PointImp*>( parents[1]->imp() )->coordinate(); + Coordinate cntrl = static_cast<const PointImp*>( parents[2]->imp() )->coordinate(); + + args.push_back( parents[0] ); + args.push_back( parents[1] ); + int winding = 0; + int nsides = computeNsides( c, v, cntrl, winding ); + ObjectConstCalcer* d = new ObjectConstCalcer( new IntImp( nsides ) ); + args.push_back( d ); + if ( winding > 1 ) + { + d = new ObjectConstCalcer( new IntImp( winding ) ); + args.push_back( d ); + } + + ObjectTypeCalcer* calcer = new ObjectTypeCalcer( mtype, args ); + ObjectHolder* h = new ObjectHolder( calcer ); + std::vector<ObjectHolder*> ret; + ret.push_back( h ); + return ret; +} + +QString PolygonBCVConstructor::useText( const ObjectCalcer&, const std::vector<ObjectCalcer*>& os, + const KigDocument&, const KigWidget& ) const +{ + switch ( os.size() ) + { + case 1: + return i18n( "Construct a regular polygon with this center" ); + break; + + case 2: + return i18n( "Construct a regular polygon with this vertex" ); + break; + + case 3: + Coordinate c = static_cast<const PointImp*>( os[0]->imp() )->coordinate(); + Coordinate v = static_cast<const PointImp*>( os[1]->imp() )->coordinate(); + Coordinate cntrl = static_cast<const PointImp*>( os[2]->imp() )->coordinate(); + int winding = 0; + int nsides = computeNsides( c, v, cntrl, winding ); + + if ( winding > 1 ) + { + QString result = QString( + i18n( "Adjust the number of sides (%1/%2)" ) + ).arg( nsides ).arg( winding ); + return result; + } else + { + QString result = QString( + i18n( "Adjust the number of sides (%1)" ) + ).arg( nsides ); + return result; + } + break; + } + + return ""; +} + +QString PolygonBCVConstructor::selectStatement( + const std::vector<ObjectCalcer*>& os, const KigDocument&, + const KigWidget& ) const +{ + switch ( os.size() ) + { + case 1: + return i18n( "Select the center of the new polygon..." ); + break; + + case 2: + return i18n( "Select a vertex for the new polygon..." ); + break; + + case 3: + return i18n( "Move the cursor to get the desired number of sides..." ); + break; + } + + return ""; +} + +void PolygonBCVConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, + const KigDocument& doc ) const +{ + if ( parents.size() < 3 || parents.size() > 4 ) return; + + assert ( parents[0]->imp()->inherits( PointImp::stype() ) && + parents[1]->imp()->inherits( PointImp::stype() ) && + parents[2]->imp()->inherits( IntImp::stype() ) ); + + if ( parents.size() == 4 ) + assert ( parents[3]->imp()->inherits( IntImp::stype() ) ); + + Args args; + std::transform( parents.begin(), parents.end(), + std::back_inserter( args ), std::mem_fun( &ObjectCalcer::imp ) ); + + ObjectImp* data = mtype->calc( args, doc ); + drawer.draw( *data, p, true ); + delete data; + data = 0; +} + +void PolygonBCVConstructor::plug( KigPart*, KigGUIAction* ) +{ +} + +bool PolygonBCVConstructor::isTransform() const +{ + return false; +} + +Coordinate PolygonBCVConstructor::getRotatedCoord( const Coordinate& c, + const Coordinate& v, double alpha ) const +{ + double cosalpha = cos(alpha); + double sinalpha = sin(alpha); + double dx = v.x - c.x; + double dy = v.y - c.y; + return c + Coordinate( cosalpha*dx - sinalpha*dy, sinalpha*dx + cosalpha*dy ); +} + +int PolygonBCVConstructor::computeNsides ( const Coordinate& c, + const Coordinate& v, const Coordinate& cntrl, int& winding ) const +{ + Coordinate lvect = v - c; + Coordinate rvect = cntrl - c; + + double angle = atan2( rvect.y, rvect.x ) - atan2( lvect.y, lvect.x ); + angle = fabs( angle/(2*M_PI) ); + while ( angle > 1 ) angle -= 1; + if ( angle > 0.5 ) angle = 1 - angle; + + double realsides = 1.0/angle; // this is bigger that 2 + if ( angle == 0. ) realsides = 3; + if ( winding <= 0 ) // free to compute winding + { + winding = 1; + double ratio = lvect.length()/rvect.length(); + winding = int ( ratio ); + if ( winding < 1 ) winding = 1; + if ( winding > 50 ) winding = 50; + } + int nsides = int( winding*realsides + 0.5 ); // nsides/winding should be reduced! + if ( nsides > 100 ) nsides = 100; // well, 100 seems large enough! + if ( nsides < 3 ) nsides = 3; + while ( !relativePrimes ( nsides, winding ) ) ++nsides; + return nsides; +} + +/* + * ConicConic intersection... + */ + +static const ArgsParser::spec argsspectc[] = { + { ConicImp::stype(), "SHOULD NOT BE SEEN", "SHOULD NOT BE SEEN", true }, + { ConicImp::stype(), "SHOULD NOT BE SEEN", "SHOULD NOT BE SEEN", true } +}; + +ConicConicIntersectionConstructor::ConicConicIntersectionConstructor() + : StandardConstructorBase( "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "curvelineintersection", mparser ), + mparser( argsspectc, 2 ) +{ +} + +ConicConicIntersectionConstructor::~ConicConicIntersectionConstructor() +{ +} + +void ConicConicIntersectionConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, + const KigDocument& ) const +{ + if ( parents.size() != 2 ) return; + assert ( parents[0]->imp()->inherits( ConicImp::stype() ) && + parents[1]->imp()->inherits( ConicImp::stype() ) ); + const ConicCartesianData conica = + static_cast<const ConicImp*>( parents[0]->imp() )->cartesianData(); + const ConicCartesianData conicb = + static_cast<const ConicImp*>( parents[1]->imp() )->cartesianData(); + bool ok = true; + for ( int wr = -1; wr < 2; wr += 2 ) + { + LineData radical = calcConicRadical( conica, conicb, wr, 1, ok ); + if ( ok ) + { + for ( int wi = -1; wi < 2; wi += 2 ) + { + Coordinate c = calcConicLineIntersect( conica, radical, 0.0, wi ); + if ( c.valid() ) { + PointImp pi( c ); + drawer.draw( pi, p, true ); + } + }; + }; + }; +} + +std::vector<ObjectHolder*> ConicConicIntersectionConstructor::build( + const std::vector<ObjectCalcer*>& os, KigDocument& doc, KigWidget& ) const +{ + assert( os.size() == 2 ); + std::vector<ObjectHolder*> ret; + ObjectCalcer* conica = os[0]; + ObjectConstCalcer* zeroindexdo = new ObjectConstCalcer( new IntImp( 1 ) ); + + for ( int wr = -1; wr < 2; wr += 2 ) + { + std::vector<ObjectCalcer*> args = os; + args.push_back( new ObjectConstCalcer( new IntImp( wr ) ) ); + args.push_back( zeroindexdo ); + ObjectTypeCalcer* radical = + new ObjectTypeCalcer( ConicRadicalType::instance(), args ); + radical->calc( doc ); + for ( int wi = -1; wi < 2; wi += 2 ) + { + args.clear(); + args.push_back( conica ); + args.push_back( radical ); + args.push_back( new ObjectConstCalcer( new IntImp( wi ) ) ); + ret.push_back( + new ObjectHolder( + new ObjectTypeCalcer( + ConicLineIntersectionType::instance(), args ) ) ); + }; + }; + return ret; +} + +void ConicConicIntersectionConstructor::plug( KigPart*, KigGUIAction* ) +{ +} + +bool ConicConicIntersectionConstructor::isTransform() const +{ + return false; +} + +ConicLineIntersectionConstructor::ConicLineIntersectionConstructor() + : MultiObjectTypeConstructor( + ConicLineIntersectionType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "curvelineintersection", -1, 1 ) +{ +} + +ConicLineIntersectionConstructor::~ConicLineIntersectionConstructor() +{ +} + +ArcLineIntersectionConstructor::ArcLineIntersectionConstructor() + : MultiObjectTypeConstructor( + ArcLineIntersectionType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "curvelineintersection", -1, 1 ) +{ +} + +ArcLineIntersectionConstructor::~ArcLineIntersectionConstructor() +{ +} + +QString ConicRadicalConstructor::useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>&, + const KigDocument&, const KigWidget& ) const +{ + if ( o.imp()->inherits( CircleImp::stype() ) ) + return i18n( "Construct the Radical Lines of This Circle" ); + else + return i18n( "Construct the Radical Lines of This Conic" ); +} + +/* + * generic affinity and generic projectivity. A unique affinity can be + * obtained by specifying the image of three points (four for projectivity) + * in the end we need, besides the object to be transformed, a total of + * six point or (alternatively) two triangles; our affinity will map the + * first triangle onto the second with corresponding ordering of their + * vertices. Since we allow for two different ways of specifying the six + * points we shall use a Generic constructor, like that for intersections. + */ + +GenericAffinityConstructor::GenericAffinityConstructor() + : MergeObjectConstructor( + I18N_NOOP( "Generic Affinity" ), + I18N_NOOP( "The unique affinity that maps three points (or a triangle) onto three other points (or a triangle)" ), + "genericaffinity" ) +{ + SimpleObjectTypeConstructor* b2tr = + new SimpleObjectTypeConstructor( + AffinityB2TrType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "genericaffinity" ); + + SimpleObjectTypeConstructor* gi3p = + new SimpleObjectTypeConstructor( + AffinityGI3PType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "genericaffinity" ); + + merge( b2tr ); + merge( gi3p ); +} + +GenericAffinityConstructor::~GenericAffinityConstructor() {} + +GenericProjectivityConstructor::GenericProjectivityConstructor() + : MergeObjectConstructor( + I18N_NOOP( "Generic Projective Transformation" ), + I18N_NOOP( "The unique projective transformation that maps four points (or a quadrilateral) onto four other points (or a quadrilateral)" ), + "genericprojectivity" ) +{ + SimpleObjectTypeConstructor* b2qu = + new SimpleObjectTypeConstructor( + ProjectivityB2QuType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "genericprojectivity" ); + + SimpleObjectTypeConstructor* gi4p = + new SimpleObjectTypeConstructor( + ProjectivityGI4PType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "genericprojectivity" ); + + merge( b2qu ); + merge( gi4p ); +} + +GenericProjectivityConstructor::~GenericProjectivityConstructor() {} + +/* + * inversion of points, lines with respect to a circle + */ + +InversionConstructor::InversionConstructor() + : MergeObjectConstructor( + I18N_NOOP( "Inversion of Point, Line or Circle" ), + I18N_NOOP( "The inversion of a point, line or circle with respect to a circle" ), + "inversion" ) +{ + SimpleObjectTypeConstructor* pointobj = + new SimpleObjectTypeConstructor( + InvertPointType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "inversion" ); + + SimpleObjectTypeConstructor* lineobj = + new SimpleObjectTypeConstructor( + InvertLineType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "inversion" ); + + SimpleObjectTypeConstructor* segmentobj = + new SimpleObjectTypeConstructor( + InvertSegmentType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "inversion" ); + + SimpleObjectTypeConstructor* circleobj = + new SimpleObjectTypeConstructor( + InvertCircleType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "inversion" ); + + SimpleObjectTypeConstructor* arcobj = + new SimpleObjectTypeConstructor( + InvertArcType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "inversion" ); + + merge( arcobj ); + merge( circleobj ); + merge( pointobj ); + merge( segmentobj ); + merge( lineobj ); +} + +InversionConstructor::~InversionConstructor() {} + +/* + * Transport of Measure + */ + +MeasureTransportConstructor::MeasureTransportConstructor() + : mtype( MeasureTransportType::instance() ) +{ +} + +MeasureTransportConstructor::~MeasureTransportConstructor() +{ +} + +const QString MeasureTransportConstructor::descriptiveName() const +{ + return i18n("Measure Transport"); +} + +const QString MeasureTransportConstructor::description() const +{ + return i18n("Transport the measure of a segment or arc over a line or circle."); +} + +const QCString MeasureTransportConstructor::iconFileName( const bool ) const +{ + return "measuretransport"; +} + +const bool MeasureTransportConstructor::isAlreadySelectedOK( + const std::vector<ObjectCalcer*>&, const int& ) const +{ + return false; +} + +/* + * we want the arguments in the exact order, this makes + * the code simpler, but I guess it is also less confusing + * to the user + */ + +const int MeasureTransportConstructor::wantArgs( + const std::vector<ObjectCalcer*>& os, + const KigDocument&, + const KigWidget& ) const +{ + if ( os.size() == 0 ) return ArgsParser::Valid; + + if ( ! os[0]->imp()->inherits( SegmentImp::stype() ) && + ! os[0]->imp()->inherits( ArcImp::stype() ) ) + return ArgsParser::Invalid; + + if ( os.size() == 1 ) return ArgsParser::Valid; + + if ( ! os[1]->imp()->inherits( LineImp::stype() ) && + ! os[1]->imp()->inherits( CircleImp::stype() ) ) + return ArgsParser::Invalid; + + if ( os.size() == 2 ) return ArgsParser::Valid; + + if ( ! os[2]->imp()->inherits( PointImp::stype() ) ) + return ArgsParser::Invalid; + + // we here use the "isPointOnCurve", which relies on + // "by construction" incidence, instead of a numerical + // check + if ( ! isPointOnCurve( os[2], os[1] ) ) + return ArgsParser::Invalid; + + if ( os.size() == 3 ) return ArgsParser::Complete; + + return ArgsParser::Invalid; +} + +void MeasureTransportConstructor::handleArgs( + const std::vector<ObjectCalcer*>& os, KigPart& d, + KigWidget& v ) const +{ + std::vector<ObjectHolder*> bos = build( os, d.document(), v ); + for ( std::vector<ObjectHolder*>::iterator i = bos.begin(); + i != bos.end(); ++i ) + { + (*i)->calc( d.document() ); + } + + d.addObjects( bos ); +} + +void MeasureTransportConstructor::handlePrelim( + KigPainter& p, const std::vector<ObjectCalcer*>& os, + const KigDocument& d, const KigWidget& + ) const +{ + p.setBrushStyle( Qt::NoBrush ); + p.setBrushColor( Qt::red ); + p.setPen( QPen ( Qt::red, 1) ); + p.setWidth( -1 ); // -1 means the default width for the object being + // drawn.. + + ObjectDrawer drawer( Qt::red ); + drawprelim( drawer, p, os, d ); +} + +void MeasureTransportConstructor::drawprelim( const ObjectDrawer& drawer, + KigPainter& p, + const std::vector<ObjectCalcer*>& parents, + const KigDocument& doc ) const +{ + Args args; + using namespace std; + transform( parents.begin(), parents.end(), + back_inserter( args ), mem_fun( &ObjectCalcer::imp ) ); + ObjectImp* data = mtype->calc( args, doc ); + drawer.draw( *data, p, true ); + delete data; +} + +QString MeasureTransportConstructor::useText( const ObjectCalcer& o, + const std::vector<ObjectCalcer*>& os, + const KigDocument&, const KigWidget& ) const +{ + if ( o.imp()->inherits( SegmentImp::stype() ) ) + return i18n("Segment to transport"); + if ( o.imp()->inherits( ArcImp::stype() ) ) + return i18n("Arc to transport"); + if ( o.imp()->inherits( LineImp::stype() ) ) + return i18n("Transport a measure on this line"); + if ( o.imp()->inherits( CircleImp::stype() ) ) + return i18n("Transport a measure on this circle"); + if ( o.imp()->inherits( PointImp::stype() ) ) + { + if ( os[1]->imp()->inherits( CircleImp::stype() ) ) + return i18n("Start transport from this point of the circle"); + if ( os[1]->imp()->inherits( LineImp::stype() ) ) + return i18n("Start transport from this point of the line"); + else + return i18n("Start transport from this point of the curve"); + // well, this isn't impemented yet, should never get here + } + return ""; +} + +QString MeasureTransportConstructor::selectStatement( + const std::vector<ObjectCalcer*>&, const KigDocument&, + const KigWidget& ) const +{ +//TODO + return i18n("Select a point to be a vertex of the new polygon..."); +} + +std::vector<ObjectHolder*> MeasureTransportConstructor::build( + const std::vector<ObjectCalcer*>& parents, + KigDocument&, KigWidget& ) const +{ + assert ( parents.size() == 3 ); +// std::vector<ObjectCalcer*> args; +// for ( uint i = 0; i < count; ++i ) args.push_back( parents[i] ); + ObjectTypeCalcer* calcer = new ObjectTypeCalcer( mtype, parents ); + ObjectHolder* h = new ObjectHolder( calcer ); + std::vector<ObjectHolder*> ret; + ret.push_back( h ); + return ret; +} + +void MeasureTransportConstructor::plug( KigPart*, KigGUIAction* ) +{ +} + +bool MeasureTransportConstructor::isTransform() const +{ + return false; +} + +/* + * Generic intersection + */ + +GenericIntersectionConstructor::GenericIntersectionConstructor() + : MergeObjectConstructor( + I18N_NOOP( "Intersect" ), + I18N_NOOP( "The intersection of two objects" ), + "curvelineintersection" ) +{ + // intersection type.. + // There is one "toplevel" object_constructor, that is composed + // of multiple subconstructors.. First we build the + // subconstructors: + SimpleObjectTypeConstructor* lineline = + new SimpleObjectTypeConstructor( + LineLineIntersectionType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "curvelineintersection" ); + + ObjectConstructor* lineconic = + new ConicLineIntersectionConstructor(); + + ObjectConstructor* arcline = + new ArcLineIntersectionConstructor(); + + MultiObjectTypeConstructor* linecubic = + new MultiObjectTypeConstructor( + LineCubicIntersectionType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "curvelineintersection", 1, 2, 3 ); + + ObjectConstructor* conicconic = + new ConicConicIntersectionConstructor(); + + MultiObjectTypeConstructor* circlecircle = + new MultiObjectTypeConstructor( + CircleCircleIntersectionType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "circlecircleintersection", -1, 1 ); + + SimpleObjectTypeConstructor* polygonline = + new SimpleObjectTypeConstructor( + PolygonLineIntersectionType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "curvelineintersection" ); + + merge( lineline ); + merge( circlecircle ); + merge( lineconic ); + merge( linecubic ); + merge( conicconic ); + merge( arcline ); + merge( polygonline ); +} + +GenericIntersectionConstructor::~GenericIntersectionConstructor() +{ +} + +bool GenericIntersectionConstructor::isIntersection() const +{ + return true; +} + +QString GenericIntersectionConstructor::useText( + const ObjectCalcer& o, const std::vector<ObjectCalcer*>& os, + const KigDocument&, const KigWidget& ) const +{ + QString preamble; + switch (os.size()) + { + case 1: + if ( o.imp()->inherits( CircleImp::stype() ) ) + return i18n( "Intersect this Circle" ); + else if ( o.imp()->inherits( ConicImp::stype() ) ) + return i18n( "Intersect this Conic" ); + else if ( o.imp()->inherits( AbstractLineImp::stype() ) ) + return i18n( "Intersect this Line" ); + else if ( o.imp()->inherits( CubicImp::stype() ) ) + return i18n( "Intersect this Cubic Curve" ); + else if ( o.imp()->inherits( ArcImp::stype() ) ) + return i18n( "Intersect this Arc" ); + else if ( o.imp()->inherits( PolygonImp::stype() ) ) + return i18n( "Intersect this Polygon" ); + else assert( false ); + break; + case 2: + if ( o.imp()->inherits( CircleImp::stype() ) ) + return i18n( "with this Circle" ); + else if ( o.imp()->inherits( ConicImp::stype() ) ) + return i18n( "with this Conic" ); + else if ( o.imp()->inherits( AbstractLineImp::stype() ) ) + return i18n( "with this Line" ); + else if ( o.imp()->inherits( CubicImp::stype() ) ) + return i18n( "with this Cubic Curve" ); + else if ( o.imp()->inherits( ArcImp::stype() ) ) + return i18n( "with this Arc" ); + else if ( o.imp()->inherits( PolygonImp::stype() ) ) + return i18n( "with this Polygon" ); + else assert( false ); + break; + } + + return QString::null; +} + +static const ArgsParser::spec argsspecMidPointOfTwoPoints[] = +{ + { PointImp::stype(), I18N_NOOP( "Construct Midpoint of This Point and Another One" ), + I18N_NOOP( "Select the first of the points of which you want to construct the midpoint..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct the midpoint of this point and another one" ), + I18N_NOOP( "Select the other of the points of which to construct the midpoint..." ), false } +}; + +MidPointOfTwoPointsConstructor::MidPointOfTwoPointsConstructor() + : StandardConstructorBase( "Mid Point", + "Construct the midpoint of two points", + "bisection", mparser ), + mparser( argsspecMidPointOfTwoPoints, 2 ) +{ +} + +MidPointOfTwoPointsConstructor::~MidPointOfTwoPointsConstructor() +{ +} + +void MidPointOfTwoPointsConstructor::drawprelim( + const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, + const KigDocument& ) const +{ + if ( parents.size() != 2 ) return; + assert( parents[0]->imp()->inherits( PointImp::stype() ) ); + assert( parents[1]->imp()->inherits( PointImp::stype() ) ); + const Coordinate m = + ( static_cast<const PointImp*>( parents[0]->imp() )->coordinate() + + static_cast<const PointImp*>( parents[1]->imp() )->coordinate() ) / 2; + drawer.draw( PointImp( m ), p, true ); +} + +std::vector<ObjectHolder*> MidPointOfTwoPointsConstructor::build( + const std::vector<ObjectCalcer*>& os, KigDocument& d, KigWidget& ) const +{ + ObjectTypeCalcer* seg = new ObjectTypeCalcer( SegmentABType::instance(), os ); + seg->calc( d ); + int index = seg->imp()->propertiesInternalNames().findIndex( "mid-point" ); + assert( index != -1 ); + ObjectPropertyCalcer* prop = new ObjectPropertyCalcer( seg, index ); + prop->calc( d ); + std::vector<ObjectHolder*> ret; + ret.push_back( new ObjectHolder( prop ) ); + return ret; +} + +void MidPointOfTwoPointsConstructor::plug( KigPart*, KigGUIAction* ) +{ +} + +bool MidPointOfTwoPointsConstructor::isTransform() const +{ + return false; +} + +TestConstructor::TestConstructor( const ArgsParserObjectType* type, const char* descname, + const char* desc, const char* iconfile ) + : StandardConstructorBase( descname, desc, iconfile, type->argsParser() ), + mtype( type ) +{ +} + +TestConstructor::~TestConstructor() +{ +} + +void TestConstructor::drawprelim( const ObjectDrawer&, KigPainter&, const std::vector<ObjectCalcer*>&, + const KigDocument& ) const +{ + // not used, only here because of the wrong + // ObjectConstructor-GUIAction design. See the TODO +} + +std::vector<ObjectHolder*> TestConstructor::build( const std::vector<ObjectCalcer*>&, KigDocument&, + KigWidget& ) const +{ + // not used, only here because of the wrong + // ObjectConstructor-GUIAction design. See the TODO + std::vector<ObjectHolder*> ret; + return ret; +} + +void TestConstructor::plug( KigPart*, KigGUIAction* ) +{ +} + +bool TestConstructor::isTransform() const +{ + return false; +} + +bool TestConstructor::isTest() const +{ + return true; +} + +BaseConstructMode* TestConstructor::constructMode( KigPart& doc ) +{ + return new TestConstructMode( doc, mtype ); +} + +const int TestConstructor::wantArgs( const std::vector<ObjectCalcer*>& os, + const KigDocument& d, const KigWidget& v ) const +{ + int ret = StandardConstructorBase::wantArgs( os, d, v ); + if ( ret == ArgsParser::Complete ) ret = ArgsParser::Valid; + return ret; +} + +QString GenericIntersectionConstructor::selectStatement( + const std::vector<ObjectCalcer*>& sel, const KigDocument&, + const KigWidget& ) const +{ + if ( sel.size() == 0 ) + return i18n( "Select the first object to intersect..." ); + else + return i18n( "Select the second object to intersect..." ); +} + +TangentConstructor::TangentConstructor() + : MergeObjectConstructor( + I18N_NOOP( "Tangent" ), + I18N_NOOP( "The line tangent to a curve" ), + "tangent" ) +{ + SimpleObjectTypeConstructor* conic = + new SimpleObjectTypeConstructor( + TangentConicType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "tangentconic" ); + + SimpleObjectTypeConstructor* arc = + new SimpleObjectTypeConstructor( + TangentArcType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "tangentarc" ); + + SimpleObjectTypeConstructor* cubic = + new SimpleObjectTypeConstructor( + TangentCubicType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "tangentcubic" ); + + SimpleObjectTypeConstructor* curve = + new SimpleObjectTypeConstructor( + TangentCurveType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "tangentcurve" ); + + merge( conic ); + merge( arc ); + merge( cubic ); + merge( curve ); +} + +TangentConstructor::~TangentConstructor() +{ +} + +QString TangentConstructor::useText( + const ObjectCalcer& o, const std::vector<ObjectCalcer*>&, + const KigDocument&, const KigWidget& ) const +{ + if ( o.imp()->inherits( CircleImp::stype() ) ) + return i18n( "Tangent to This Circle" ); + else if ( o.imp()->inherits( ConicImp::stype() ) ) + return i18n( "Tangent to This Conic" ); + else if ( o.imp()->inherits( ArcImp::stype() ) ) + return i18n( "Tangent to This Arc" ); + else if ( o.imp()->inherits( CubicImp::stype() ) ) + return i18n( "Tangent to This Cubic Curve" ); + else if ( o.imp()->inherits( CurveImp::stype() ) ) + return i18n( "Tangent to This Curve" ); + else if ( o.imp()->inherits( PointImp::stype() ) ) + return i18n( "Tangent at This Point" ); +// else assert( false ); + return QString::null; +} + +//QString TangentConstructor::selectStatement( +// const std::vector<ObjectCalcer*>& sel, const KigDocument&, +// const KigWidget& ) const +//{ +// if ( sel.size() == 0 ) +// return i18n( "Select the object..." ); +// else +// return i18n( "Select the point for the tangent to go through..." ); +//} + +/* + * center of curvature of a curve + */ + +CocConstructor::CocConstructor() + : MergeObjectConstructor( + I18N_NOOP( "Center Of Curvature" ), + I18N_NOOP( "The center of the osculating circle to a curve" ), + "centerofcurvature" ) +{ + SimpleObjectTypeConstructor* conic = + new SimpleObjectTypeConstructor( + CocConicType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "cocconic" ); + + SimpleObjectTypeConstructor* cubic = + new SimpleObjectTypeConstructor( + CocCubicType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "coccubic" ); + + SimpleObjectTypeConstructor* curve = + new SimpleObjectTypeConstructor( + CocCurveType::instance(), + "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", + "coccurve" ); + + merge( conic ); + merge( cubic ); + merge( curve ); +} + +CocConstructor::~CocConstructor() +{ +} + +QString CocConstructor::useText( + const ObjectCalcer& o, const std::vector<ObjectCalcer*>&, + const KigDocument&, const KigWidget& ) const +{ + if ( o.imp()->inherits( ConicImp::stype() ) ) + return i18n( "Center of Curvature of This Conic" ); + else if ( o.imp()->inherits( CubicImp::stype() ) ) + return i18n( "Center of Curvature of This Cubic Curve" ); + else if ( o.imp()->inherits( CurveImp::stype() ) ) + return i18n( "Center of Curvature of This Curve" ); + else if ( o.imp()->inherits( PointImp::stype() ) ) + return i18n( "Center of Curvature at This Point" ); + return QString::null; +} + +bool relativePrimes( int n, int p ) +{ + if ( p > n ) return relativePrimes( p, n ); + assert ( p >= 0 ); + if ( p == 0 ) return false; + if ( p == 1 ) return true; + int d = int( n/p ); + return relativePrimes( p, n-d*p ); +} + +//QString CocConstructor::selectStatement( +// const std::vector<ObjectCalcer*>& sel, const KigDocument&, +// const KigWidget& ) const +//{ +// if ( sel.size() == 0 ) +// return i18n( "Select the object..." ); +// else +// return i18n( "Select the point where to compute the center of curvature..." ); +//} diff --git a/kig/misc/special_constructors.h b/kig/misc/special_constructors.h new file mode 100644 index 00000000..99760be3 --- /dev/null +++ b/kig/misc/special_constructors.h @@ -0,0 +1,320 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_MISC_SPECIAL_CONSTRUCTORS_H +#define KIG_MISC_SPECIAL_CONSTRUCTORS_H + +#include "object_constructor.h" + +class PolygonVertexTypeConstructor + : public StandardConstructorBase +{ + const ArgsParserObjectType* mtype; + ArgsParser margsparser; +public: + PolygonVertexTypeConstructor(); + ~PolygonVertexTypeConstructor(); + + void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, const KigDocument& ) const; + std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, KigDocument& d, KigWidget& w ) const; + void plug( KigPart* doc, KigGUIAction* kact ); + bool isTransform() const; +}; + +class PolygonSideTypeConstructor + : public StandardConstructorBase +{ + const ArgsParserObjectType* mtype; + ArgsParser margsparser; +public: + PolygonSideTypeConstructor(); + ~PolygonSideTypeConstructor(); + + void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, const KigDocument& ) const; + std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, KigDocument& d, KigWidget& w ) const; + void plug( KigPart* doc, KigGUIAction* kact ); + bool isTransform() const; +}; + +class PolygonBNPTypeConstructor + : public ObjectConstructor +{ + const ObjectType* mtype; +public: + PolygonBNPTypeConstructor(); + ~PolygonBNPTypeConstructor(); + + const QString descriptiveName() const; + const QString description() const; + const QCString iconFileName( const bool canBeNull = false ) const; + const bool isAlreadySelectedOK( const std::vector<ObjectCalcer*>& os, + const int& ) const; + const int wantArgs( const std::vector<ObjectCalcer*>& os, + const KigDocument& d, + const KigWidget& v + ) const; + void handleArgs( const std::vector<ObjectCalcer*>& os, + KigPart& d, + KigWidget& v + ) const; + QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel, + const KigDocument& d, const KigWidget& v + ) const; + QString selectStatement( + const std::vector<ObjectCalcer*>& sel, const KigDocument& d, + const KigWidget& w ) const; + void handlePrelim( KigPainter& p, + const std::vector<ObjectCalcer*>& sel, + const KigDocument& d, + const KigWidget& v + ) const; + + void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, const KigDocument& ) const; + std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, KigDocument& d, KigWidget& w ) const; + void plug( KigPart* doc, KigGUIAction* kact ); + bool isTransform() const; +}; + +class PolygonBCVConstructor + : public ObjectConstructor +{ + const ObjectType* mtype; +public: + PolygonBCVConstructor(); + ~PolygonBCVConstructor(); + + const QString descriptiveName() const; + const QString description() const; + const QCString iconFileName( const bool canBeNull = false ) const; + const bool isAlreadySelectedOK( const std::vector<ObjectCalcer*>& os, + const int& ) const; + const int wantArgs( const std::vector<ObjectCalcer*>& os, + const KigDocument& d, + const KigWidget& v + ) const; + void handleArgs( const std::vector<ObjectCalcer*>& os, + KigPart& d, + KigWidget& v + ) const; + QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel, + const KigDocument& d, const KigWidget& v + ) const; + QString selectStatement( + const std::vector<ObjectCalcer*>& sel, const KigDocument& d, + const KigWidget& w ) const; + void handlePrelim( KigPainter& p, + const std::vector<ObjectCalcer*>& sel, + const KigDocument& d, + const KigWidget& v + ) const; + void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, const KigDocument& ) const; + std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, KigDocument& d, KigWidget& w ) const; + void plug( KigPart* doc, KigGUIAction* kact ); + bool isTransform() const; + int computeNsides( const Coordinate& c, const Coordinate& v, const Coordinate& cntrl, int& winding ) const; + Coordinate getRotatedCoord( const Coordinate& c1, + const Coordinate& c2, double alpha ) const; +}; + +class MeasureTransportConstructor + : public ObjectConstructor +{ + const ObjectType* mtype; +public: + MeasureTransportConstructor(); + ~MeasureTransportConstructor(); + + const QString descriptiveName() const; + const QString description() const; + const QCString iconFileName( const bool canBeNull = false ) const; + const bool isAlreadySelectedOK( const std::vector<ObjectCalcer*>& os, + const int& ) const; + const int wantArgs( const std::vector<ObjectCalcer*>& os, + const KigDocument& d, + const KigWidget& v + ) const; + void handleArgs( const std::vector<ObjectCalcer*>& os, + KigPart& d, + KigWidget& v + ) const; + QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel, + const KigDocument& d, const KigWidget& v + ) const; + QString selectStatement( + const std::vector<ObjectCalcer*>& sel, const KigDocument& d, + const KigWidget& w ) const; + void handlePrelim( KigPainter& p, + const std::vector<ObjectCalcer*>& sel, + const KigDocument& d, + const KigWidget& v + ) const; + + void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, const KigDocument& ) const; + std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, KigDocument& d, KigWidget& w ) const; + void plug( KigPart* doc, KigGUIAction* kact ); + bool isTransform() const; +}; + +class ConicRadicalConstructor + : public StandardConstructorBase +{ + const ArgsParserObjectType* mtype; + const ArgsParser mparser; +public: + ConicRadicalConstructor(); + ~ConicRadicalConstructor(); + QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel, const KigDocument& d, + const KigWidget& v ) const; + void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, const KigDocument& ) const; + std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, KigDocument& d, KigWidget& w ) const; + void plug( KigPart* doc, KigGUIAction* kact ); + + bool isTransform() const; +}; + +class LocusConstructor + : public StandardConstructorBase +{ + ArgsParser margsparser; +public: + LocusConstructor(); + ~LocusConstructor(); + /** + * we override the wantArgs() function, since we need to see + * something about the objects that an ArgsParser can't know about, + * namely, whether the first point is a constrained point... + */ + const int wantArgs( + const std::vector<ObjectCalcer*>& os, const KigDocument& d, + const KigWidget& v + ) const; + QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel, const KigDocument& d, + const KigWidget& v ) const; + + void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, const KigDocument& ) const; + std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, KigDocument& d, KigWidget& w ) const; + void plug( KigPart* doc, KigGUIAction* kact ); + + bool isTransform() const; +}; + +class GenericAffinityConstructor + : public MergeObjectConstructor +{ +public: + GenericAffinityConstructor(); + ~GenericAffinityConstructor(); +}; + +class GenericProjectivityConstructor + : public MergeObjectConstructor +{ +public: + GenericProjectivityConstructor(); + ~GenericProjectivityConstructor(); +}; + +class InversionConstructor + : public MergeObjectConstructor +{ +public: + InversionConstructor(); + ~InversionConstructor(); +}; + +class GenericIntersectionConstructor + : public MergeObjectConstructor +{ +public: + GenericIntersectionConstructor(); + ~GenericIntersectionConstructor(); + + bool isIntersection() const; + + QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel, const KigDocument& d, + const KigWidget& v ) const; + QString selectStatement( + const std::vector<ObjectCalcer*>& sel, const KigDocument& d, + const KigWidget& w ) const; +}; + +class MidPointOfTwoPointsConstructor + : public StandardConstructorBase +{ + ArgsParser mparser; +public: + MidPointOfTwoPointsConstructor(); + ~MidPointOfTwoPointsConstructor(); + void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, + const KigDocument& ) const; + std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, KigDocument& d, + KigWidget& w ) const; + void plug( KigPart* doc, KigGUIAction* kact ); + bool isTransform() const; +}; + +class TestConstructor + : public StandardConstructorBase +{ + const ArgsParserObjectType* mtype; +public: + TestConstructor( const ArgsParserObjectType* type, const char* descname, + const char* desc, const char* iconfile ); + ~TestConstructor(); + void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, + const KigDocument& ) const; + std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, KigDocument& d, + KigWidget& w ) const; + const int wantArgs( const std::vector<ObjectCalcer*>& os, + const KigDocument& d, const KigWidget& v ) const; + void plug( KigPart* doc, KigGUIAction* kact ); + bool isTransform() const; + bool isTest() const; + + BaseConstructMode* constructMode( KigPart& doc ); +}; + +class TangentConstructor + : public MergeObjectConstructor +{ +public: + TangentConstructor(); + ~TangentConstructor(); + + QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel, const KigDocument& d, + const KigWidget& v ) const; +// QString selectStatement( +// const std::vector<ObjectCalcer*>& sel, const KigDocument& d, +// const KigWidget& w ) const; +}; + +class CocConstructor + : public MergeObjectConstructor +{ +public: + CocConstructor(); + ~CocConstructor(); + + QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel, const KigDocument& d, + const KigWidget& v ) const; +// QString selectStatement( +// const std::vector<ObjectCalcer*>& sel, const KigDocument& d, +// const KigWidget& w ) const; +}; + +bool relativePrimes( int n, int p ); +#endif diff --git a/kig/modes/Makefile.am b/kig/modes/Makefile.am new file mode 100644 index 00000000..5f7518e5 --- /dev/null +++ b/kig/modes/Makefile.am @@ -0,0 +1,39 @@ +INCLUDES=$(all_includes) + +noinst_LTLIBRARIES=libmodes.la +libmodes_la_SOURCES= \ + base_mode.cc \ + construct_mode.cc \ + dragrectmode.cc \ + edittype.cc \ + edittypebase.ui \ + label.cc \ + linkslabel.cpp \ + macro.cc \ + macrowizard.cc \ + macrowizardbase.ui \ + mode.cc \ + moving.cc \ + normal.cc \ + popup.cc \ + textlabelwizard.cc \ + textlabelwizardbase.ui \ + typesdialog.cpp \ + typesdialogbase.ui +noinst_HEADERS=\ + base_mode.h \ + construct_mode.h \ + dragrectmode.h \ + edittype.h \ + label.h \ + linkslabel.h \ + macro.h \ + macrowizard.h \ + mode.h \ + moving.h \ + normal.h \ + popup.h \ + textlabelwizard.h \ + typesdialog.h + +METASOURCES=AUTO diff --git a/kig/modes/base_mode.cc b/kig/modes/base_mode.cc new file mode 100644 index 00000000..9e92274a --- /dev/null +++ b/kig/modes/base_mode.cc @@ -0,0 +1,160 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "base_mode.h" + +#include "popup.h" + +#include "../kig/kig_view.h" +#include "../kig/kig_part.h" +#include "../kig/kig_document.h" + +#include <qevent.h> +#include <kcursor.h> + +BaseMode::BaseMode( KigPart& d ) + : KigMode( d ) +{ +} + +BaseMode::~BaseMode() +{ +} + +void BaseMode::leftClicked( QMouseEvent* e, KigWidget* v ) +{ + // touch screens don't send a mouseMoved event before a click event, + // apparently, so we simulate it. + mouseMoved( e, v ); + + // get rid of text still showing... + v->updateCurPix(); + v->updateWidget(); + + mplc = e->pos(); + moco = mdoc.document().whatAmIOn( v->fromScreen( mplc ), *v ); + + if( moco.empty() ) + { + // clicked on an empty spot --> we show the rectangle for + // selecting stuff... + dragRect( mplc, *v ); + } + else + { + // the user clicked on some object.. --> this could either mean + // that he/she wants to select the object or that he wants to + // start moving it. We assume nothing here, we wait till he + // either moves some 4 pixels, or till he releases his mouse + // button in leftReleased() or mouseMoved()... + }; +} + +void BaseMode::leftMouseMoved( QMouseEvent* e, KigWidget* w ) +{ + if( !moco.empty() && ( mplc - e->pos() ).manhattanLength() > 3 ) + dragObject( moco, mplc, *w, + ( e->state() & (ShiftButton | ControlButton ) ) != 0 + ); +} + +void BaseMode::leftReleased( QMouseEvent* e, KigWidget* v ) +{ + if( (mplc - e->pos()).manhattanLength() > 4 ) return; + + ObjectHolder* o = 0; + bool keyCtrl = ( e->state() & ControlButton ) != 0; + bool keyShift = ( e->state() & ShiftButton ) != 0; + if ( ! moco.empty() ) + { + if ( keyShift ) + { + int id = ObjectChooserPopup::getObjectFromList( e->pos(), v, moco ); + if ( id >= 0 ) + o = moco[id]; + } + else + o = moco.front(); + } + leftClickedObject( o, e->pos(), *v, keyCtrl ); +} + +void BaseMode::midClicked( QMouseEvent* e, KigWidget* v ) +{ + // get rid of text still showing... + v->updateCurPix(); + v->updateWidget(); + + mplc = e->pos(); + moco = mdoc.document().whatAmIOn( v->fromScreen( e->pos() ), *v ); +} + +void BaseMode::midReleased( QMouseEvent* e, KigWidget* v ) +{ + if( (e->pos() - mplc).manhattanLength() > 4 ) return; + + midClicked( mplc, *v ); +} + +void BaseMode::rightClicked( QMouseEvent* e, KigWidget* w ) +{ + // get rid of text still showing... + w->updateCurPix(); + w->updateWidget(); + // set a normal cursor... + w->setCursor( KCursor::arrowCursor() ); + + mplc = e->pos(); + moco = mdoc.document().whatAmIOn( w->fromScreen( mplc ), *w ); + + rightClicked( moco, mplc, *w ); +} + +void BaseMode::mouseMoved( QMouseEvent* e, KigWidget* w ) +{ + std::vector<ObjectHolder*> os = mdoc.document().whatAmIOn( w->fromScreen( e->pos() ), *w ); + mouseMoved( os, e->pos(), *w, e->state() & Qt::ShiftButton ); +} + +void BaseMode::dragRect( const QPoint&, KigWidget& ) +{ +} + +void BaseMode::leftClickedObject( ObjectHolder*, const QPoint&, + KigWidget&, bool ) +{ +} + +void BaseMode::dragObject( const std::vector<ObjectHolder*>&, const QPoint&, + KigWidget&, bool ) +{ +} + +void BaseMode::enableActions() +{ + KigMode::enableActions(); +} + +std::vector<ObjectHolder*> BaseMode::oco() +{ + return moco; +} + +QPoint BaseMode::pointLocation() +{ + return mplc; +} diff --git a/kig/modes/base_mode.h b/kig/modes/base_mode.h new file mode 100644 index 00000000..2f89996f --- /dev/null +++ b/kig/modes/base_mode.h @@ -0,0 +1,64 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_MODE_BASE_MODE_H +#define KIG_MODE_BASE_MODE_H + +#include "mode.h" + +#include <qpoint.h> +#include <vector> + +class KigWidget; +class KigDocument; +class ObjectHolder; + +class BaseMode + : public KigMode +{ + QPoint mplc; + std::vector<ObjectHolder*> moco; + + void leftClicked( QMouseEvent* e, KigWidget* v ); + void leftMouseMoved( QMouseEvent*, KigWidget* ); + void leftReleased( QMouseEvent* e, KigWidget* v ); + void midClicked( QMouseEvent* e, KigWidget* v ); + void midReleased( QMouseEvent* e, KigWidget* v ); + void rightClicked( QMouseEvent*, KigWidget* ); + void mouseMoved( QMouseEvent* e, KigWidget* v ); + +protected: + void enableActions(); + + std::vector<ObjectHolder*> oco(); + QPoint pointLocation(); +protected: + + virtual void dragRect( const QPoint& p, KigWidget& w ); + virtual void dragObject( const std::vector<ObjectHolder*>& os, const QPoint& pointClickedOn, KigWidget& w, bool ctrlOrShiftDown ); + virtual void leftClickedObject( ObjectHolder* o, const QPoint& p, + KigWidget& w, bool ctrlOrShiftDown ) = 0; + virtual void midClicked( const QPoint& p, KigWidget& w ) = 0; + virtual void rightClicked( const std::vector<ObjectHolder*>& oco, const QPoint& p, KigWidget& w ) = 0; + virtual void mouseMoved( const std::vector<ObjectHolder*>& os, const QPoint& p, KigWidget& w, bool shiftpressed ) = 0; + +protected: + BaseMode( KigPart& ); + ~BaseMode(); +}; + +#endif diff --git a/kig/modes/construct_mode.cc b/kig/modes/construct_mode.cc new file mode 100644 index 00000000..9618aded --- /dev/null +++ b/kig/modes/construct_mode.cc @@ -0,0 +1,572 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "construct_mode.h" + +#include "../objects/object_factory.h" +#include "../objects/object_drawer.h" +#include "../objects/text_type.h" +#include "../objects/text_imp.h" +#include "../objects/bogus_imp.h" +#include "../objects/point_imp.h" +#include "../misc/argsparser.h" + +#include "../kig/kig_document.h" +#include "../kig/kig_part.h" +#include "../kig/kig_view.h" +#include "../misc/object_constructor.h" +#include "../misc/coordinate_system.h" +#include "../misc/kigpainter.h" +#include "../misc/calcpaths.h" + +#include "popup.h" + +#include <kcursor.h> +#include <kaction.h> + +#include <algorithm> +#include <functional> + +static void redefinePoint( ObjectTypeCalcer* mpt, const Coordinate& c, KigDocument& doc, const KigWidget& w ) +{ + ObjectFactory::instance()->redefinePoint( mpt, c, doc, w ); + mpt->calc( doc ); +} + +BaseConstructMode::BaseConstructMode( KigPart& d ) + : BaseMode( d ) +{ + mpt = ObjectFactory::instance()->fixedPointCalcer( Coordinate( 0, 0 ) ); + mpt->calc( d.document() ); + mcursor = ObjectFactory::instance()->cursorPointCalcer( Coordinate( 0, 0 ) ); + mcursor->calc( d.document() ); +// mcursorholder = new ObjectHolder( mcursor ); +} + +BaseConstructMode::~BaseConstructMode() +{ + delete mcursor; +// delete mcursorholder; +} + +void BaseConstructMode::leftReleased( QMouseEvent* e, KigWidget* v ) +{ + if( (pointLocation() - e->pos()).manhattanLength() > 4 ) return; + + ObjectHolder* o = 0; + bool keyCtrlOrShift = ( e->state() & ( ControlButton | ShiftButton) ) != 0; + std::vector<ObjectHolder*> moco = oco(); + if ( ! moco.empty() ) + { + std::vector<ObjectHolder*> goodargs; + if ( !moco.empty() ) + { + std::vector<ObjectHolder*>::const_iterator it; + std::vector<ObjectCalcer*> testargs = getCalcers( mparents ); + for ( std::vector<ObjectHolder*>::const_iterator i = moco.begin(); i != moco.end(); ++i ) + { + it = std::find( mparents.begin(), mparents.end(), *i ); + bool newdup = + ( it == mparents.end() ) || + isAlreadySelectedOK( testargs, it - mparents.begin() ); + if ( newdup ) + { + testargs.push_back( ( *i )->calcer() ); + if ( wantArgs( testargs, mdoc.document(), *v ) ) + goodargs.push_back( *i ); + testargs.pop_back(); + } + } + int id = ObjectChooserPopup::getObjectFromList( e->pos(), v, goodargs ); + if ( id >= 0 ) + o = goodargs[id]; + } + } + leftClickedObject( o, e->pos(), *v, keyCtrlOrShift ); + KigMode::leftReleased( e, v ); +} + +void BaseConstructMode::leftClickedObject( + ObjectHolder* o, const QPoint& p, KigWidget& w, bool ) +{ + std::vector<ObjectHolder*>::iterator it = std::find( mparents.begin(), mparents.end(), o ); + std::vector<ObjectCalcer*> nargs = getCalcers( mparents ); +// +// mp: duplicationchecked controls whether the arguments list is +// free of duplications or if a duplication is safe (asking this to +// the Constructor class through the "isAlreadySelectedOK" method). +// + bool duplicationchecked = + ( it == mparents.end() ) || + isAlreadySelectedOK( nargs, it - mparents.begin() ); + if ( o && duplicationchecked ) + { + nargs.push_back( o->calcer() ); + if ( wantArgs( nargs, mdoc.document(), w ) ) + { + selectObject( o, w ); + return; + } + } + + nargs = getCalcers( mparents ); + nargs.push_back( mpt.get() ); + if ( wantArgs( nargs, mdoc.document(), w ) ) + { + // add mpt to the document.. + ObjectHolder* n = new ObjectHolder( mpt.get() ); + mdoc.addObject( n ); + selectObject( n, w ); + // get a new mpt for our further use.. + mpt = ObjectFactory::instance()->sensiblePointCalcer( w.fromScreen( p ), mdoc.document(), w ); + mpt->calc( mdoc.document() ); + return; + } + + nargs = getCalcers( mparents ); + nargs.push_back( mcursor ); + + if ( wantArgs( nargs, mdoc.document(), w ) ) + { + // DON'T add mpt to the document.. + // the objectholder has been constructed once and for all + // when entering construction mode, and delete in the + // destructor. + ObjectHolder* n = new ObjectHolder( mcursor ); + selectObject( n, w ); + mcursor = ObjectFactory::instance()->cursorPointCalcer( w.fromScreen( p ) ); +// mcursor = ObjectFactory::instance()->sensiblePointCalcer( w.fromScreen( p ), mdoc.document(), w ); + mcursor->calc( mdoc.document() ); + delete n; + } +} + +void BaseConstructMode::midClicked( const QPoint& p, KigWidget& w ) +{ + std::vector<ObjectCalcer*> args = getCalcers( mparents ); + args.push_back( mpt.get() ); + if ( wantArgs( args, mdoc.document(), w ) ) + { + ObjectHolder* n = new ObjectHolder( mpt.get() ); + mdoc.addObject( n ); + + selectObject( n, w ); + + mpt = ObjectFactory::instance()->sensiblePointCalcer( w.fromScreen( p ), mdoc.document(), w ); + mpt->calc( mdoc.document() ); + } +} + +void BaseConstructMode::rightClicked( const std::vector<ObjectHolder*>&, const QPoint&, KigWidget& ) +{ + // TODO ? +} + +void BaseConstructMode::mouseMoved( const std::vector<ObjectHolder*>& os, const QPoint& p, + KigWidget& w, bool shiftpressed ) +{ + mdoc.emitStatusBarText( selectStatement( getCalcers( mparents ), w ) ); + + w.updateCurPix(); + KigPainter pter( w.screenInfo(), &w.curPix, mdoc.document() ); + + Coordinate ncoord = w.fromScreen( p ); + if ( shiftpressed ) + ncoord = mdoc.document().coordinateSystem().snapToGrid( ncoord, w ); + + redefinePoint( mpt.get(), ncoord, mdoc.document(), w ); + mcursor->move( ncoord, mdoc.document() ); + mcursor->calc( mdoc.document() ); + + std::vector<ObjectCalcer*> args = getCalcers( mparents ); + bool duplicationchecked = false; + std::vector<ObjectHolder*> goodargs; + if ( ! os.empty() ) + { + std::vector<ObjectHolder*>::const_iterator it; + std::vector<ObjectCalcer*> testargs = getCalcers( mparents ); + for ( std::vector<ObjectHolder*>::const_iterator i = os.begin(); i != os.end(); ++i ) + { + it = std::find( mparents.begin(), mparents.end(), *i ); + bool newdup = + ( it == mparents.end() ) || + isAlreadySelectedOK( args, it - mparents.begin() ); + if ( newdup ) + { + testargs.push_back( ( *i )->calcer() ); + if ( wantArgs( testargs, mdoc.document(), w ) ) + goodargs.push_back( *i ); + testargs.pop_back(); + } + duplicationchecked |= newdup; + } + } + bool calcnow = ( goodargs.size() == 1 ) || ( ( goodargs.size() > 0 ) && ( goodargs.front()->imp()->inherits( PointImp::stype() ) ) ); + if ( calcnow ) + { + args.push_back( goodargs.front()->calcer() ); + } + + if ( !os.empty() && duplicationchecked && calcnow ) + { + handlePrelim( args, p, pter, w ); + + w.setCursor( KCursor::handCursor() ); + } + else + { + std::vector<ObjectCalcer*> args = getCalcers( mparents ); + args.push_back( mpt.get() ); + std::vector<ObjectCalcer*> argscursor = getCalcers( mparents ); + argscursor.push_back( mcursor ); + bool text = true; + if ( wantArgs( args, mdoc.document(), w ) ) + { + ObjectDrawer d; + d.draw( *mpt->imp(), pter, true ); + + handlePrelim( args, p, pter, w ); + + w.setCursor( KCursor::handCursor() ); + } + else if ( wantArgs( argscursor, mdoc.document(), w ) ) + { + ObjectDrawer d; +// d.draw( *mcursor->imp(), pter, true ); + + handlePrelim( argscursor, p, pter, w ); + + w.setCursor( KCursor::crossCursor() ); + } + else + { + w.setCursor( KCursor::arrowCursor() ); + text = false; + } + if ( !text && ( goodargs.size() > 1 ) ) + { + QString strwhich = i18n( "Which object?" ); + mdoc.emitStatusBarText( strwhich ); + + QPoint textloc = p; + textloc.setX( textloc.x() + 15 ); + pter.drawTextStd( textloc, strwhich ); + + w.setCursor( KCursor::handCursor() ); + } + } + w.updateWidget( pter.overlay() ); +} + +void BaseConstructMode::selectObject( ObjectHolder* o, KigWidget& w ) +{ + mparents.push_back( o ); + std::vector<ObjectCalcer*> args = getCalcers( mparents ); + + if ( wantArgs( args, mdoc.document(), w ) == ArgsParser::Complete ) + { + handleArgs( args, w ); + }; + + w.redrawScreen( mparents ); +} + +PointConstructMode::PointConstructMode( KigPart& d ) + : BaseMode( d ) +{ + // we add the data objects to the document cause + // ObjectFactory::redefinePoint does that too, and this way, we can + // depend on them already being known by the doc when we add the + // mpt.. + mpt = ObjectFactory::instance()->fixedPointCalcer( Coordinate() ); + mpt->calc( d.document() ); + + mdoc.emitStatusBarText( i18n( "Click the location where you want to place the new point, or the curve that you want to attach it to..." ) ); +} + +PointConstructMode::~PointConstructMode() +{ +} + +void PointConstructMode::leftClickedObject( + ObjectHolder*, const QPoint&, KigWidget& w, bool ) +{ + mdoc.addObject( new ObjectHolder( mpt.get() ) ); + w.redrawScreen( std::vector<ObjectHolder*>() ); + + mdoc.emitStatusBarText( QString::null ); + mdoc.doneMode( this ); +} + +void PointConstructMode::midClicked( const QPoint& p, KigWidget& w ) +{ + leftClickedObject( 0, p, w, true ); +} + +void PointConstructMode::rightClicked( const std::vector<ObjectHolder*>&, const QPoint&, + KigWidget& ) +{ + // TODO ? +} + +void PointConstructMode::mouseMoved( + const std::vector<ObjectHolder*>&, + const QPoint& p, + KigWidget& w, + bool shiftpressed ) +{ + w.updateCurPix(); + KigPainter pter( w.screenInfo(), &w.curPix, mdoc.document() ); + + Coordinate ncoord = w.fromScreen( p ); + if ( shiftpressed ) + ncoord = mdoc.document().coordinateSystem().snapToGrid( ncoord, w ); + + redefinePoint( mpt.get(), ncoord, mdoc.document(), w ); + + ObjectDrawer d; + d.draw( *mpt->imp(), pter, true ); + w.setCursor( KCursor::blankCursor() ); + + w.updateWidget( pter.overlay() ); +} + +void BaseConstructMode::enableActions() +{ + BaseMode::enableActions(); + + mdoc.aCancelConstruction->setEnabled( true ); +} + +void BaseConstructMode::cancelConstruction() +{ + finish(); +} + +void PointConstructMode::enableActions() +{ + BaseMode::enableActions(); + + mdoc.aCancelConstruction->setEnabled( true ); +} + +void PointConstructMode::cancelConstruction() +{ + mdoc.doneMode( this ); +} + +void BaseConstructMode::selectObjects( const std::vector<ObjectHolder*>& os, KigWidget& w ) +{ + for ( std::vector<ObjectHolder*>::const_iterator i = os.begin(); i != os.end(); ++i ) + { + std::vector<ObjectCalcer*> args = getCalcers( mparents ); + assert( wantArgs( args, mdoc.document(), w ) != ArgsParser::Complete ); + selectObject( *i, w ); + }; +} + +void ConstructMode::handlePrelim( const std::vector<ObjectCalcer*>& args, const QPoint& p, KigPainter& pter, KigWidget& w ) +{ + // set the text next to the arrow cursor like in modes/normal.cc + QPoint textloc = p; + textloc.setX( textloc.x() + 15 ); + + mctor->handlePrelim( pter, args, mdoc.document(), w ); + + QString o = mctor->useText( *args.back(), args, mdoc.document(), w ); + pter.drawTextStd( textloc, o ); +} + +int ConstructMode::isAlreadySelectedOK( const std::vector<ObjectCalcer*>& os, + const int& pos ) +{ + return mctor->isAlreadySelectedOK( os, pos ); +} + +int ConstructMode::wantArgs( const std::vector<ObjectCalcer*>& os, KigDocument& d, KigWidget& w ) +{ + return mctor->wantArgs( os, d, w ); +} + +void BaseConstructMode::finish() +{ + mdoc.doneMode( this ); +} + +ConstructMode::ConstructMode( KigPart& d, const ObjectConstructor* ctor ) + : BaseConstructMode( d ), mctor( ctor ) +{ +} + +ConstructMode::~ConstructMode() +{ +} + +// does a test result have a frame by default ? +static const bool test_has_frame_dflt = true; + +void TestConstructMode::handlePrelim( const std::vector<ObjectCalcer*>& os, const QPoint& p, KigPainter& pter, KigWidget& w ) +{ + Args args; + std::transform( os.begin(), os.end(), std::back_inserter( args ), + std::mem_fun( &ObjectCalcer::imp ) ); + + // usetext + QString usetext = i18n( mtype->argsParser().usetext( args.back(), args ).c_str() ); + QPoint textloc = p; + textloc.setX( textloc.x() + 15 ); + pter.drawTextStd( textloc, usetext ); + + // test result + ObjectImp* data = mtype->calc( args, mdoc.document() ); + if ( ! data->valid() ) return; + assert( data->inherits( TestResultImp::stype() ) ); + QString outputtext = static_cast<TestResultImp*>( data )->data(); + TextImp ti( outputtext, w.fromScreen( p + QPoint( - 40, 30 ) ), test_has_frame_dflt ); + ti.draw( pter ); + + delete data; +} + +TestConstructMode::TestConstructMode( KigPart& d, const ArgsParserObjectType* type ) + : BaseConstructMode( d ), mtype( type ) +{ +} + +TestConstructMode::~TestConstructMode() +{ +} + +void ConstructMode::handleArgs( const std::vector<ObjectCalcer*>& args, KigWidget& w ) +{ + mctor->handleArgs( args, mdoc, w ); + finish(); +} + +int TestConstructMode::isAlreadySelectedOK( const std::vector<ObjectCalcer*>&, + const int& ) +{ + return false; +} + +int TestConstructMode::wantArgs( const std::vector<ObjectCalcer*>& os, KigDocument&, KigWidget& ) +{ + return mtype->argsParser().check( os ); +} + +void TestConstructMode::handleArgs( const std::vector<ObjectCalcer*>& args, KigWidget& ) +{ + mresult = new ObjectTypeCalcer( mtype, args ); + mresult->calc( mdoc.document() ); + mdoc.emitStatusBarText( i18n( "Now select the location for the result label." ) ); +} + +void TestConstructMode::leftClickedObject( ObjectHolder* o, const QPoint& p, + KigWidget& w, bool ctrlOrShiftDown ) +{ + if ( mresult ) { + QPoint qloc = p + QPoint( -40, 0 ); + Coordinate loc = w.fromScreen( qloc ); + + std::vector<ObjectCalcer*> parents; + parents.push_back( new ObjectConstCalcer( new IntImp( test_has_frame_dflt ) ) ); + parents.push_back( new ObjectConstCalcer( new PointImp( loc ) ) ); + parents.push_back( new ObjectConstCalcer( new StringImp( QString::fromLatin1( "%1" ) ) ) ); + assert( mresult->imp()->inherits( TestResultImp::stype() ) ); + parents.push_back( + new ObjectPropertyCalcer( + mresult.get(), mresult->imp()->propertiesInternalNames().findIndex( "test-result" ) ) ); + parents.back()->calc( mdoc.document() ); + + ObjectCalcer* ret = new ObjectTypeCalcer( TextType::instance(), parents ); + ret->calc( mdoc.document() ); + mdoc.addObject( new ObjectHolder( ret ) ); + + w.unsetCursor(); + mdoc.emitStatusBarText( QString::null ); + + finish(); + } + else + BaseConstructMode::leftClickedObject( o, p, w, ctrlOrShiftDown ); +} + +void TestConstructMode::midClicked( const QPoint& p, KigWidget& w ) +{ + if ( mresult ) { + // nothing to be done here, really + } + else + BaseConstructMode::midClicked( p, w ); +} + +void TestConstructMode::rightClicked( const std::vector<ObjectHolder*>& oco, const QPoint& p, KigWidget& w ) +{ + if ( mresult ) { + // nothing to be done here, really + } + else + BaseConstructMode::rightClicked( oco, p, w ); +} + +void TestConstructMode::mouseMoved( const std::vector<ObjectHolder*>& os, const QPoint& p, KigWidget& w, bool shiftPressed ) +{ + if ( mresult ) { + w.setCursor( KCursor::blankCursor() ); + + w.updateCurPix(); + KigPainter pter( w.screenInfo(), &w.curPix, mdoc.document() ); + + QPoint qloc = p + QPoint( -40, 0 ); + Coordinate loc = w.fromScreen( qloc ); + assert( dynamic_cast<const TestResultImp*>( mresult->imp() ) ); + TextImp ti( static_cast<const TestResultImp*>( mresult->imp() )->data(), loc, test_has_frame_dflt ); + ObjectDrawer d; + d.draw( ti, pter, false ); + + + w.updateWidget( pter.overlay() ); + } + else + BaseConstructMode::mouseMoved( os, p, w, shiftPressed ); +} + +QString ConstructMode::selectStatement( const std::vector<ObjectCalcer*>& args, const KigWidget& w ) +{ + return mctor->selectStatement( args, mdoc.document(), w ); +} + +QString TestConstructMode::selectStatement( const std::vector<ObjectCalcer*>& sel, const KigWidget& ) +{ + using namespace std; + Args args; + transform( sel.begin(), sel.end(), back_inserter( args ), mem_fun( &ObjectCalcer::imp ) ); + + std::string ret = mtype->argsParser().selectStatement( args ); + if ( ret.empty() ) return QString::null; + return i18n( ret.c_str() ); +} + +void PointConstructMode::redrawScreen( KigWidget* w ) +{ + w->redrawScreen( std::vector<ObjectHolder*>() ); +} + +void BaseConstructMode::redrawScreen( KigWidget* w ) +{ + w->redrawScreen( std::vector<ObjectHolder*>() ); +} diff --git a/kig/modes/construct_mode.h b/kig/modes/construct_mode.h new file mode 100644 index 00000000..fa1be86e --- /dev/null +++ b/kig/modes/construct_mode.h @@ -0,0 +1,154 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_MODES_CONSTRUCT_MODE_H +#define KIG_MODES_CONSTRUCT_MODE_H + +#include "base_mode.h" + +#include "../objects/object_calcer.h" + +class ArgsParserObjectType; +class ObjectConstructor; +class ObjectCalcer; + +class PointConstructMode + : public BaseMode +{ + /** + * this is the point that we move around, for the user to add + * somewhere.. + */ + ObjectTypeCalcer::shared_ptr mpt; +public: + PointConstructMode( KigPart& d ); + ~PointConstructMode(); +protected: + void leftClickedObject( ObjectHolder* o, const QPoint& p, + KigWidget& w, bool ctrlOrShiftDown ); + void midClicked( const QPoint& p, KigWidget& w ); + void rightClicked( const std::vector<ObjectHolder*>& oco, const QPoint& p, KigWidget& w ); + void mouseMoved( const std::vector<ObjectHolder*>& os, const QPoint& p, KigWidget& w, bool shiftpressed ); + + void enableActions(); + void cancelConstruction(); + + void redrawScreen( KigWidget* ); +}; + +class BaseConstructMode + : public BaseMode +{ + /** + * this is the point that we move around, in case the user wants to + * add a point somewhere.. + */ + ObjectTypeCalcer::shared_ptr mpt; + /** + * mp: this point always follows the cursor + * + * IMPORTANT: this Calcer must NEVER be added to the document, since + * its is used in constructors that need more input from the user + * to decide parameters of the constructed object that will be fixed + * afterwards (like the number of sides of a regular polygon) + */ + ObjectTypeCalcer* mcursor; + // we also allocate here the corresponding objectholder, since the + // only sensible place where to deallocate it is in the destructor + // of this class +// ObjectHolder* mcursorholder; + std::vector<ObjectHolder*> mparents; + + void leftReleased( QMouseEvent* e, KigWidget* v ); + +public: + void selectObject( ObjectHolder* o, KigWidget& w ); + void selectObjects( const std::vector<ObjectHolder*>& os, KigWidget& w ); + virtual ~BaseConstructMode(); +protected: + BaseConstructMode( KigPart& d ); +protected: + void leftClickedObject( ObjectHolder* o, const QPoint& p, + KigWidget& w, bool ctrlOrShiftDown ); + void midClicked( const QPoint& p, KigWidget& w ); + void rightClicked( const std::vector<ObjectHolder*>& oco, const QPoint& p, KigWidget& w ); + void mouseMoved( const std::vector<ObjectHolder*>& os, const QPoint& p, KigWidget& w, bool shiftpressed ); + + void enableActions(); + void cancelConstruction(); + void finish(); + +protected: + virtual void handlePrelim( const std::vector<ObjectCalcer*>& os, const QPoint& p, KigPainter&, KigWidget& w ) = 0; + virtual QString selectStatement( const std::vector<ObjectCalcer*>& args, const KigWidget& w ) = 0; + virtual int isAlreadySelectedOK( const std::vector<ObjectCalcer*>&, const int& ) = 0; + virtual int wantArgs( const std::vector<ObjectCalcer*>&, KigDocument& d, KigWidget& w ) = 0; + virtual void handleArgs( const std::vector<ObjectCalcer*>& args, KigWidget& w ) = 0; + + void redrawScreen( KigWidget* ); +}; + +class ConstructMode + : public BaseConstructMode +{ + const ObjectConstructor* mctor; +public: + ConstructMode( KigPart& d, const ObjectConstructor* ctor ); + ~ConstructMode(); + + void handlePrelim( const std::vector<ObjectCalcer*>& os, const QPoint& p, KigPainter&, KigWidget& w ); + QString selectStatement( const std::vector<ObjectCalcer*>& args, const KigWidget& w ); + int isAlreadySelectedOK( const std::vector<ObjectCalcer*>&, const int& ); + int wantArgs( const std::vector<ObjectCalcer*>&, KigDocument& d, KigWidget& w ); + void handleArgs( const std::vector<ObjectCalcer*>& args, KigWidget& w ); +}; + +/** + * This class constructs a test object. It has special needs over + * ConstructMode because first the arguments need to be chosen, and + * then the location for the resulting TextImp needs to be chosen. It + * also needs special code for the drawPrelim and wantArgs code. + * + * Therefore, we inherit from BaseConstructMode, and override the + * event callbacks, so that this mode behaves like a + * BaseConstructMode, until handleArgs is called. After that, mresult + * is no longer 0, and then the mode behaves in its own way, allowing + * the user to choose a location for the new label object. + */ +class TestConstructMode + : public BaseConstructMode +{ + const ArgsParserObjectType* mtype; + ObjectCalcer::shared_ptr mresult; +public: + TestConstructMode( KigPart& d, const ArgsParserObjectType* type ); + ~TestConstructMode(); + + void handlePrelim( const std::vector<ObjectCalcer*>& os, const QPoint& p, KigPainter&, KigWidget& w ); + QString selectStatement( const std::vector<ObjectCalcer*>& args, const KigWidget& w ); + int isAlreadySelectedOK( const std::vector<ObjectCalcer*>&, const int& ); + int wantArgs( const std::vector<ObjectCalcer*>&, KigDocument& d, KigWidget& w ); + void handleArgs( const std::vector<ObjectCalcer*>& args, KigWidget& w ); + + void leftClickedObject( ObjectHolder* o, const QPoint& p, + KigWidget& w, bool ctrlOrShiftDown ); + void midClicked( const QPoint& p, KigWidget& w ); + void rightClicked( const std::vector<ObjectHolder*>& oco, const QPoint& p, KigWidget& w ); + void mouseMoved( const std::vector<ObjectHolder*>& os, const QPoint& p, KigWidget& w, bool shiftpressed ); +}; + +#endif diff --git a/kig/modes/dragrectmode.cc b/kig/modes/dragrectmode.cc new file mode 100644 index 00000000..a3c8c033 --- /dev/null +++ b/kig/modes/dragrectmode.cc @@ -0,0 +1,180 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "dragrectmode.h" + +#include "../kig/kig_document.h" +#include "../kig/kig_part.h" +#include "../kig/kig_view.h" +#include "../misc/kigpainter.h" + +#include <qevent.h> +#include <qglobal.h> +#include <kaction.h> + +DragRectMode::DragRectMode( const QPoint& start, KigPart& d, KigWidget& w ) + : KigMode( d ), mstart( start ), mnc( true ), mstartselected( true ), + mcancelled( false ) +{ + moved( start, w ); +} + +DragRectMode::DragRectMode( KigPart& d, KigWidget& w ) + : KigMode( d ), mnc( true ), mstartselected( false ), + mcancelled( false ) +{ + w.updateCurPix(); + w.updateWidget(); +} + +void DragRectMode::moved( const QPoint& p, KigWidget& w ) +{ + // update the rect... + w.updateCurPix(); + std::vector<QRect> overlay; + if ( mstartselected ) + { + KigPainter pt( w.screenInfo(), &w.curPix, mdoc.document() ); + pt.drawFilledRect( QRect( p, mstart ) ); + overlay = pt.overlay(); + }; + w.updateWidget( overlay ); +} + +void DragRectMode::released( const QPoint& p, KigWidget& w, bool nc ) +{ + if ( mstartselected ) + { + mrect = w.fromScreen( QRect( mstart, p ) ); + mret = mdoc.document().whatIsInHere( mrect, w ); + mnc = nc; + + mdoc.doneMode( this ); + }; +} + +void DragRectMode::enableActions() +{ + KigMode::enableActions(); + + mdoc.aCancelConstruction->setEnabled( true ); +} + +std::vector<ObjectHolder*> DragRectMode::ret() const +{ + return mret; +} + +bool DragRectMode::needClear() const +{ + return mnc; +} + +void DragRectMode::moved( QMouseEvent* e, KigWidget& w ) +{ + moved( e->pos(), w ); +} + +void DragRectMode::released( QMouseEvent* e, KigWidget& w ) +{ + released( e->pos(), w, ! ( e->state() & ( ControlButton | ShiftButton ) ) ); +} + +DragRectMode::~DragRectMode() +{ +} + +void DragRectMode::mouseMoved( QMouseEvent* e, KigWidget* w ) +{ + moved( e, *w ); +} + +void DragRectMode::leftMouseMoved( QMouseEvent* e, KigWidget* w ) +{ + moved( e, *w ); +} + +void DragRectMode::midMouseMoved( QMouseEvent* e, KigWidget* w ) +{ + moved( e, *w ); +} + +void DragRectMode::rightMouseMoved( QMouseEvent* e, KigWidget* w ) +{ + moved( e, *w ); +} + +void DragRectMode::leftReleased( QMouseEvent* e, KigWidget* w ) +{ + released( e, *w ); +} + +void DragRectMode::midReleased( QMouseEvent* e, KigWidget* w ) +{ + released( e, *w ); +} + +void DragRectMode::rightReleased( QMouseEvent* e, KigWidget* w ) +{ + released( e, *w ); +} + +Rect DragRectMode::rect() const +{ + return mrect; +} + +void DragRectMode::clicked( const QMouseEvent* e, KigWidget& w ) +{ + clicked( e->pos(), w ); +} + +void DragRectMode::leftClicked( QMouseEvent* e, KigWidget* w ) +{ + clicked( e, *w ); +} + +void DragRectMode::midClicked( QMouseEvent* e, KigWidget* w ) +{ + clicked( e, *w ); +} + +void DragRectMode::rightClicked( QMouseEvent* e, KigWidget* w ) +{ + clicked( e, *w ); +} + +void DragRectMode::clicked( const QPoint& p, KigWidget& ) +{ + if ( !mstartselected ) + { + mstartselected = true; + mstart = p; + }; +} + +bool DragRectMode::cancelled() const +{ + return mcancelled; +} + +void DragRectMode::cancelConstruction() +{ + mcancelled = true; + mdoc.doneMode( this ); +} + diff --git a/kig/modes/dragrectmode.h b/kig/modes/dragrectmode.h new file mode 100644 index 00000000..29f92139 --- /dev/null +++ b/kig/modes/dragrectmode.h @@ -0,0 +1,104 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + + +#ifndef KIG_MODES_DRAGRECTMODE_H +#define KIG_MODES_DRAGRECTMODE_H + +#include "mode.h" + +#include "../misc/rect.h" + +#include <qpoint.h> +#include <vector> + +class ObjectHolder; + +/** + * DragRectMode is a mode that provides a rect for selecting the + * objects inside it. Here's an example of how to use it + * \code + * DragRectMode d( e->pos(), document, widget ); + * mDoc.runMode( &d ); + * Objects sel = d.ret(); + * \endcode + */ +class DragRectMode + : public KigMode +{ + QPoint mstart; + std::vector<ObjectHolder*> mret; + Rect mrect; + bool mnc; + bool mstartselected; + bool mcancelled; +private: + void clicked( const QPoint& p, KigWidget& w ); + void clicked( const QMouseEvent* e, KigWidget& w ); + void released( const QPoint& p, KigWidget& w, bool nc ); + void released( QMouseEvent* e, KigWidget& w ); + void moved( const QPoint& p, KigWidget& w ); + void moved( QMouseEvent*, KigWidget& w ); + + void leftClicked( QMouseEvent*, KigWidget* ); + void leftMouseMoved( QMouseEvent*, KigWidget* ); + void leftReleased( QMouseEvent*, KigWidget* ); + void midClicked( QMouseEvent*, KigWidget* ); + void midMouseMoved( QMouseEvent*, KigWidget* ); + void midReleased( QMouseEvent*, KigWidget* ); + void rightClicked( QMouseEvent*, KigWidget* ); + void rightMouseMoved( QMouseEvent*, KigWidget* ); + void rightReleased( QMouseEvent*, KigWidget* ); + void mouseMoved( QMouseEvent*, KigWidget* ); + + void cancelConstruction(); + + void enableActions(); + +public: + DragRectMode( const QPoint& start, KigPart& d, KigWidget& w ); + DragRectMode( KigPart& d, KigWidget& w ); + ~DragRectMode(); + + /** + * this returns the selected objects.. + */ + std::vector<ObjectHolder*> ret() const; + + /** + * this returns the selected rect.. + */ + Rect rect() const; + + /** + * this returns false if the control or shift button were pressed + * when the mouse button was released, and true otherwise. This is + * because the user expects us to not clear the selection before + * adding the newly selected objects if (s)he pressed control or + * shift.. + */ + bool needClear() const; + + /** + * whether the user cancelled the rect mode.. If this returns true, + * all the other return data above will be in undefined state, so + * first check this function's result.. + */ + bool cancelled() const; +}; + +#endif diff --git a/kig/modes/edittype.cc b/kig/modes/edittype.cc new file mode 100644 index 00000000..8c6b538f --- /dev/null +++ b/kig/modes/edittype.cc @@ -0,0 +1,107 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2004 Dominique Devriese <devriese@kde.org> + Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + + +#include "edittype.h" +#include "edittype.moc" + +#include <kapplication.h> +#include <kicondialog.h> +#include <klineedit.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kpushbutton.h> +#include <kstdguiitem.h> + +EditType::EditType( QWidget* parent, QString name, QString desc, QString icon ) + : EditTypeBase( parent, "edittype", true ), mname( name ), mdesc( desc ), micon( icon ) +{ + // improving GUI look'n'feel... + buttonHelp->setGuiItem( KStdGuiItem::help() ); + buttonOk->setGuiItem( KStdGuiItem::ok() ); + buttonCancel->setGuiItem( KStdGuiItem::cancel() ); + + editName->setText( mname ); + editDescription->setText( mdesc ); + typeIcon->setIcon( !micon.isEmpty() ? micon : "gear" ); +} + +EditType::~EditType() +{ +} + +void EditType::helpSlot() +{ + kapp->invokeHelp( QString::fromLatin1( "working-with-types" ), + QString::fromLatin1( "kig" ) ); +} + +void EditType::okSlot() +{ + QString tmp = editName->text(); + if ( tmp.isEmpty() ) + { + KMessageBox::information( this, i18n( "The name of the macro can not be empty." ) ); + return; + } + + bool namechanged = false; + bool descchanged = false; + bool iconchanged = false; + if ( tmp != mname ) + { + mname = tmp; + namechanged = true; + } + tmp = editDescription->text(); + if ( tmp != mdesc ) + { + mdesc = tmp; + descchanged = true; + } + tmp = typeIcon->icon(); + if ( tmp != micon ) + { + micon = tmp; + iconchanged = true; + } + done( namechanged || descchanged || iconchanged ); +} + +void EditType::cancelSlot() +{ + done( 0 ); +} + +const QString EditType::name() const +{ + return mname; +} + +const QString EditType::description() const +{ + return mdesc; +} + +const QString EditType::icon() const +{ + return micon; +} diff --git a/kig/modes/edittype.h b/kig/modes/edittype.h new file mode 100644 index 00000000..c6530df5 --- /dev/null +++ b/kig/modes/edittype.h @@ -0,0 +1,48 @@ +// This file is part of Kig, a KDE program for Interactive Geometry... +// Copyright (C) 2004 Dominique Devriese <devriese@kde.org> +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_MODES_EDITTYPE_H +#define KIG_MODES_EDITTYPE_H + +#include "edittypebase.h" + +/** + * Simply dialog that allow the user the editing of a macro type... + */ +class EditType : public EditTypeBase +{ + Q_OBJECT + + QString mname; + QString mdesc; + QString micon; +public: + EditType( QWidget* parent, QString name = QString::null, QString desc = QString::null, QString icon = QString::null ); + ~EditType(); + const QString name() const; + const QString description() const; + const QString icon() const; + +public slots: + void helpSlot(); + void okSlot(); + void cancelSlot(); +}; + +#endif diff --git a/kig/modes/edittypebase.ui b/kig/modes/edittypebase.ui new file mode 100644 index 00000000..0dc8129d --- /dev/null +++ b/kig/modes/edittypebase.ui @@ -0,0 +1,287 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>EditTypeBase</class> +<widget class="QDialog"> + <property name="name"> + <cstring>EditTypeBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>481</width> + <height>142</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="caption"> + <string>Edit Type</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>label15</cstring> + </property> + <property name="text"> + <string>Here you can modify the name, the description and the icon of this macro type.</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout8</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout8</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>label16</cstring> + </property> + <property name="text"> + <string>Name:</string> + </property> + </widget> + <widget class="KLineEdit"> + <property name="name"> + <cstring>editName</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Here you can edit the name of the current macro type.</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout1</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>label17</cstring> + </property> + <property name="text"> + <string>Description:</string> + </property> + </widget> + <widget class="KLineEdit"> + <property name="name"> + <cstring>editDescription</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Here you can edit the description of the current macro type. This field is optional, so you can also leave this empty: if you do so, then your macro type will have no description.</string> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout23</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KIconButton"> + <property name="name"> + <cstring>typeIcon</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string>Use this button to change the icon of the current macro type.</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </vbox> + </widget> + </hbox> + </widget> + <widget class="Line"> + <property name="name"> + <cstring>Line1</cstring> + </property> + <property name="frameShape"> + <enum>HLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout24</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="KPushButton"> + <property name="name"> + <cstring>buttonHelp</cstring> + </property> + <property name="text"> + <string>&Help</string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>Horizontal Spacing2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KPushButton"> + <property name="name"> + <cstring>buttonOk</cstring> + </property> + <property name="text"> + <string>&OK</string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>buttonCancel</cstring> + </property> + <property name="text"> + <string>&Cancel</string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<connections> + <connection> + <sender>buttonHelp</sender> + <signal>clicked()</signal> + <receiver>EditTypeBase</receiver> + <slot>helpSlot()</slot> + </connection> + <connection> + <sender>buttonOk</sender> + <signal>clicked()</signal> + <receiver>EditTypeBase</receiver> + <slot>okSlot()</slot> + </connection> + <connection> + <sender>buttonCancel</sender> + <signal>clicked()</signal> + <receiver>EditTypeBase</receiver> + <slot>cancelSlot()</slot> + </connection> +</connections> +<slots> + <slot>helpSlot()</slot> + <slot>okSlot()</slot> + <slot>cancelSlot()</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klineedit.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kicondialog.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/kig/modes/label.cc b/kig/modes/label.cc new file mode 100644 index 00000000..cd726918 --- /dev/null +++ b/kig/modes/label.cc @@ -0,0 +1,589 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "label.h" +#include "normal.h" + +#include "textlabelwizard.h" +#include "linkslabel.h" + +#include "../kig/kig_commands.h" +#include "../kig/kig_document.h" +#include "../kig/kig_part.h" +#include "../kig/kig_view.h" +#include "../misc/common.h" +#include "../misc/kigpainter.h" +#include "../objects/bogus_imp.h" +#include "../objects/curve_imp.h" +#include "../objects/object_factory.h" +#include "../objects/point_imp.h" +#include "../objects/text_imp.h" +#include "../objects/text_type.h" + +#include <kcursor.h> +#include <kmessagebox.h> +#include <kaction.h> +#include <klocale.h> +#include <qtextedit.h> +#include <kdebug.h> +#include <kiconloader.h> +#include <qregexp.h> +#include <qpopupmenu.h> +#include <qcheckbox.h> + +#include <algorithm> +#include <functional> + +class TextLabelModeBase::Private +{ +public: + // point last clicked.. + QPoint plc; + // the currently selected coordinate + Coordinate mcoord; + // the possible parent object that defines the location of the label.. + ObjectCalcer* locationparent; + + // the text is only kept in the text input widget, not here + // QString mtext; + + // the property objects we'll be using as args, we keep a reference + // to them in the args object, and keep a pointer to them ( or 0 ) + // in the correct order in args ( separately, because we can't use + // the order of the parents of a ReferenceObject, and certainly + // can't give 0 as a parent.. + argvect args; + + // if we're ReallySelectingArgs, then this var points to the arg + // we're currently selecting... + int mwaaws; + + // last percent count... + uint lpc; + + TextLabelWizard* wiz; + + // What Are We Doing + wawdtype mwawd; +}; + +TextLabelModeBase::~TextLabelModeBase() +{ + delete d->wiz; + delete d; +} + +TextLabelModeBase::TextLabelModeBase( KigPart& doc ) + : KigMode( doc ), d( new Private ) +{ + d->locationparent = 0; + d->lpc = 0; + d->mwawd = SelectingLocation; + d->wiz = new TextLabelWizard( doc.widget(), this ); +} + +void TextLabelModeBase::leftClicked( QMouseEvent* e, KigWidget* ) +{ + d->plc = e->pos(); + switch( d->mwawd ) + { + case RequestingText: + case SelectingArgs: + d->wiz->raise(); + d->wiz->setActiveWindow(); + break; + default: + break; + }; +} + +void TextLabelModeBase::leftReleased( QMouseEvent* e, KigWidget* v ) +{ + switch( d->mwawd ) + { + case SelectingLocation: + { + if ( ( d->plc - e->pos() ).manhattanLength() > 4 ) return; + setCoordinate( v->fromScreen( d->plc ) ); + break; + } + case RequestingText: + case SelectingArgs: + d->wiz->raise(); + d->wiz->setActiveWindow(); + break; + case ReallySelectingArgs: + { + if ( ( d->plc - e->pos() ).manhattanLength() > 4 ) break; + std::vector<ObjectHolder*> os = mdoc.document().whatAmIOn( v->fromScreen( d->plc ), *v ); + if ( os.empty() ) break; + ObjectHolder* o = os[0]; + QPopupMenu* p = new QPopupMenu( v, "text_label_select_arg_popup" ); + p->insertItem( i18n( "Name" ), 0 ); + QCStringList l = o->imp()->properties(); + assert( l.size() == o->imp()->numberOfProperties() ); + for ( int i = 0; static_cast<uint>( i ) < l.size(); ++i ) + { + QString s = i18n( l[i] ); + const char* iconfile = o->imp()->iconForProperty( i ); + int t; + if ( iconfile && *iconfile ) + { + QPixmap pix = mdoc.instance()->iconLoader()->loadIcon( iconfile, KIcon::User ); + t = p->insertItem( QIconSet( pix ), s, i + 1 ); + } + else + { + t = p->insertItem( s, i + 1 ); + }; + assert( t == i + 1 ); + }; + int result = p->exec( v->mapToGlobal( d->plc ) ); + ObjectCalcer::shared_ptr argcalcer; + if ( result == -1 ) break; + else if ( result == 0 ) + { + argcalcer = o->nameCalcer(); + if ( !argcalcer ) + { + ObjectConstCalcer* c = new ObjectConstCalcer( new StringImp( i18n( "<unnamed object>" ) ) ); + o->setNameCalcer( c ); + argcalcer = c; + } + } + else + { + assert( static_cast<uint>( result ) < l.size() + 1 ); + argcalcer = new ObjectPropertyCalcer( o->calcer(), result - 1 ); + } + d->args[d->mwaaws] = argcalcer.get(); + argcalcer->calc( mdoc.document() ); + + updateLinksLabel(); + updateWiz(); + break; + } + default: + assert( false ); + break; + }; +} + +void TextLabelModeBase::killMode() +{ + mdoc.doneMode( this ); +} + +void TextLabelModeBase::cancelConstruction() +{ + killMode(); +} + +void TextLabelModeBase::enableActions() +{ + KigMode::enableActions(); + + mdoc.aCancelConstruction->setEnabled( true ); +} + +void TextLabelModeBase::mouseMoved( QMouseEvent* e, KigWidget* w ) +{ + if ( d->mwawd == ReallySelectingArgs ) + { + std::vector<ObjectHolder*> os = mdoc.document().whatAmIOn( w->fromScreen( e->pos() ), *w ); + if ( !os.empty() ) w->setCursor( KCursor::handCursor() ); + else w->setCursor( KCursor::arrowCursor() ); + } + else if ( d->mwawd == SelectingLocation ) + { + std::vector<ObjectHolder*> os = mdoc.document().whatAmIOn( w->fromScreen( e->pos() ), *w ); + bool attachable = false; + d->locationparent = 0; + for ( std::vector<ObjectHolder*>::iterator i = os.begin(); i != os.end(); ++i ) + { + if( (*i)->imp()->attachPoint().valid() || + (*i)->imp()->inherits( PointImp::stype() ) || + (*i)->imp()->inherits( CurveImp::stype() ) ) + { + attachable = true; + d->locationparent = (*i)->calcer(); + break; + }; + }; + w->updateCurPix(); + if ( attachable ) + { + w->setCursor( KCursor::handCursor() ); + QString s = d->locationparent->imp()->type()->attachToThisStatement(); + mdoc.emitStatusBarText( s ); + + KigPainter p( w->screenInfo(), &w->curPix, mdoc.document() ); + + // set the text next to the arrow cursor + QPoint point = e->pos(); + point.setX(point.x()+15); + + p.drawTextStd( point, s ); + w->updateWidget( p.overlay() ); + } + else + { + w->setCursor( KCursor::crossCursor() ); + mdoc.emitStatusBarText( 0 ); + w->updateWidget(); + }; + } +} + +void TextLabelModeBase::enterTextPageEntered() +{ +} + +void TextLabelModeBase::selectArgumentsPageEntered() +{ + updateLinksLabel(); +} + +void TextLabelModeBase::cancelPressed() +{ + cancelConstruction(); +} + +static uint percentCount( const QString& s ) +{ +// QRegExp re( QString::fromUtf8( "%[0-9]" ) ); + QRegExp re( QString::fromUtf8( "%[\\d]+" ) ); + int offset = 0; + uint percentcount = 0; + while ( ( offset = re.search( s, offset ) ) != -1 ) + { + ++percentcount; + offset += re.matchedLength(); + }; + return percentcount; +} + +void TextLabelModeBase::finishPressed() +{ + bool needframe = d->wiz->needFrameCheckBox->isChecked(); + QString s = d->wiz->labelTextInput->text(); + + assert( percentCount( s ) == d->args.size() ); + if ( d->wiz->currentPage() == d->wiz->enter_text_page ) + assert( d->args.size() == 0 ); + + bool finished = true; + for ( argvect::iterator i = d->args.begin(); i != d->args.end(); ++i ) + finished &= ( *i != 0 ); + + if ( ! finished ) + KMessageBox::sorry( mdoc.widget(), + i18n( "There are '%n' parts in the text that you have not selected a " + "value for. Please remove them or select enough arguments." ) ); + else + { + finish( d->mcoord, s, d->args, needframe, d->locationparent ); + killMode(); + }; +} + +void TextLabelModeBase::updateWiz() +{ + QString s = d->wiz->labelTextInput->text(); + uint percentcount = percentCount( s ); + if ( d->lpc > percentcount ) + { + d->args = argvect( d->args.begin(), d->args.begin() + percentcount ); + } + else if ( d->lpc < percentcount ) + { + d->args.resize( percentcount, 0 ); + }; + + if ( percentcount == 0 && ! s.isEmpty() ) + { + d->wiz->setNextEnabled( d->wiz->enter_text_page, false ); + d->wiz->setFinishEnabled( d->wiz->enter_text_page, true ); + d->wiz->setAppropriate( d->wiz->select_arguments_page, false ); + } + else + { + d->wiz->setAppropriate( d->wiz->select_arguments_page, !s.isEmpty() ); + d->wiz->setNextEnabled( d->wiz->enter_text_page, ! s.isEmpty() ); + d->wiz->setFinishEnabled( d->wiz->enter_text_page, false ); + bool finished = true; + for ( argvect::iterator i = d->args.begin(); i != d->args.end(); ++i ) + finished &= ( *i != 0 ); + assert( percentCount( s ) == d->args.size() ); + + d->wiz->setFinishEnabled( d->wiz->select_arguments_page, finished ); + }; + + d->lpc = percentcount; +} + +void TextLabelModeBase::labelTextChanged() +{ + updateWiz(); +} + +void TextLabelModeBase::updateLinksLabel() +{ + LinksLabel::LinksLabelEditBuf buf = d->wiz->myCustomWidget1->startEdit(); + QString s = d->wiz->labelTextInput->text(); +// QRegExp re( "%[0-9]" ); + QRegExp re( "%[\\d]+" ); + int prevpos = 0; + int pos = 0; + uint count = 0; + // we split up the string into text and "links" + while ( ( pos = re.search( s, pos ) ) != -1 ) + { + // prevpos is the first character after the last match, pos is the + // first char of the current match.. + if ( prevpos != pos ) + { + // there is a text part between the previous and the current + // "link"... + assert( prevpos < pos ); + // fetch the text part... + QString subs = s.mid( prevpos, pos - prevpos ); + // and add it... + d->wiz->myCustomWidget1->addText( subs, buf ); + }; + // we always need a link part... + QString linktext( "%1" ); + assert( count < d->args.size() ); + if ( d->args[count] ) + { + // if the user has already selected a property, then we show its + // value... + d->args[count]->imp()->fillInNextEscape( linktext, mdoc.document() ); + } + else + // otherwise, we show a stub... + linktext = i18n( "argument %1" ).arg( count + 1 ); + + d->wiz->myCustomWidget1->addLink( linktext, buf ); + // set pos and prevpos to the next char after the last match, so + // we don't enter infinite loops... +// pos += 2; + pos += re.matchedLength(); + prevpos = pos; + ++count; + }; + + if ( static_cast<uint>( prevpos ) != s.length() ) + d->wiz->myCustomWidget1->addText( s.mid( prevpos ), buf ); + + d->wiz->myCustomWidget1->applyEdit( buf ); + d->wiz->relayoutArgsPage(); + + d->wiz->resize( d->wiz->size() ); +} + +void TextLabelModeBase::linkClicked( int i ) +{ + mdoc.widget()->setActiveWindow(); + mdoc.widget()->raise(); + + assert( d->args.size() >= static_cast<uint>( i + 1 ) ); + + d->mwawd = ReallySelectingArgs; + d->mwaaws = i; + + mdoc.emitStatusBarText( i18n( "Selecting argument %1" ).arg( i + 1 ) ); +} + +void TextLabelModeBase::redrawScreen( KigWidget* w ) +{ + w->redrawScreen( std::vector<ObjectHolder*>() ); + w->updateScrollBars(); +} + +void TextLabelModeBase::setCoordinate( const Coordinate& coord ) +{ + d->mcoord = coord; + if ( d->mwawd == SelectingLocation ) + { + d->mwawd = RequestingText; + updateWiz(); + d->wiz->show(); + // shouldn't be necessary, but seems to be anyway.. :( + updateWiz(); + }; +} + +void TextLabelModeBase::setText( const QString& s ) +{ + d->wiz->labelTextInput->setText( s ); +} + +void TextLabelModeBase::setPropertyObjects( const argvect& props ) +{ + d->args = props; + for ( argvect::iterator i = d->args.begin(); i != d->args.end(); ++i ) + (*i)->calc( mdoc.document() ); +} + +TextLabelConstructionMode::TextLabelConstructionMode( KigPart& d ) + : TextLabelModeBase( d ) +{ +} + +TextLabelConstructionMode::~TextLabelConstructionMode() +{ +} + +void TextLabelConstructionMode::finish( + const Coordinate& coord, const QString& s, + const argvect& props, bool needframe, + ObjectCalcer* locationparent ) +{ + std::vector<ObjectCalcer*> args; + for ( argvect::const_iterator i = props.begin(); + i != props.end(); ++i ) + args.push_back( i->get() ); + + ObjectHolder* label = 0; + if ( locationparent ) + label = ObjectFactory::instance()->attachedLabel( s, locationparent, coord, needframe, args, mdoc.document() ); + else + label = ObjectFactory::instance()->label( s, coord, needframe, args, mdoc.document() ); + mdoc.addObject( label ); +} + +TextLabelRedefineMode::TextLabelRedefineMode( KigPart& d, ObjectTypeCalcer* label ) + : TextLabelModeBase( d ), mlabel( label ) +{ + assert( label->imp()->inherits( TextImp::stype() ) ); + std::vector<ObjectCalcer*> parents = label->parents(); + assert( parents.size() >= 3 ); + std::vector<ObjectCalcer*> firstthree( parents.begin(), parents.begin() + 3 ); + std::vector<ObjectCalcer*> rest( parents.begin() + 3, parents.end() ); + firstthree = TextType::instance()->argParser().parse( firstthree ); + + assert( firstthree[0]->imp()->inherits( IntImp::stype() ) ); + assert( firstthree[1]->imp()->inherits( PointImp::stype() ) ); + assert( firstthree[2]->imp()->inherits( StringImp::stype() ) ); + + bool frame = static_cast<const IntImp*>( firstthree[0]->imp() )->data() != 0; + Coordinate coord = static_cast<const PointImp*>( firstthree[1]->imp() )->coordinate(); + QString text = static_cast<const StringImp*>( firstthree[2]->imp() )->data(); + + // don't set it, let the user redefine it.. +// setCoordinate( coord ); + setText( text ); + setFrame( frame ); + + argvect v; + for ( uint i = 0; i < rest.size(); ++i ) + { + v.push_back( rest[i] ); + }; + assert( v.size() == rest.size() ); + + setPropertyObjects( v ); +} + +TextLabelRedefineMode::~TextLabelRedefineMode() +{ +} + +void TextLabelRedefineMode::finish( + const Coordinate& coord, const QString& s, + const argvect& props, bool needframe, + ObjectCalcer* locationparent ) +{ + std::vector<ObjectCalcer*> parents = mlabel->parents(); + assert( parents.size() >= 3 ); + std::vector<ObjectCalcer*> firstthree( parents.begin(), parents.begin() + 3 ); + std::vector<ObjectCalcer*> rest( parents.begin() + 3, parents.end() ); + firstthree = TextType::instance()->argParser().parse( firstthree ); + + KigCommand* kc = new KigCommand( mdoc, i18n( "Change Label" ) ); + MonitorDataObjects mon( firstthree ); + + assert( firstthree[0]->imp()->inherits( IntImp::stype() ) ); + assert( firstthree[1]->imp()->inherits( PointImp::stype() ) ); + assert( firstthree[2]->imp()->inherits( StringImp::stype() ) ); + + assert( dynamic_cast<ObjectConstCalcer*>( firstthree[0] ) ); + assert( dynamic_cast<ObjectConstCalcer*>( firstthree[2] ) ); + static_cast<ObjectConstCalcer*>( firstthree[0] )->setImp( new IntImp( needframe ? 1 : 0 ) ); + + // we don't do this, because + // 1 this isn't necessarily a DataObject, we also support it to be a + // user-known point, or an internal constrained point.. + // 2 we don't know that we don't want it to become a user-known + // point or an internal constrained point, instead of a + // DataObject.. + // static_cast<DataObject*>( firstthree[1] )->setImp( new PointImp( + // coord ) ); + + static_cast<ObjectConstCalcer*>( firstthree[2] )->setImp( new StringImp( s ) ); + mon.finish( kc ); + + std::vector<ObjectCalcer*> oldparents = mlabel->parents(); + std::vector<ObjectCalcer*> p; + for ( argvect::const_iterator i = props.begin(); + i != props.end(); ++i ) + p.push_back( i->get() ); + for ( std::vector<ObjectCalcer*>::iterator i = p.begin(); + i != p.end(); ++i ) + ( *i )->calc( mdoc.document() ); + + std::vector<ObjectCalcer*> np = firstthree; + /* + * take advantage of the method "getAttachPoint" that should + * do all the work; it is also used when creating a new label + */ + np[1] = ObjectFactory::instance()->getAttachPoint( locationparent, coord, mdoc.document() ); + +/* this is the old code, just in case... */ +// if ( locationparent && locationparent->imp()->inherits( CurveImp::stype() ) ) +// { +// double param = static_cast<const CurveImp*>( locationparent->imp() )->getParam( coord, mdoc.document() ); +// np[1] = ObjectFactory::instance()->constrainedPointCalcer( locationparent, param ); +// np[1]->calc( mdoc.document() ); +// } +// else if ( locationparent ) +// { +// assert( locationparent->imp()->inherits( PointImp::stype() ) ); +// np[1] = locationparent; +// } +// else +// np[1] = new ObjectConstCalcer( new PointImp( coord ) ); + + copy( p.begin(), p.end(), back_inserter( np ) ); + + kc->addTask( + new ChangeParentsAndTypeTask( + mlabel, np, TextType::instance() ) ); + + mdoc.history()->addCommand( kc ); +} + +void TextLabelModeBase::setFrame( bool f ) +{ + d->wiz->needFrameCheckBox->setChecked( f ); +} + +void TextLabelModeBase::setLocationParent( ObjectCalcer* o ) +{ + d->locationparent = o; +} diff --git a/kig/modes/label.h b/kig/modes/label.h new file mode 100644 index 00000000..4a3d09f7 --- /dev/null +++ b/kig/modes/label.h @@ -0,0 +1,129 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_MODE_LABEL_H +#define KIG_MODE_LABEL_H + +#include "mode.h" + +#include "../objects/object_calcer.h" + +#include <vector> + +class TextLabelWizard; +class NormalMode; +class Coordinate; +class QString; +class ObjectPropertyCalcer; +class ObjectTypeCalcer; +class ObjectCalcer; + +/** + * this is the base class for TextLabelConstructionMode and + * TextLabelRedefineMode.. most of the work is done in this class, + * with some specific things delegated to the children.. Template + * method pattern, right ? :) + */ +class TextLabelModeBase + : public KigMode +{ + class Private; + Private* d; + +public: + // below is the interface towards TextLabelWizard... + void cancelPressed(); + void finishPressed(); + void enterTextPageEntered(); + void selectArgumentsPageEntered(); + void labelTextChanged(); + void linkClicked( int ); + void redrawScreen( KigWidget* w ); + +protected: + typedef std::vector<ObjectCalcer::shared_ptr> argvect; + // the protected interface for subclasses + TextLabelModeBase( KigPart& d ); + ~TextLabelModeBase(); + + void setCoordinate( const Coordinate& coord ); + void setText( const QString& s ); + void setLocationParent( ObjectCalcer* o ); + /** + * objects you pass here, should be newly created property objects, + * that have no children.. + */ + void setPropertyObjects( const argvect& props ); + void setFrame( bool f ); + + virtual void finish( const Coordinate& c, const QString& s, + const argvect& props, bool needframe, + ObjectCalcer* locationparent ) = 0; + +private: + // the KigMode interface.. + void leftClicked( QMouseEvent*, KigWidget* ); + void leftReleased( QMouseEvent*, KigWidget* ); + + void mouseMoved( QMouseEvent*, KigWidget* ); + + void enableActions(); + + void cancelConstruction(); + + void killMode(); + +private: + /** + * \internal + * What Are We Doing... + * the diff between SelectingArgs and ReallySelectingArgs is that + * the latter means the user is selecting an arg in the kig window, + * whereas the first only means that he's looking at the second + * page of the wizard... + */ + typedef enum { SelectingLocation, RequestingText, SelectingArgs, ReallySelectingArgs } wawdtype; + + void updateWiz(); + void updateLinksLabel(); +}; + +class TextLabelConstructionMode + : public TextLabelModeBase +{ +public: + TextLabelConstructionMode( KigPart& d ); + ~TextLabelConstructionMode(); + + void finish( const Coordinate& coord, const QString& s, + const argvect& props, bool needframe, + ObjectCalcer* locationparent ); +}; + +class TextLabelRedefineMode + : public TextLabelModeBase +{ + ObjectTypeCalcer* mlabel; + void finish( const Coordinate& coord, const QString& s, + const argvect& props, bool needframe, + ObjectCalcer* locationparent ); +public: + TextLabelRedefineMode( KigPart& d, ObjectTypeCalcer* label ); + ~TextLabelRedefineMode(); +}; + +#endif diff --git a/kig/modes/linkslabel.cpp b/kig/modes/linkslabel.cpp new file mode 100644 index 00000000..572c0bf8 --- /dev/null +++ b/kig/modes/linkslabel.cpp @@ -0,0 +1,134 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301 USA + + +#include "linkslabel.h" +#include "linkslabel.moc" + +#include <qlabel.h> +#include <kurllabel.h> +#include <qlayout.h> + +#include <vector> +#include <algorithm> +#include <functional> + +#include <assert.h> +using namespace std; + +class LinksLabel::Private +{ +public: + QHBoxLayout* layout; + std::vector<QLabel*> labels; + std::vector<KURLLabel*> urllabels; +}; + +LinksLabel::LinksLabel( QWidget* parent, const char* name ) + : QWidget( parent, name ) +{ + p = new Private; + p->layout = new QHBoxLayout( this ); + + QLabel* l = new QLabel( QString::fromUtf8( "Dit is een " ), this ); + p->labels.push_back( l ); + p->layout->addWidget( l ); + + KURLLabel* u = new KURLLabel( QString::fromUtf8( "http://www.kde.org/" ), + QString::fromUtf8( "url"), this ); + p->urllabels.push_back( u ); + p->layout->addWidget( u ); + + l = new QLabel( QString::fromUtf8( " !" ), this ); + p->labels.push_back( l ); + p->layout->addWidget(l ); + + p->layout->activate(); +} + +LinksLabel::~LinksLabel() +{ + delete p; +} + +void LinksLabel::urlClicked() +{ + const QObject* o = sender(); + std::vector<KURLLabel*>::iterator i = std::find( p->urllabels.begin(), p->urllabels.end(), static_cast<const KURLLabel*>( o ) ); + assert( i != p->urllabels.end() ); + emit linkClicked( i - p->urllabels.begin() ); +} + +LinksLabel::LinksLabelEditBuf LinksLabel::startEdit() +{ + return LinksLabelEditBuf(); +} + +void LinksLabel::addText( const QString& s, LinksLabelEditBuf& buf ) +{ + buf.data.push_back( std::pair<bool, QString>( false, s ) ); +} + +void LinksLabel::addLink( const QString& s, LinksLabelEditBuf& buf ) +{ + buf.data.push_back( std::pair<bool, QString>( true, s ) ); +} + +namespace { + void deleteObj( QObject* o ) { delete o; } +} + +void LinksLabel::applyEdit( LinksLabelEditBuf& buf ) +{ + std::for_each( p->urllabels.begin(), p->urllabels.end(), deleteObj ); + std::for_each( p->labels.begin(), p->labels.end(), deleteObj ); + p->urllabels.clear(); + p->labels.clear(); + + delete p->layout; + p->layout = new QHBoxLayout( this ); + + for ( LinksLabelEditBuf::vec::iterator i = buf.data.begin(); i != buf.data.end(); ++i ) + { + if ( i->first ) + { + // we need a KURLLabel... + // the url is an unused stub... + KURLLabel* l = new KURLLabel( QString::fromUtf8( "http://edu.kde.org/kig" ), + i->second, this ); + p->urllabels.push_back( l ); + p->layout->addWidget( l ); + connect( l, SIGNAL( leftClickedURL() ), SLOT( urlClicked() ) ); + } + else + { + // we need a normal label... + QLabel* l = new QLabel( i->second, this ); + p->labels.push_back( l ); + p->layout->addWidget( l ); + }; + }; + + QSpacerItem* spacer = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum ); + + p->layout->addItem( spacer ); + + p->layout->activate(); + + std::for_each( p->urllabels.begin(), p->urllabels.end(), mem_fun( &QWidget::show ) ); + std::for_each( p->labels.begin(), p->labels.end(), mem_fun( &QWidget::show ) ); +} diff --git a/kig/modes/linkslabel.h b/kig/modes/linkslabel.h new file mode 100644 index 00000000..ba64dbc2 --- /dev/null +++ b/kig/modes/linkslabel.h @@ -0,0 +1,85 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301 USA + +#ifndef KIG_lINKS_LABEL_H +#define KIG_lINKS_LABEL_H + +#include <qwidget.h> + +#include <vector> +#include <utility> + +/** + * this widget shows a line of text, with some links underlined, and + * emits a signal if one of the links is clicked... + */ +class LinksLabel : public QWidget +{ + Q_OBJECT + +public: + LinksLabel( QWidget* parent = 0, const char* name = 0 ); + ~LinksLabel(); + + class LinksLabelEditBuf + { + public: + friend class LinksLabel; + ~LinksLabelEditBuf() {} + private: + // declare these private so only LinksLabel can use them... + LinksLabelEditBuf() {} + typedef std::vector<std::pair<bool,QString> > vec; + vec data; + }; + + /** + * start editing, start recording changes in a LinksLabelEditBuf, + * but don't apply them until after endEdit(); + */ + LinksLabelEditBuf startEdit(); + /** + * add a piece of normal text.. + */ + void addText( const QString& s, LinksLabelEditBuf& buf ); + /** + * add a link... + */ + void addLink( const QString& s, LinksLabelEditBuf& buf ); + /** + * apply the changes... This clears the current contents and adds + * the new data... + */ + void applyEdit( LinksLabelEditBuf& buf ); + +signals: + /** + * the user clicked on a link. The index is the order in which it + * was added. E.g. this signal is emitted with arg 0 if the link + * you first added is clicked, argument 2 for the third link etc. + */ + void linkClicked( int i ); + +private slots: + void urlClicked(); + +private: + class Private; + Private* p; +}; + +#endif // KDE_URLS_LABEL_H diff --git a/kig/modes/macro.cc b/kig/modes/macro.cc new file mode 100644 index 00000000..879466dc --- /dev/null +++ b/kig/modes/macro.cc @@ -0,0 +1,245 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "macro.h" + +#include "macrowizard.h" +#include "dragrectmode.h" +#include "../kig/kig_part.h" +#include "../kig/kig_view.h" +#include "../misc/kigpainter.h" +#include "../misc/object_constructor.h" +#include "../misc/lists.h" +#include "../misc/guiaction.h" +#include "../objects/object_imp.h" + +#include <klineedit.h> +#include <kmessagebox.h> +#include <kcursor.h> +#include <klocale.h> + +#include <functional> +#include <algorithm> + +using namespace std; + +DefineMacroMode::DefineMacroMode( KigPart& d ) + : BaseMode( d ) +{ + mwizard = new MacroWizard( d.widget(), this ); + mwizard->show(); + updateNexts(); +} + +DefineMacroMode::~DefineMacroMode() +{ + delete mwizard; +} + +void DefineMacroMode::abandonMacro() +{ + mdoc.doneMode( this ); +} + +void DefineMacroMode::updateNexts() +{ + mwizard->setNextEnabled( mwizard->mpgiven, + !mgiven.empty() ); + mwizard->setNextEnabled( mwizard->mpfinal, + !mfinal.empty() ); + mwizard->setFinishEnabled( + mwizard->mpname, + !mwizard->KLineEdit2->text().isEmpty() + ); +} + +void DefineMacroMode::enableActions() +{ + KigMode::enableActions(); + // we don't enable any actions... +} + +void DefineMacroMode::givenPageEntered() +{ + std::vector<ObjectHolder*> given( mgiven.begin(), mgiven.end() ); + static_cast<KigView*>( mdoc.widget() )->realWidget()->redrawScreen( given ); + updateNexts(); +} + +void DefineMacroMode::finalPageEntered() +{ + std::vector<ObjectHolder*> final( mfinal.begin(), mfinal.end() ); + static_cast<KigView*>( mdoc.widget() )->realWidget()->redrawScreen( final ); + + updateNexts(); +} + +void DefineMacroMode::namePageEntered() +{ + ObjectCalcer* (ObjectHolder::*memfun)() = &ObjectHolder::calcer; + std::vector<ObjectCalcer*> given; + std::transform( mgiven.begin(), mgiven.end(), + std::back_inserter( given ), + std::mem_fun( memfun ) ); + std::vector<ObjectCalcer*> final; + std::transform( mfinal.begin(), mfinal.end(), + std::back_inserter( final ), + std::mem_fun( memfun ) ); + ObjectHierarchy hier( given, final ); + if ( hier.resultDoesNotDependOnGiven() ) + { + KMessageBox::sorry( mwizard, + i18n( "One of the result objects you selected " + "cannot be calculated from the given objects. " + "Kig cannot calculate this macro because of this. " + "Please press Back, and construct the objects " + "in the correct order..." ) ); + mwizard->back(); + } + else if( !hier.allGivenObjectsUsed() ) + { + KMessageBox::sorry( mwizard, + i18n( "One of the given objects is not used in the " + "calculation of the resultant objects. This " + "probably means you are expecting Kig to do " + "something impossible. Please check the " + "macro and try again." ) ); + mwizard->back(); + } + + static_cast<KigView*>( mdoc.widget() )->realWidget()->redrawScreen( std::vector<ObjectHolder*>() ); + + updateNexts(); +} + +void DefineMacroMode::finishPressed() +{ + ObjectCalcer* (ObjectHolder::*memfun)() = &ObjectHolder::calcer; + std::vector<ObjectCalcer*> given; + std::transform( mgiven.begin(), mgiven.end(), + std::back_inserter( given ), + std::mem_fun( memfun ) ); + std::vector<ObjectCalcer*> final; + std::transform( mfinal.begin(), mfinal.end(), + std::back_inserter( final ), + std::mem_fun( memfun ) ); + ObjectHierarchy hier( given, final ); + MacroConstructor* ctor = + new MacroConstructor( hier, + mwizard->KLineEdit2->text(), + mwizard->KLineEdit1->text() ); + ConstructibleAction* act = new ConstructibleAction( ctor, 0 ); + MacroList::instance()->add( new Macro( act, ctor ) ); + + abandonMacro(); +} + +void DefineMacroMode::cancelPressed() +{ + abandonMacro(); +} + +void DefineMacroMode::macroNameChanged() +{ + mwizard->setFinishEnabled( + mwizard->mpname, + !mwizard->KLineEdit2->text().isEmpty() + ); +} + +void DefineMacroMode::dragRect( const QPoint& p, KigWidget& w ) +{ + if ( mwizard->currentPage() == mwizard->mpname ) return; + std::vector<ObjectHolder*>* objs = mwizard->currentPage() == mwizard->mpgiven ? &mgiven : &mfinal; + DragRectMode dm( p, mdoc, w ); + mdoc.runMode( &dm ); + KigPainter pter( w.screenInfo(), &w.stillPix, mdoc.document() ); + if ( ! dm.cancelled() ) + { + std::vector<ObjectHolder*> ret = dm.ret(); + if ( dm.needClear() ) + { + pter.drawObjects( objs->begin(), objs->end(), false ); + objs->clear(); + } + + std::copy( ret.begin(), ret.end(), std::back_inserter( *objs ) ); + pter.drawObjects( objs->begin(), objs->end(), true ); + }; + w.updateCurPix( pter.overlay() ); + w.updateWidget(); + + updateNexts(); +} + +void DefineMacroMode::leftClickedObject( ObjectHolder* o, const QPoint&, + KigWidget& w, bool ) +{ + if ( mwizard->currentPage() == mwizard->mpname ) return; + std::vector<ObjectHolder*>* objs = mwizard->currentPage() == mwizard->mpgiven ? &mgiven : &mfinal; + std::vector<ObjectHolder*>::iterator iter = std::find( objs->begin(), objs->end(), o ); + bool isselected = ( iter != objs->end() ); + if ( isselected ) objs->erase( iter ); + else objs->push_back( o ); + + KigPainter p( w.screenInfo(), &w.stillPix, mdoc.document() ); + p.drawObject( o, !isselected ); + w.updateCurPix( p.overlay() ); + w.updateWidget(); + + updateNexts(); +} + +void DefineMacroMode::mouseMoved( const std::vector<ObjectHolder*>& os, const QPoint& pt, KigWidget& w, bool ) +{ + w.updateCurPix(); + + if ( os.empty() ) + { + w.setCursor( KCursor::arrowCursor() ); + mdoc.emitStatusBarText( 0 ); + w.updateWidget(); + } + else + { + // the cursor is over an object, show object type next to cursor + // and set statusbar text + + w.setCursor( KCursor::handCursor() ); + QString selectstat = os.front()->selectStatement(); + + // statusbar text + mdoc.emitStatusBarText( selectstat ); + KigPainter p( w.screenInfo(), &w.curPix, mdoc.document() ); + + // set the text next to the arrow cursor + QPoint point = pt; + point.setX(point.x()+15); + + p.drawTextStd( point, selectstat ); + w.updateWidget( p.overlay() ); + } +} + +void DefineMacroMode::rightClicked( const std::vector<ObjectHolder*>&, const QPoint&, KigWidget& ) +{ +} + +void DefineMacroMode::midClicked( const QPoint&, KigWidget& ) +{ +} + diff --git a/kig/modes/macro.h b/kig/modes/macro.h new file mode 100644 index 00000000..4bf8c43e --- /dev/null +++ b/kig/modes/macro.h @@ -0,0 +1,68 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_MODES_MACRO_H +#define KIG_MODES_MACRO_H + +#include "base_mode.h" + +#include <qobject.h> + +class MacroWizard; + +class DefineMacroMode + : public BaseMode +{ +public: + DefineMacroMode( KigPart& ); + ~DefineMacroMode(); + + void dragRect( const QPoint& p, KigWidget& w ); + void leftClickedObject( ObjectHolder* o, const QPoint& p, + KigWidget& w, bool ctrlOrShiftDown ); + void rightClicked( const std::vector<ObjectHolder*>& oco, const QPoint& p, KigWidget& w ); + void midClicked( const QPoint& p, KigWidget& w ); + void mouseMoved( const std::vector<ObjectHolder*>& os, const QPoint& p, KigWidget& w, bool shiftpressed ); + + // called by MacroWizard class + void givenPageEntered(); + void finalPageEntered(); + void namePageEntered(); + void finishPressed(); + void cancelPressed(); + void macroNameChanged(); + +protected: + void enableActions(); + /** + * update the enabled state of the next buttons on the wizard... + */ + void updateNexts(); + /** + * quit this mode... + */ + void abandonMacro(); + + QPoint plc; + MacroWizard* mwizard; + + // we can't use a set for this because the order is important + std::vector<ObjectHolder*> mgiven; + std::vector<ObjectHolder*> mfinal; +}; + +#endif diff --git a/kig/modes/macrowizard.cc b/kig/modes/macrowizard.cc new file mode 100644 index 00000000..e6315caf --- /dev/null +++ b/kig/modes/macrowizard.cc @@ -0,0 +1,90 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "macrowizard.h" +#include "macrowizard.moc" + +#include "macro.h" + +#include <kdebug.h> +#include <klineedit.h> +#include <kapplication.h> + +MacroWizard::MacroWizard( QWidget* parent, DefineMacroMode* m ) + : MacroWizardBase( parent, "Define Macro Wizard", false ), mmode( m ) +{ + connect( KLineEdit2, SIGNAL( textChanged( const QString& ) ), + this, SLOT( nameTextChanged( const QString& ) ) ); + connect( this, SIGNAL( helpClicked() ), this, + SLOT( slotHelpClicked() ) ); +} + +MacroWizard::~MacroWizard() +{ +} + +void MacroWizard::back() +{ + if ( currentPage() == mpfinal ) + { + // currentPage() is not yet updated when we get here, so this + // means that the page about to be shown is actually mpgiven... + mmode->givenPageEntered(); + } + else if ( currentPage() == mpname ) + { + mmode->finalPageEntered(); + } + MacroWizardBase::back(); +} + +void MacroWizard::next() +{ + if ( currentPage() == mpgiven ) + { + // currentPage() is not yet updated when we get here, so this + // means that the page about to be shown is actually mpfinal... + mmode->finalPageEntered(); + } + else if ( currentPage() == mpfinal ) + { + mmode->namePageEntered(); + } + MacroWizardBase::next(); +} + +void MacroWizard::reject() +{ + MacroWizardBase::reject(); + mmode->cancelPressed(); +} + +void MacroWizard::nameTextChanged( const QString& ) +{ + mmode->macroNameChanged(); +} + +void MacroWizard::accept() +{ + mmode->finishPressed(); +} + +void MacroWizard::slotHelpClicked() +{ + kapp->invokeHelp( QString::fromLatin1( "defining-macros"), + QString::fromLatin1( "kig" ) ); +} diff --git a/kig/modes/macrowizard.h b/kig/modes/macrowizard.h new file mode 100644 index 00000000..dfce8b06 --- /dev/null +++ b/kig/modes/macrowizard.h @@ -0,0 +1,43 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef MACROWIZARD_H +#define MACROWIZARD_H + +#include "macrowizardbase.h" + +class DefineMacroMode; + +class MacroWizard : public MacroWizardBase +{ + Q_OBJECT +public: + MacroWizard( QWidget* parent, DefineMacroMode* m ); + ~MacroWizard(); + + void back(); + void next(); + void reject(); + void accept(); +private slots: + void nameTextChanged( const QString& ); + void slotHelpClicked(); +private: + DefineMacroMode* mmode; +}; + +#endif // MACROWIZARD_H diff --git a/kig/modes/macrowizardbase.ui b/kig/modes/macrowizardbase.ui new file mode 100644 index 00000000..d3929ad8 --- /dev/null +++ b/kig/modes/macrowizardbase.ui @@ -0,0 +1,188 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>MacroWizardBase</class> +<widget class="QWizard"> + <property name="name"> + <cstring>MacroWizardBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>4</x> + <y>0</y> + <width>344</width> + <height>172</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="caption"> + <string>Define New Macro</string> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>mpgiven</cstring> + </property> + <attribute name="title"> + <string>Given Objects</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Select the "given" objects for your new macro and press "Next".</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>mpfinal</cstring> + </property> + <attribute name="title"> + <string>Final Object</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel2</cstring> + </property> + <property name="text"> + <string>Select the final object(s) for your new macro.</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>mpname</cstring> + </property> + <attribute name="title"> + <string>Name</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel1_2</cstring> + </property> + <property name="text"> + <string>Enter a name and description for your new type.</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel2_2</cstring> + </property> + <property name="text"> + <string>Name:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>KLineEdit2</cstring> + </property> + </widget> + <widget class="KLineEdit"> + <property name="name"> + <cstring>KLineEdit2</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout1</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel2_2_2</cstring> + </property> + <property name="text"> + <string>Description:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>KLineEdit1</cstring> + </property> + </widget> + <widget class="KLineEdit"> + <property name="name"> + <cstring>KLineEdit1</cstring> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> +</widget> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klineedit.h</includehint> + <includehint>klineedit.h</includehint> +</includehints> +</UI> diff --git a/kig/modes/mode.cc b/kig/modes/mode.cc new file mode 100644 index 00000000..9d95b64d --- /dev/null +++ b/kig/modes/mode.cc @@ -0,0 +1,133 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#include "mode.h" + +#include "../kig/kig_part.h" + +#include <kaction.h> + +void KigMode::enableActions() +{ + mdoc.enableConstructActions( false ); + mdoc.aSelectAll->setEnabled( false ); + mdoc.aDeselectAll->setEnabled( false ); + mdoc.aInvertSelection->setEnabled( false ); + mdoc.aCancelConstruction->setEnabled( false ); + mdoc.aConfigureTypes->setEnabled( false ); + mdoc.aDeleteObjects->setEnabled( false ); + mdoc.aShowHidden->setEnabled( false ); + mdoc.aNewMacro->setEnabled( false ); + mdoc.action( "edit_undo" )->setEnabled( false ); + mdoc.action( "edit_redo" )->setEnabled( false ); +} + +KigMode::~KigMode() +{ +} + +KigMode::KigMode( KigPart& d ) + : mdoc( d ) +{ +} + +void KigMode::leftClicked( QMouseEvent*, KigWidget* ) +{ +} + +void KigMode::leftMouseMoved( QMouseEvent*, KigWidget* ) +{ +} + +void KigMode::leftReleased( QMouseEvent*, KigWidget* ) +{ + /* insist disabling the undo button to avoid crashes */ + mdoc.action( "edit_undo" )->setEnabled( false ); + mdoc.action( "edit_redo" )->setEnabled( false ); +} + +void KigMode::midClicked( QMouseEvent*, KigWidget* ) +{ +} + +void KigMode::midMouseMoved( QMouseEvent*, KigWidget* ) +{ +} + +void KigMode::midReleased( QMouseEvent*, KigWidget* ) +{ +} + +void KigMode::rightClicked( QMouseEvent*, KigWidget* ) +{ +} + +void KigMode::rightMouseMoved( QMouseEvent*, KigWidget* ) +{ +} + +void KigMode::rightReleased( QMouseEvent*, KigWidget* ) +{ +} + +void KigMode::mouseMoved( QMouseEvent*, KigWidget* ) +{ +} + +void KigMode::cancelConstruction() +{ +} + +void KigMode::deleteObjects() +{ +} + +void KigMode::showHidden() +{ +} + +void KigMode::newMacro() +{ +} + +void KigMode::editTypes() +{ +} + +void KigMode::redrawScreen( KigWidget* ) +{ +} + +StdConstructionMode* KigMode::toStdConstructionMode() +{ + return 0; +} + +void KigMode::selectAll() +{ +} + +void KigMode::deselectAll() +{ +} + +void KigMode::invertSelection() +{ +} diff --git a/kig/modes/mode.h b/kig/modes/mode.h new file mode 100644 index 00000000..6f476072 --- /dev/null +++ b/kig/modes/mode.h @@ -0,0 +1,91 @@ +/* + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#ifndef KIG_PART_MODE_H +#define KIG_PART_MODE_H + +#include <qnamespace.h> + +class KigDocument; +class KigPart; +class KigWidget; +class QMouseEvent; +class StdConstructionMode; + +/** + * this is an ABC of a class containing the current "Mode" of the Kig + * document... It tells us how to respond to a certain event. + */ +class KigMode + : public Qt +{ +public: + virtual ~KigMode(); + + virtual StdConstructionMode* toStdConstructionMode(); + + virtual void leftClicked( QMouseEvent*, KigWidget* ); + /** + * this means: mouse moved with left mouse button down (in case that + * wasn't clear...) + */ + virtual void leftMouseMoved( QMouseEvent*, KigWidget* ); + virtual void leftReleased( QMouseEvent*, KigWidget* ); + virtual void midClicked( QMouseEvent*, KigWidget* ); + virtual void midMouseMoved( QMouseEvent*, KigWidget* ); + virtual void midReleased( QMouseEvent*, KigWidget* ); + virtual void rightClicked( QMouseEvent*, KigWidget* ); + virtual void rightMouseMoved( QMouseEvent*, KigWidget* ); + virtual void rightReleased( QMouseEvent*, KigWidget* ); + /** + * mouse moved without any buttons down... + */ + virtual void mouseMoved( QMouseEvent*, KigWidget* ); + + /** + * actions: we enable the actions we want when our mode is made + * active. These actions are members of KigDocument, and call slots + * on KigDocument. These slots all call the correspondent mode() + * member. Modes reimplement the ones they need, and turn on the + * actions they support in enableActions(). + */ + virtual void enableActions(); + + virtual void cancelConstruction(); + virtual void deleteObjects(); + virtual void showHidden(); + virtual void newMacro(); + virtual void editTypes(); + virtual void selectAll(); + virtual void deselectAll(); + virtual void invertSelection(); + + /** + * Redraw the document on KigWidget \p w . It's up to the mode to + * refresh the screen... + */ + virtual void redrawScreen( KigWidget* w ); +protected: + KigPart& mdoc; + + KigMode( KigPart& d ); +}; + +#endif diff --git a/kig/modes/moving.cc b/kig/modes/moving.cc new file mode 100644 index 00000000..e628a7ce --- /dev/null +++ b/kig/modes/moving.cc @@ -0,0 +1,245 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "moving.h" + +#include "normal.h" + +#include "../objects/object_imp.h" +#include "../objects/object_factory.h" +#include "../kig/kig_document.h" +#include "../kig/kig_part.h" +#include "../kig/kig_view.h" +#include "../kig/kig_commands.h" +#include "../misc/kigpainter.h" +#include "../misc/calcpaths.h" +#include "../misc/coordinate_system.h" + +#include <qevent.h> + +#include <functional> +#include <algorithm> +#include <map> + +void MovingModeBase::initScreen( const std::vector<ObjectCalcer*>& in ) +{ + mcalcable = in; + std::set<ObjectCalcer*> calcableset( mcalcable.begin(), mcalcable.end() ); + + // don't try to move objects that have been deleted from the + // document or internal objects that the user is not aware of.. + std::vector<ObjectHolder*> docobjs = mdoc.document().objects(); + for ( std::vector<ObjectHolder*>::iterator i = docobjs.begin(); + i != docobjs.end(); ++i ) + if ( calcableset.find( ( *i )->calcer() ) != calcableset.end() ) + mdrawable.push_back( *i ); + + std::set<ObjectHolder*> docobjsset( docobjs.begin(), docobjs.end() ); + std::set<ObjectHolder*> drawableset( mdrawable.begin(), mdrawable.end() ); + std::set<ObjectHolder*> notmovingobjs; + std::set_difference( docobjsset.begin(), docobjsset.end(), drawableset.begin(), drawableset.end(), + std::inserter( notmovingobjs, notmovingobjs.begin() ) ); + + mview.clearStillPix(); + KigPainter p( mview.screenInfo(), &mview.stillPix, mdoc.document() ); + p.drawGrid( mdoc.document().coordinateSystem(), mdoc.document().grid(), + mdoc.document().axes() ); + p.drawObjects( notmovingobjs.begin(), notmovingobjs.end(), false ); + mview.updateCurPix(); + + KigPainter p2( mview.screenInfo(), &mview.curPix, mdoc.document() ); + p2.drawObjects( drawableset.begin(), drawableset.end(), true ); +} + +void MovingModeBase::leftReleased( QMouseEvent*, KigWidget* v ) +{ + // clean up after ourselves: + for ( std::vector<ObjectCalcer*>::iterator i = mcalcable.begin(); + i != mcalcable.end(); ++i ) + ( *i )->calc( mdoc.document() ); + stopMove(); + mdoc.setModified( true ); + + // refresh the screen: + v->redrawScreen( std::vector<ObjectHolder*>() ); + v->updateScrollBars(); + + mdoc.doneMode( this ); +} + +void MovingModeBase::mouseMoved( QMouseEvent* e, KigWidget* v ) +{ + v->updateCurPix(); + Coordinate c = v->fromScreen( e->pos() ); + + bool snaptogrid = e->state() & Qt::ShiftButton; + moveTo( c, snaptogrid ); + for ( std::vector<ObjectCalcer*>::iterator i = mcalcable.begin(); + i != mcalcable.end(); ++i ) + ( *i )->calc( mdoc.document() ); + KigPainter p( v->screenInfo(), &v->curPix, mdoc.document() ); + // TODO: only draw the explicitly moving objects as selected, the + // other ones as deselected.. Needs some support from the + // subclasses.. + p.drawObjects( mdrawable, true ); + v->updateWidget( p.overlay() ); + v->updateScrollBars(); +} + +class MovingMode::Private +{ +public: + // explicitly moving objects: these are the objects that the user + // requested to move... + std::vector<ObjectCalcer*> emo; + // point where we started moving.. + Coordinate pwwsm; + MonitorDataObjects* mon; + // we keep a map from the emo objects to their reference location. + // This is the location that they claim to be at before moving + // starts, and we use it as a reference point to determine where + // they should move next.. + std::map<const ObjectCalcer*, Coordinate> refmap; +}; + +MovingMode::MovingMode( const std::vector<ObjectHolder*>& os, const Coordinate& c, + KigWidget& v, KigPart& doc ) + : MovingModeBase( doc, v ), d( new Private ) +{ + d->pwwsm = c; + std::vector<ObjectCalcer*> emo; + std::set<ObjectCalcer*> objs; + for ( std::vector<ObjectHolder*>::const_iterator i = os.begin(); i != os.end(); ++i ) + if ( (*i)->canMove() ) + { + emo.push_back( ( *i )->calcer() ); + d->refmap[( *i )->calcer()] = (*i)->moveReferencePoint(); + objs.insert( ( *i )->calcer() ); + std::vector<ObjectCalcer*> parents = ( *i )->calcer()->movableParents(); + objs.insert( parents.begin(), parents.end() ); + }; + + emo = calcPath( emo ); + for ( std::vector<ObjectCalcer*>::const_iterator i = emo.begin(); i != emo.end(); ++i ) + if ( !isChild( *i, d->emo ) ) + d->emo.push_back( *i ); + + d->mon = new MonitorDataObjects( std::vector<ObjectCalcer*>( objs.begin(),objs.end() ) ); + + std::set<ObjectCalcer*> tmp = objs; + for ( std::set<ObjectCalcer*>::const_iterator i = tmp.begin(); i != tmp.end(); ++i ) + { + std::set<ObjectCalcer*> children = getAllChildren(*i); + objs.insert( children.begin(), children.end() ); + } + + initScreen( calcPath( std::vector<ObjectCalcer*>( objs.begin(), objs.end() ) ) ); +} + +void MovingMode::stopMove() +{ + QString text = d->emo.size() == 1 ? + d->emo[0]->imp()->type()->moveAStatement() : + i18n( "Move %1 Objects" ).arg( d->emo.size() ); + KigCommand* mc = new KigCommand( mdoc, text ); + d->mon->finish( mc ); + mdoc.history()->addCommand( mc ); +} + +void MovingMode::moveTo( const Coordinate& o, bool snaptogrid ) +{ + for( std::vector<ObjectCalcer*>::iterator i = d->emo.begin(); i != d->emo.end(); ++i ) + { + assert( d->refmap.find( *i ) != d->refmap.end() ); + Coordinate nc = d->refmap[*i] + ( o - d->pwwsm ); + if ( snaptogrid ) nc = mdoc.document().coordinateSystem().snapToGrid( nc, mview ); + (*i)->move( nc, mdoc.document() ); + }; +} + +PointRedefineMode::PointRedefineMode( ObjectHolder* p, KigPart& d, KigWidget& v ) + : MovingModeBase( d, v ), mp( p ), mmon( 0 ) +{ + assert( dynamic_cast<ObjectTypeCalcer*>( p->calcer() ) ); + moldtype = static_cast<ObjectTypeCalcer*>( p->calcer() )->type(); + std::vector<ObjectCalcer*> oldparents = p->calcer()->parents(); + std::copy( oldparents.begin(), oldparents.end(), std::back_inserter( moldparents ) ); + + std::vector<ObjectCalcer*> parents = getAllParents( mp->calcer() ); + mmon = new MonitorDataObjects( parents ); + std::vector<ObjectCalcer*> moving = parents; + std::set<ObjectCalcer*> children = getAllChildren( mp->calcer() ); + std::copy( children.begin(), children.end(), std::back_inserter( moving ) ); + initScreen( moving ); +} + +void PointRedefineMode::moveTo( const Coordinate& o, bool snaptogrid ) +{ + Coordinate realo = + snaptogrid ? mdoc.document().coordinateSystem().snapToGrid( o, mview ) : o; + ObjectFactory::instance()->redefinePoint( + static_cast<ObjectTypeCalcer*>( mp->calcer() ), realo, mdoc.document(), mview ); +} + +PointRedefineMode::~PointRedefineMode() +{ +} + +MovingModeBase::MovingModeBase( KigPart& doc, KigWidget& v ) + : KigMode( doc ), mview( v ) +{ +} + +MovingModeBase::~MovingModeBase() +{ +} + +void MovingModeBase::leftMouseMoved( QMouseEvent* e, KigWidget* v ) +{ + mouseMoved( e, v ); +} + +MovingMode::~MovingMode() +{ + delete d->mon; + delete d; +} + +void PointRedefineMode::stopMove() +{ + assert( dynamic_cast<ObjectTypeCalcer*>( mp->calcer() ) ); + ObjectTypeCalcer* mpcalc = static_cast<ObjectTypeCalcer*>( mp->calcer() ); + + std::vector<ObjectCalcer*> newparents = mpcalc->parents(); + std::vector<ObjectCalcer::shared_ptr> newparentsref( + newparents.begin(), newparents.end() ); + const ObjectType* newtype = mpcalc->type(); + + std::vector<ObjectCalcer*> oldparents; + for( std::vector<ObjectCalcer::shared_ptr>::iterator i = moldparents.begin(); + i != moldparents.end(); ++i ) + oldparents.push_back( i->get() ); + mpcalc->setType( moldtype ); + mpcalc->setParents( oldparents ); + mp->calc( mdoc.document() ); + + KigCommand* command = new KigCommand( mdoc, i18n( "Redefine Point" ) ); + command->addTask( + new ChangeParentsAndTypeTask( mpcalc, newparents, newtype ) ); + mmon->finish( command ); + mdoc.history()->addCommand( command ); +} diff --git a/kig/modes/moving.h b/kig/modes/moving.h new file mode 100644 index 00000000..32828db3 --- /dev/null +++ b/kig/modes/moving.h @@ -0,0 +1,101 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef MOVING_H +#define MOVING_H + +#include "mode.h" + +#include "../misc/coordinate.h" +#include "../objects/object_calcer.h" + +class ObjectType; +class Coordinate; +class NormalPoint; +class KigWidget; +class KigDocument; +class MonitorDataObjects; + +/** + * "Template method" pattern ( see the Design patterns book ): + * This is a base class for two modes: normal MovingMode: used for + * moving a set of objects around, using Object::startMove, + * Object::moveTo and Object::stopMove, and another mode + * PointRedefineMode, used for redefining a NormalPoint... + */ +class MovingModeBase + : public KigMode +{ +protected: + KigWidget& mview; +private: + // all moving objects: these objects are all of the objects that + // need to be redrawn every time the cursor moves, and after calc is + // called. + std::vector<ObjectCalcer*> mcalcable; + std::vector<ObjectHolder*> mdrawable; +protected: + MovingModeBase( KigPart& doc, KigWidget& v ); + ~MovingModeBase(); + + /** + * Subclasses should call this in their constructor, when they know + * which objects will be moving around... They are expected to be in + * the right order for being calc()'ed... + */ + void initScreen( const std::vector<ObjectCalcer*>& amo ); + + // in these functions, subclasses should do the equivalent of + // Object::stopMove() and moveTo()... Note that no calc()'ing or + // drawing is to be done.. + virtual void stopMove() = 0; + virtual void moveTo( const Coordinate& o, bool snaptogrid ) = 0; + +public: + void leftReleased( QMouseEvent*, KigWidget* ); + void leftMouseMoved( QMouseEvent*, KigWidget* ); + void mouseMoved( QMouseEvent*, KigWidget* ); +}; + +class MovingMode + : public MovingModeBase +{ + class Private; + Private* d; + void stopMove(); + void moveTo( const Coordinate& o, bool snaptogrid ); +public: + MovingMode( const std::vector<ObjectHolder*>& objects, const Coordinate& c, + KigWidget&, KigPart& ); + ~MovingMode(); +}; + +class PointRedefineMode + : public MovingModeBase +{ + ObjectHolder* mp; + std::vector<ObjectCalcer::shared_ptr> moldparents; + const ObjectType* moldtype; + MonitorDataObjects* mmon; + void stopMove(); + void moveTo( const Coordinate& o, bool snaptogrid ); +public: + PointRedefineMode( ObjectHolder* p, KigPart& d, KigWidget& v ); + ~PointRedefineMode(); +}; + +#endif diff --git a/kig/modes/normal.cc b/kig/modes/normal.cc new file mode 100644 index 00000000..ecf5f5c1 --- /dev/null +++ b/kig/modes/normal.cc @@ -0,0 +1,306 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "normal.h" + +#include "../kig/kig_view.h" +#include "../kig/kig_part.h" +#include "../kig/kig_document.h" +#include "../kig/kig_commands.h" +#include "../objects/object_factory.h" +#include "../objects/object_imp.h" +#include "../objects/object_drawer.h" +#include "../misc/kigpainter.h" +#include "popup.h" +#include "moving.h" +#include "macro.h" +#include "dragrectmode.h" +#include "typesdialog.h" + +#include <kcursor.h> +#include <kaction.h> +#include <kcommand.h> +#include <klocale.h> + +#include <algorithm> +#include <functional> + +using namespace std; + +void NormalMode::enableActions() +{ + KigMode::enableActions(); + mdoc.enableConstructActions( true ); + mdoc.aSelectAll->setEnabled( true ); + mdoc.aDeselectAll->setEnabled( true ); + mdoc.aInvertSelection->setEnabled( true ); + mdoc.aDeleteObjects->setEnabled( true ); + mdoc.aShowHidden->setEnabled( true ); + mdoc.aNewMacro->setEnabled( true ); + mdoc.aConfigureTypes->setEnabled( true ); + mdoc.history()->updateActions(); +} + +void NormalMode::deleteObjects() +{ + std::vector<ObjectHolder*> sel( sos.begin(), sos.end() ); + mdoc.delObjects( sel ); + sos.clear(); +} + +void NormalMode::selectObject( ObjectHolder* o ) +{ + sos.insert( o ); +} + +void NormalMode::selectObjects( const std::vector<ObjectHolder*>& os ) +{ + // hehe, don't you love this c++ stuff ;) + std::for_each( os.begin(), os.end(), + std::bind1st( + std::mem_fun( &NormalMode::selectObject ), this ) ); +} + +void NormalMode::unselectObject( ObjectHolder* o ) +{ + sos.erase( o ); +} + +void NormalMode::clearSelection() +{ + sos.clear(); +} + +// KigDocumentPopup* NormalMode::popup( KigDocument* ) +// { +// return 0; +// } + +void NormalMode::showHidden() +{ + mdoc.showObjects( mdoc.document().objects() ); +} + +void NormalMode::newMacro() +{ + DefineMacroMode m( mdoc ); + mdoc.runMode( &m ); +} + +void NormalMode::redrawScreen( KigWidget* w ) +{ + // unselect removed objects.. + std::vector<ObjectHolder*> nsos; + const std::set<ObjectHolder*> docobjs = mdoc.document().objectsSet(); + std::set_intersection( docobjs.begin(), docobjs.end(), sos.begin(), sos.end(), + std::back_inserter( nsos ) ); + sos = std::set<ObjectHolder*>( nsos.begin(), nsos.end() ); + w->redrawScreen( nsos, true ); + w->updateScrollBars(); +} + +void NormalMode::editTypes() +{ + TypesDialog d( mdoc.widget(), mdoc ); + d.exec(); +} + +NormalMode::NormalMode( KigPart& d ) + : BaseMode( d ) +{ +} + +NormalMode::~NormalMode() +{ +} + +void NormalMode::dragRect( const QPoint& p, KigWidget& w ) +{ + DragRectMode d( p, mdoc, w ); + mdoc.runMode( &d ); + + KigPainter pter( w.screenInfo(), &w.stillPix, mdoc.document() ); + + if ( ! d.cancelled() ) + { + std::vector<ObjectHolder*> sel = d.ret(); + + if ( d.needClear() ) + { + pter.drawObjects( sos.begin(), sos.end(), false ); + clearSelection(); + }; + + selectObjects( sel ); + pter.drawObjects( sel, true ); + }; + + w.updateCurPix( pter.overlay() ); + w.updateWidget(); +} + +void NormalMode::dragObject( const std::vector<ObjectHolder*>& oco, const QPoint& pco, + KigWidget& w, bool ctrlOrShiftDown ) +{ + // first determine what to move... + if( sos.find( oco.front() ) == sos.end() ) + { + // the user clicked on something that is currently not + // selected... --> we select it, taking the Ctrl- and + // Shift-buttons into account... + if ( !ctrlOrShiftDown ) clearSelection(); + selectObject(oco.front()); + } + + std::vector<ObjectHolder*> sosv( sos.begin(), sos.end() ); + MovingMode m( sosv, w.fromScreen( pco ), w, mdoc ); + mdoc.runMode( &m ); +} + +void NormalMode::leftClickedObject( ObjectHolder* o, const QPoint&, + KigWidget& w, bool ctrlOrShiftDown ) +{ + KigPainter pter( w.screenInfo(), &w.stillPix, mdoc.document() ); + + if ( ! o ) + { + pter.drawObjects( sos.begin(), sos.end(), false ); + clearSelection(); + } + else if( sos.find( o ) == sos.end() ) + { + // clicked on an object that wasn't selected.... + if (!ctrlOrShiftDown) + { + pter.drawObjects( sos.begin(), sos.end(), false ); + clearSelection(); + }; + pter.drawObject( o, true ); + selectObject( o ); + } + else + { + // clicked on an object that was selected.... + pter.drawObject( o, false ); + unselectObject( o ); + }; + w.updateCurPix( pter.overlay() ); + w.updateWidget(); +} + +void NormalMode::midClicked( const QPoint& p, KigWidget& w ) +{ + ObjectHolder* pto = ObjectFactory::instance()->sensiblePoint( w.fromScreen( p ), mdoc.document(), w ); + pto->calc( mdoc.document() ); + mdoc.addObject( pto ); + + // refresh the screen... + // not necessary, done by addObjects, which calls NormalMode::redrawScreen.. +// w.redrawScreen(); +// w.updateScrollBars(); +} + +void NormalMode::rightClicked( const std::vector<ObjectHolder*>& os, + const QPoint& plc, + KigWidget& w ) +{ + // saving the current cursor position + QPoint pt = QCursor::pos(); + if( !os.empty() ) + { + ObjectHolder* o = 0; + int id = ObjectChooserPopup::getObjectFromList( pt, &w, os ); + if ( id >= 0 ) + o = os[id]; + else + return; + if( sos.find( o ) == sos.end() ) + { + clearSelection(); + selectObject( o ); + }; + // show a popup menu... + std::vector<ObjectHolder*> sosv( sos.begin(), sos.end() ); + NormalModePopupObjects p( mdoc, w, *this, sosv, plc ); + p.exec( pt ); + } + else + { + NormalModePopupObjects p( mdoc, w, *this, std::vector<ObjectHolder*>(), plc ); + p.exec( pt ); + }; +} + +void NormalMode::mouseMoved( const std::vector<ObjectHolder*>& os, + const QPoint& plc, + KigWidget& w, + bool ) +{ + w.updateCurPix(); + if( os.empty() ) + { + w.setCursor( KCursor::arrowCursor() ); + mdoc.emitStatusBarText( 0 ); + w.updateWidget(); + } + else + { + // the cursor is over an object, show object type next to cursor + // and set statusbar text + + w.setCursor( KCursor::handCursor() ); + + int id = ObjectChooserPopup::getObjectFromList( plc, &w, os, false ); + QString stat = id == 0 ? os.front()->selectStatement() : i18n( "Which object?" ); + + // statusbar text + mdoc.emitStatusBarText( stat ); + KigPainter p( w.screenInfo(), &w.curPix, mdoc.document() ); + + // set the text next to the arrow cursor + QPoint point = plc; + point.setX(point.x()+15); + + p.drawTextStd( point, stat ); + w.updateWidget( p.overlay() ); + }; +} + +void NormalMode::selectAll() +{ + const std::vector<ObjectHolder*> os = mdoc.document().objects(); + selectObjects( os ); + mdoc.redrawScreen(); +} + +void NormalMode::deselectAll() +{ + clearSelection(); + mdoc.redrawScreen(); +} + +void NormalMode::invertSelection() +{ + std::vector<ObjectHolder*> os = mdoc.document().objects(); + std::set<ObjectHolder*> oldsel = sos; + clearSelection(); + for ( std::vector<ObjectHolder*>::const_iterator i = os.begin(); + i != os.end(); ++i ) + if ( oldsel.find( *i ) == oldsel.end() ) + sos.insert( *i ); + mdoc.redrawScreen(); +} diff --git a/kig/modes/normal.h b/kig/modes/normal.h new file mode 100644 index 00000000..dd51c854 --- /dev/null +++ b/kig/modes/normal.h @@ -0,0 +1,74 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_MODES_NORMAL_H +#define KIG_MODES_NORMAL_H + +#include "base_mode.h" + +#include <qpoint.h> +#include <set> + +class NormalMode + : public BaseMode +{ +public: + NormalMode( KigPart& ); + ~NormalMode(); +protected: + void dragRect( const QPoint& p, KigWidget& w ); + void dragObject( const std::vector<ObjectHolder*>& os, const QPoint& pointClickedOn, + KigWidget& w, bool ctrlOrShiftDown ); + void leftClickedObject( ObjectHolder* o, const QPoint& p, + KigWidget& w, bool ctrlOrShiftDown ); + void midClicked( const QPoint& p, KigWidget& w ); + void rightClicked( const std::vector<ObjectHolder*>& os, const QPoint& p, KigWidget& w ); + void mouseMoved( const std::vector<ObjectHolder*>& os, const QPoint& p, KigWidget& w, + bool shiftpressed ); + void selectAll(); + void deselectAll(); + void invertSelection(); + +protected: + /** + * Objcects were added.. + */ + void redrawScreen( KigWidget* ); + + void enableActions(); + + void deleteObjects(); + void showHidden(); + void newMacro(); + void editTypes(); + +public: + void selectObject( ObjectHolder* o ); + void selectObjects( const std::vector<ObjectHolder*>& os ); + void unselectObject( ObjectHolder* o ); + void clearSelection(); + +// KigObjectsPopup* popup( const Objects& os ); +// KigDocumentPopup* popup( KigDocument* ); +protected: + /** + * selected objects... + */ + std::set<ObjectHolder*> sos; +}; + +#endif diff --git a/kig/modes/popup.cc b/kig/modes/popup.cc new file mode 100644 index 00000000..b71d9896 --- /dev/null +++ b/kig/modes/popup.cc @@ -0,0 +1,1200 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#include "popup.h" +#include "popup.moc" + +#include "../kig/kig_part.h" +#include "../kig/kig_document.h" +#include "../kig/kig_view.h" +#include "../kig/kig_commands.h" +#include "../objects/object_imp.h" +#include "../objects/object_drawer.h" +#include "../objects/bogus_imp.h" +#include "../objects/point_imp.h" +#include "../objects/line_imp.h" +#include "../objects/other_type.h" +#include "../objects/object_factory.h" +#include "../objects/polygon_imp.h" +#include "../objects/text_imp.h" +#include "../misc/lists.h" +#include "../misc/argsparser.h" +#include "../misc/kigpainter.h" +#include "../misc/coordinate_system.h" +#include "../misc/object_constructor.h" +#include "construct_mode.h" +#include "normal.h" +#include "moving.h" + +#include <algorithm> +#include <functional> + +#include <qcursor.h> +#include <qdialog.h> +#include <qpen.h> +#include <qregexp.h> +#include <qvalidator.h> + +#include <kaction.h> +#include <kcolordialog.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <klocale.h> +#if KDE_IS_VERSION( 3, 1, 90 ) +#include <kinputdialog.h> +#else +#include <klineeditdlg.h> +#endif + +#include <config.h> + +using namespace std; + +class NormalModePopupObjects; + +/** + * This class is an abstract class. Its role is to fill up the + * NormalModePopupObjects with useful actions.. + */ +class PopupActionProvider +{ +public: + virtual ~PopupActionProvider(); + /** + * add all your entries to menu menu in popup popup. Set nextfree + * to the next free index.. + */ + virtual void fillUpMenu( NormalModePopupObjects& popup, int menu, int& nextfree ) = 0; + /** + * try to execute the id'th action you added to menu menu in popup + * popup ( first is 0 ). Return true if this action does indeed + * belong to you ( is not greater than the number of actions you + * added ). Else return false, and subtract the number of actions + * you added from id. This requires you to keep a record of how + * much actions you added ( unless it's a fixed number, of course + * ). + */ + virtual bool executeAction( int menu, int& id, const std::vector<ObjectHolder*>& os, + NormalModePopupObjects& popup, + KigPart& doc, KigWidget& w, NormalMode& m ) = 0; +}; + +class BuiltinObjectActionsProvider + : public PopupActionProvider +{ +public: + void fillUpMenu( NormalModePopupObjects& popup, int menu, int& nextfree ); + bool executeAction( int menu, int& id, const std::vector<ObjectHolder*>& os, + NormalModePopupObjects& popup, + KigPart& doc, KigWidget& w, NormalMode& m ); +}; + +class NameObjectActionsProvider + : public PopupActionProvider +{ +public: + void fillUpMenu( NormalModePopupObjects& popup, int menu, int& nextfree ); + bool executeAction( int menu, int& id, const std::vector<ObjectHolder*>& os, + NormalModePopupObjects& popup, + KigPart& doc, KigWidget& w, NormalMode& m ); +}; + +class BuiltinDocumentActionsProvider + : public PopupActionProvider +{ + int mnumberofcoordsystems; + bool misfullscreen; +public: + void fillUpMenu( NormalModePopupObjects& popup, int menu, int& nextfree ); + bool executeAction( int menu, int& id, const std::vector<ObjectHolder*>& os, + NormalModePopupObjects& popup, + KigPart& doc, KigWidget& w, NormalMode& m ); +}; + +class ObjectConstructorActionsProvider + : public PopupActionProvider +{ + std::vector<ObjectConstructor*> mctors[NormalModePopupObjects::NumberOfMenus]; +public: + void fillUpMenu( NormalModePopupObjects& popup, int menu, int& nextfree ); + bool executeAction( int menu, int& id, const std::vector<ObjectHolder*>& os, + NormalModePopupObjects& popup, + KigPart& doc, KigWidget& w, NormalMode& m ); +}; + +class PropertiesActionsProvider + : public PopupActionProvider +{ + // we don't really need NumberOfMenus vectors, but this is the + // easiest way to do it, and I'm too lazy to do it properly ;) + std::vector<int> mprops[NormalModePopupObjects::NumberOfMenus]; +public: + void fillUpMenu( NormalModePopupObjects& popup, int menu, int& nextfree ); + bool executeAction( int menu, int& id, const std::vector<ObjectHolder*>& os, + NormalModePopupObjects& popup, + KigPart& doc, KigWidget& w, NormalMode& m ); +}; + +class ObjectTypeActionsProvider + : public PopupActionProvider +{ + int mnoa; +public: + void fillUpMenu( NormalModePopupObjects& popup, int menu, int& nextfree ); + bool executeAction( int menu, int& id, const std::vector<ObjectHolder*>& os, + NormalModePopupObjects& popup, + KigPart& doc, KigWidget& w, NormalMode& m ); +}; + +#ifdef KIG_ENABLE_PYTHON_SCRIPTING +#include "../scripting/script-common.h" +#include "../scripting/script_mode.h" +#include "../scripting/python_type.h" + +class ScriptActionsProvider + : public PopupActionProvider +{ + int mns; +public: + void fillUpMenu( NormalModePopupObjects& popup, int menu, int& nextfree ); + bool executeAction( int menu, int& id, const std::vector<ObjectHolder*>& os, + NormalModePopupObjects& popup, + KigPart& doc, KigWidget& w, NormalMode& m ); +}; +#endif + +NormalModePopupObjects::NormalModePopupObjects( KigPart& part, + KigWidget& view, + NormalMode& mode, + const std::vector<ObjectHolder*>& objs, + const QPoint& plc ) + : KPopupMenu( &view ), mplc( plc ), mpart( part ), mview( view ), mobjs( objs ), + mmode( mode ), monlylabels( false ) +{ + bool empty = objs.empty(); + bool single = objs.size() == 1; + connect( this, SIGNAL( activated( int ) ), this, SLOT( toplevelMenuSlot( int ) ) ); + + QString title; + if ( empty ) + title = i18n( "Kig Document" ); + else if ( single ) + { + if ( !objs[0]->name().isNull() ) + title = QString::fromLatin1( "%1 %2" ).arg( objs[0]->imp()->type()->translatedName() ).arg( objs[0]->name() ); + else + title = objs[0]->imp()->type()->translatedName(); + } + else + title = i18n( "%1 Objects" ).arg( objs.size() ); + insertTitle( title, 1 ); + + if ( !empty ) + { + monlylabels = true; + uint i = 0; + while ( i < objs.size() && monlylabels ) + { + monlylabels &= objs[i]->imp()->inherits( TextImp::stype() ); + ++i; + } + } + + if ( empty ) + { + // provides some diverse stuff like "unhide all", set coordinate + // system etc. + mproviders.push_back( new BuiltinDocumentActionsProvider() ); + }; + // construct an object using these objects and start constructing an + // object using these objects + mproviders.push_back( new ObjectConstructorActionsProvider() ); + if ( single ) + mproviders.push_back( new NameObjectActionsProvider() ); + if ( ! empty ) + { + // stuff like hide, show, delete, set size, set color.. + mproviders.push_back( new BuiltinObjectActionsProvider() ); + // show property as text label -> show menu + // and construct property's as objects -> construct menu + mproviders.push_back( new PropertiesActionsProvider() ); + // stuff like "redefine point" for a fixed or constrained point.. + mproviders.push_back( new ObjectTypeActionsProvider() ); + } +#ifdef KIG_ENABLE_PYTHON_SCRIPTING + // script action.. + mproviders.push_back( new ScriptActionsProvider() ); +#endif + + for ( uint i = 0; i < NumberOfMenus; ++i ) + mmenus[i] = new QPopupMenu( this ); + + connect( mmenus[TransformMenu], SIGNAL( activated( int ) ), + this, SLOT( transformMenuSlot( int ) ) ); + connect( mmenus[TestMenu], SIGNAL( activated( int ) ), + this, SLOT( testMenuSlot( int ) ) ); + connect( mmenus[ConstructMenu], SIGNAL( activated( int ) ), + this, SLOT( constructMenuSlot( int ) ) ); + connect( mmenus[StartMenu], SIGNAL( activated( int ) ), + this, SLOT( startMenuSlot( int ) ) ); + connect( mmenus[ShowMenu], SIGNAL( activated( int ) ), + this, SLOT( showMenuSlot( int ) ) ); + connect( mmenus[SetColorMenu], SIGNAL( activated( int ) ), + this, SLOT( setColorMenuSlot( int ) ) ); + connect( mmenus[SetSizeMenu], SIGNAL( activated( int ) ), + this, SLOT( setSizeMenuSlot( int ) ) ); + connect( mmenus[SetStyleMenu], SIGNAL( activated( int ) ), + this, SLOT( setStyleMenuSlot( int ) ) ); + connect( mmenus[SetCoordinateSystemMenu], SIGNAL( activated( int ) ), + this, SLOT( setCoordinateSystemMenuSlot( int ) ) ); + + for ( int i = 0; i <= NumberOfMenus; ++i ) + { + int nextfree = 10; + for ( uint j = 0; j < mproviders.size(); ++j ) + mproviders[j]->fillUpMenu( *this, i, nextfree ); + }; + static const QString menunames[NumberOfMenus] = + { + i18n( "&Transform" ), + i18n( "T&est" ), + i18n( "Const&ruct" ), + i18n( "&Start" ), + i18n( "Add Te&xt Label" ), + i18n( "Set Co&lor" ), + i18n( "Set &Pen Width" ), + i18n( "Set St&yle" ), + QString::null, + i18n( "Set Coordinate S&ystem" ) + }; + static const QString menuicons[NumberOfMenus] = + { + "centralsymmetry", + "test", + QString::null, + "launch", + "kig_text", + "color_fill", +// "colorize", + "sizer", + "paintbrush", + QString::null, + QString::null + }; + int index = 1; + for ( int i = 0; i < NumberOfMenus; ++i ) + { + if ( mmenus[i]->count() == 0 ) continue; + if ( menuicons[i].isNull() ) + insertItem( menunames[i], mmenus[i], i, index++ ); + else + { + KIconLoader* l = part.instance()->iconLoader(); + QPixmap icon = l->loadIcon( menuicons[i], KIcon::Small, 22, KIcon::DefaultState, 0L, true ); + insertItem( QIconSet( icon ), menunames[i], mmenus[i], i, index++ ); + } + }; +} + +void NormalModePopupObjects::testMenuSlot( int i ) +{ + activateAction( TestMenu, i ); +} + +void NormalModePopupObjects::transformMenuSlot( int i ) +{ + activateAction( TransformMenu, i ); +} + +void NormalModePopupObjects::constructMenuSlot( int i ) +{ + activateAction( ConstructMenu, i ); +} + +void NormalModePopupObjects::startMenuSlot( int i ) +{ + activateAction( StartMenu, i ); +} + +void NormalModePopupObjects::showMenuSlot( int i ) +{ + activateAction( ShowMenu, i ); +} + +void NormalModePopupObjects::toplevelMenuSlot( int i ) +{ + activateAction( ToplevelMenu, i ); +} + +void NormalModePopupObjects::activateAction( int menu, int action ) +{ + bool done = false; + // we need action - 10 cause we called fillUpMenu with nextfree set + // to 10 initially.. + action -= 10; + for ( uint i = 0; ! done && i < mproviders.size(); ++i ) + done = mproviders[i]->executeAction( menu, action, mobjs, *this, mpart, mview, mmode ); +} + +NormalModePopupObjects::~NormalModePopupObjects() +{ + delete_all ( mproviders.begin(), mproviders.end() ); +} + +static const QColor* colors[] = +{ + &Qt::blue, + &Qt::black, + &Qt::gray, + &Qt::red, + &Qt::green, + &Qt::cyan, + &Qt::yellow, + &Qt::darkRed +}; +static const int numberofcolors = sizeof( colors ) / sizeof( QColor* ); + +void BuiltinObjectActionsProvider::fillUpMenu( NormalModePopupObjects& popup, int menu, int& nextfree ) +{ + if ( menu == NormalModePopupObjects::ToplevelMenu ) + { + KIconLoader* l = popup.part().instance()->iconLoader(); + std::vector<ObjectHolder*> os = popup.objects(); + + /* + * mp: we want the "show" action to be visible only + * if we selected only one object (to be conservative) + * and if that object is currently hidden. + * conversely for one hidden object we don't want + * the "hide" action to be inserted. + * in any case we have a fixed 'id' associated + * with the two actions. + */ + + if ( os.size() > 1 || os.front()->shown() ) + { + popup.addAction( menu, i18n( "&Hide" ), nextfree ); + } + if ( os.size() == 1 && !os.front()->shown() ) + { + popup.addAction( menu, i18n( "&Show" ), nextfree+1 ); + } + nextfree += 2; + QPixmap p = l->loadIcon( "move", KIcon::Toolbar ); + popup.addAction( menu, p, i18n( "&Move" ), nextfree++ ); + p = l->loadIcon( "editdelete", KIcon::Toolbar ); + popup.addAction( menu, p, i18n( "&Delete" ), nextfree++ ); + } + else if ( menu == NormalModePopupObjects::SetColorMenu ) + { + QPixmap p( 50, 20 ); + for( const QColor** c = colors; c < colors + numberofcolors; ++c ) + { + p.fill( **c ); + popup.addAction( menu, p, nextfree++ ); + } + popup.addAction( menu, i18n( "&Custom Color" ), nextfree++ ); + } + else if ( menu == NormalModePopupObjects::SetSizeMenu && !popup.onlyLabels() ) + { + bool point = true; + bool samecolor = true; + std::vector<ObjectHolder*> os = popup.objects(); + QColor color = os.front()->drawer()->color(); + for ( std::vector<ObjectHolder*>::const_iterator i = os.begin(); i != os.end(); ++i ) + { + if ( ! (*i)->imp()->inherits( PointImp::stype() ) ) + point = false; + if ( (*i)->drawer()->color() != color ) samecolor = false; + }; + if ( ! samecolor ) color = Qt::blue; + QPixmap p( point ? 20 : 50, 20 ); + for ( int i = 1; i < 8; ++i ) + { + p.fill( popup.eraseColor() ); + QPainter ptr( &p ); + ptr.setPen( QPen( color, 1 ) ); + ptr.setBrush( QBrush( color, Qt::SolidPattern ) ); + if ( point ) + { + int size = 2*i; + QRect r( ( 20 - size ) / 2, ( 20 - size ) / 2, size, size ); + ptr.drawEllipse( r ); + } + else + { + ptr.setPen( QPen( color, -1 + 2*i ) ); + ptr.drawLine( QPoint( 0, 10 ), QPoint( 50, 10 ) ); + }; + ptr.end(); + popup.addAction( menu, p, nextfree++ ); + }; + } + else if ( menu == NormalModePopupObjects::SetStyleMenu && !popup.onlyLabels() ) + { + bool samecolor = true; + int npoints = 0; + int nothers = 0; + std::vector<ObjectHolder*> os = popup.objects(); + QColor color = os.front()->drawer()->color(); + for ( std::vector<ObjectHolder*>::const_iterator i = os.begin(); i != os.end(); ++i ) + { + if ( (*i)->imp()->inherits( PointImp::stype() ) ) + npoints++; + else + nothers++; + if ( (*i)->drawer()->color() != color ) samecolor = false; + }; + bool point = ( npoints > nothers ); + if ( ! samecolor ) color = Qt::blue; + if ( point ) + for ( int i = 0; i < 5; ++i ) + { + QPixmap p( 20, 20 ); + p.fill( popup.eraseColor() ); + ScreenInfo si( Rect( -1, -1, 2, 2 ), p.rect() ); + KigPainter ptr( si, &p, popup.part().document(), false ); + PointImp pt( Coordinate( 0, 0 ) ); + ObjectDrawer d( color, -1, true, Qt::SolidLine, i ); + d.draw( pt, ptr, false ); + popup.addAction( menu, p, nextfree++ ); + } + else + { + Qt::PenStyle penstyles[] = {Qt::SolidLine, Qt::DashLine, Qt::DashDotLine, Qt::DashDotDotLine, Qt::DotLine}; + for ( int i = 0; i < (int) ( sizeof( penstyles ) / sizeof( Qt::PenStyle ) ); ++i ) + { + QPixmap p( 50, 20 ); + p.fill( popup.eraseColor() ); + ScreenInfo si( Rect( -2.5, -1, 5, 2 ), p.rect() ); + KigPainter ptr( si, &p, popup.part().document(), false ); + LineImp line( Coordinate( -1, 0 ), Coordinate( 1, 0 ) ); + Qt::PenStyle ps = penstyles[i]; + ObjectDrawer d( color, -1, true, ps, 1 ); + d.draw( line, ptr, false ); + popup.addAction( menu, p, nextfree++ ); + }; + } + } +} + +void NameObjectActionsProvider::fillUpMenu( NormalModePopupObjects& popup, int menu, int& nextfree ) +{ + if ( menu == NormalModePopupObjects::ToplevelMenu ) + { + popup.addAction( menu, i18n( "Set &Name..." ), nextfree++ ); + } + else if ( menu == NormalModePopupObjects::ShowMenu ) + { + popup.addAction( menu, i18n( "&Name" ), nextfree++ ); + } +} + +static void addNameLabel( ObjectCalcer* object, ObjectCalcer* namecalcer, const Coordinate& loc, KigPart& doc ) +{ + std::vector<ObjectCalcer*> args; + args.push_back( namecalcer ); + const bool namelabelneedsframe = false; + ObjectCalcer* attachto = 0; + if ( object->imp()->inherits( PointImp::stype() ) || + object->imp()->attachPoint().valid() || + object->imp()->inherits( CurveImp::stype() ) ) + attachto = object; + ObjectHolder* label = ObjectFactory::instance()->attachedLabel( + QString::fromLatin1( "%1" ), attachto, loc, namelabelneedsframe, args, doc.document() ); + doc.addObject( label ); +} + +bool NameObjectActionsProvider::executeAction( + int menu, int& id, const std::vector<ObjectHolder*>& os, NormalModePopupObjects& popup, + KigPart& doc, KigWidget& w, NormalMode& ) +{ + if ( menu == NormalModePopupObjects::ToplevelMenu ) + { + if ( id >= 1 ) + { + id -= 1; + return false; + } + assert( os.size() == 1 ); + QString name = os[0]->name(); + bool ok; + QRegExp re( ".*" ); + QRegExpValidator* rev = new QRegExpValidator( re, &doc ); + QString caption = i18n( "Set Object Name" ); + QString label = i18n( "Set Name of this Object:" ); +#if KDE_IS_VERSION( 3, 1, 90 ) + name = KInputDialog::getText( caption, label, name, &ok, &w, 0, rev ); +#else + name = KLineEditDlg::getText( caption, label, name, &ok, &w, rev ); +#endif + if ( ok ) + { + bool justadded = false; + ObjectCalcer* namecalcer = os[0]->nameCalcer(); + if ( !namecalcer ) + { + justadded = true; + ObjectConstCalcer* c = new ObjectConstCalcer( new StringImp( i18n( "<unnamed object>" ) ) ); + os[0]->setNameCalcer( c ); + namecalcer = c; + } + assert( dynamic_cast<ObjectConstCalcer*>( namecalcer ) ); + ObjectConstCalcer* cnamecalcer = static_cast<ObjectConstCalcer*>( os[0]->nameCalcer() ); + MonitorDataObjects mon( cnamecalcer ); + cnamecalcer->setImp( new StringImp( name ) ); + KigCommand* kc = new KigCommand( doc, i18n( "Set Object Name" ) ); + mon.finish( kc ); + doc.history()->addCommand( kc ); + + // if we just added the name, we add a label to show it to the user. + if ( justadded ) + addNameLabel( os[0]->calcer(), namecalcer, +// w.fromScreen( w.mapFromGlobal( popup.mapToGlobal( QPoint( 5, 0 ) ) ) ), + w.fromScreen( popup.plc() ), + doc ); + } + return true; + } + else if ( menu == NormalModePopupObjects::ShowMenu ) + { + if ( id >= 1 ) + { + id -= 1; + return false; + } + assert( os.size() == 1 ); + ObjectCalcer* namecalcer = os[0]->nameCalcer(); + if ( !namecalcer ) + { + ObjectConstCalcer* c = new ObjectConstCalcer( new StringImp( i18n( "<unnamed object>" ) ) ); + os[0]->setNameCalcer( c ); + namecalcer = c; + } + addNameLabel( os[0]->calcer(), namecalcer, +// w.fromScreen( w.mapFromGlobal( popup.mapToGlobal( QPoint( 5, 0 ) ) ) ), doc ); + w.fromScreen( popup.plc() ), doc ); + return true; + } + else + { + return false; + } +} + +bool BuiltinObjectActionsProvider::executeAction( + int menu, int& id, const std::vector<ObjectHolder*>& os, NormalModePopupObjects& popup, + KigPart& doc, KigWidget& w, NormalMode& mode ) +{ + if ( menu == NormalModePopupObjects::ToplevelMenu ) + { + if ( id > 3 ) + { + id -= 4; + return false; + }; + switch( id ) + { + case 0: + // hide the objects.. + doc.hideObjects( os ); + break; + case 1: + // show the objects.. + doc.showObjects( os ); + break; + case 2: + { + // move + QCursor::setPos( popup.mapToGlobal( QPoint( 0, 0 ) ) ); + QPoint p = w.mapFromGlobal( QCursor::pos() ); + Coordinate c = w.fromScreen( p ); + MovingMode m( os, c, w, doc ); + doc.runMode( &m ); + // in this case, we return, cause we don't want objects to be + // unselected... ( or maybe we do ? ) + return true; + } + case 3: + // delete + doc.delObjects( os ); + break; + default: assert( false ); + }; + mode.clearSelection(); + return true; + } + else if ( menu == NormalModePopupObjects::SetColorMenu ) + { + if ( id >= numberofcolors + 1 ) + { + id -= numberofcolors + 1; + return false; + }; + QColor color; + if ( id < numberofcolors ) + color = *colors[id]; + else + { + if ( os.size() == 1 ) + color = os.front()->drawer()->color(); + int result = KColorDialog::getColor( color, &w ); + if ( result != KColorDialog::Accepted ) return true; + } + KigCommand* kc = new KigCommand( doc, i18n( "Change Object Color" ) ); + assert( color.isValid() ); + for ( std::vector<ObjectHolder*>::const_iterator i = os.begin(); i != os.end(); ++i ) + kc->addTask( new ChangeObjectDrawerTask( *i, ( *i )->drawer()->getCopyColor( color ) ) ); + doc.history()->addCommand( kc ); + mode.clearSelection(); + return true; + } + else if ( menu == NormalModePopupObjects::SetSizeMenu ) + { + if ( id >= 7 ) + { + id -= 7; + return false; + }; + + KigCommand* kc = new KigCommand( doc, i18n( "Change Object Width" ) ); + for ( std::vector<ObjectHolder*>::const_iterator i = os.begin(); i != os.end(); ++i ) + kc->addTask( new ChangeObjectDrawerTask( *i, ( *i )->drawer()->getCopyWidth( 1 + 2 * id ) ) ); + doc.history()->addCommand( kc ); + mode.clearSelection(); + return true; + } + else if ( menu == NormalModePopupObjects::SetStyleMenu ) + { + int npoints = 0; + int nothers = 0; + for ( std::vector<ObjectHolder*>::const_iterator i = os.begin(); i != os.end(); ++i ) + { + if ( (*i)->imp()->inherits( PointImp::stype() ) ) + npoints++; + else + nothers++; + }; + bool point = ( npoints > nothers ); + int max = point ? 5 : 5; + if ( id >= max ) + { + id -= max; + return false; + }; + + if ( point ) + { + KigCommand* kc = new KigCommand( doc, i18n( "Change Point Style" ) ); + for ( std::vector<ObjectHolder*>::const_iterator i = os.begin(); i != os.end(); ++i ) + if ( (*i)->imp()->inherits( PointImp::stype() ) ) + kc->addTask( new ChangeObjectDrawerTask( *i, ( *i )->drawer()->getCopyPointStyle( id ) ) ); + doc.history()->addCommand( kc ); + mode.clearSelection(); + return true; + } + else + { + Qt::PenStyle penstyles[] = {Qt::SolidLine, Qt::DashLine, Qt::DashDotLine, Qt::DashDotDotLine, Qt::DotLine}; + assert( id < (int)( sizeof( penstyles ) / sizeof( Qt::PenStyle ) ) ); + Qt::PenStyle p = penstyles[id]; + KigCommand* kc = new KigCommand( doc, i18n( "Change Object Style" ) ); + for ( std::vector<ObjectHolder*>::const_iterator i = os.begin(); i != os.end(); ++i ) + if ( ! (*i)->imp()->inherits( PointImp::stype() ) ) + kc->addTask( new ChangeObjectDrawerTask( *i, ( *i )->drawer()->getCopyStyle( p ) ) ); + doc.history()->addCommand( kc ); + mode.clearSelection(); + } + return true; + } + else return false; +} + +void ObjectConstructorActionsProvider::fillUpMenu( NormalModePopupObjects& popup, int menu, int& nextfree ) +{ + const KigDocument& d = popup.part().document(); + const KigWidget& v = popup.widget(); + typedef ObjectConstructorList::vectype vectype; + vectype vec = ObjectConstructorList::instance()->constructors(); + + for ( vectype::iterator i = vec.begin(); i != vec.end(); ++i ) + { + bool add = false; + if ( popup.objects().empty() ) + { + add = menu == NormalModePopupObjects::StartMenu && ! (*i)->isTransform() && ! (*i)->isTest(); + } + else + { + int ret = (*i)->wantArgs( getCalcers( popup.objects() ), d, v ); + if ( ret == ArgsParser::Invalid ) continue; + if ( (*i)->isTransform() && popup.objects().size() == 1 ) add = menu == NormalModePopupObjects::TransformMenu; + else if ( (*i)->isTest() ) add = menu == NormalModePopupObjects::TestMenu; + else if ( ( *i )->isIntersection() ) add = menu == NormalModePopupObjects::ToplevelMenu; + else if ( ret == ArgsParser::Complete ) add = menu == NormalModePopupObjects::ConstructMenu; + else add = menu == NormalModePopupObjects::StartMenu; + }; + if ( add ) + { + QCString iconfile = (*i)->iconFileName(); + if ( !iconfile.isEmpty() && !iconfile.isNull() ) + { + QPixmap icon = popup.part().instance()->iconLoader()->loadIcon( iconfile, KIcon::Toolbar, 22, KIcon::DefaultState, 0L, true ); + popup.addAction( menu, icon, (*i)->descriptiveName(), nextfree++ ); + } + else + popup.addAction( menu, (*i)->descriptiveName(), nextfree++ ); + mctors[menu].push_back( *i ); + } + }; +} + +bool ObjectConstructorActionsProvider::executeAction( + int menu, int& id, const std::vector<ObjectHolder*>& os, + NormalModePopupObjects&, + KigPart& doc, KigWidget& w, NormalMode& m ) +{ + if ( (uint) id >= mctors[menu].size() ) + { + id -= mctors[menu].size(); + return false; + } + + ObjectConstructor* ctor = mctors[menu][id]; + std::vector<ObjectCalcer*> osc = getCalcers( os ); + if ( ! os.empty() && ctor->wantArgs( osc, doc.document(), w ) == ArgsParser::Complete ) + { + ctor->handleArgs( osc, doc, w ); + m.clearSelection(); + } + else + { + BaseConstructMode* mode = ctor->constructMode( doc ); + mode->selectObjects( os, w ); + doc.runMode( mode ); + delete mode; + }; + return true; +} + +void NormalModePopupObjects::addAction( int menu, const QPixmap& pix, int id ) +{ + QPopupMenu* m = 0; + if ( menu == ToplevelMenu ) m = this; + else m = mmenus[menu]; + int ret = m->insertItem( pix, id ); + assert( ret == id ); + // pretend to use this var.. + (void) ret; +} + +void NormalModePopupObjects::setColorMenuSlot( int i ) +{ + activateAction( SetColorMenu, i ); +} + +void NormalModePopupObjects::setSizeMenuSlot( int i ) +{ + activateAction( SetSizeMenu, i ); +} + +void NormalModePopupObjects::setStyleMenuSlot( int i ) +{ + activateAction( SetStyleMenu, i ); +} + +void NormalModePopupObjects::setCoordinateSystemMenuSlot( int i ) +{ + activateAction( SetCoordinateSystemMenu, i ); +} + +void NormalModePopupObjects::addAction( int menu, const QPixmap& icon, const QString& name, int id ) +{ + QPopupMenu* m = 0; + if ( menu == ToplevelMenu ) m = this; + else m = mmenus[menu]; + int ret = m->insertItem( QIconSet( icon ), name, id ); + assert( ret == id ); + // pretend to use this var.. + (void)ret; +} + +void NormalModePopupObjects::addAction( int menu, const QString& name, int id ) +{ + QPopupMenu* m = 0; + if ( menu == ToplevelMenu ) m = this; + else m = mmenus[menu]; + int ret = m->insertItem( name, id ); + assert( ret == id ); + // pretend to use this var.. + (void)ret; +} + +PopupActionProvider::~PopupActionProvider() +{ +} + +void PropertiesActionsProvider::fillUpMenu( NormalModePopupObjects& popup, + int menu, int& nextfree ) +{ + if ( popup.objects().size() != 1 ) return; + ObjectHolder* o = popup.objects()[0]; + uint np = o->imp()->numberOfProperties(); + if ( menu != NormalModePopupObjects::ConstructMenu && + menu != NormalModePopupObjects::ShowMenu ) return; + for ( uint i = 0; i < np; ++i ) + { + ObjectImp* prop = o->imp()->property( i, popup.part().document() ); + const char* iconfile = o->imp()->iconForProperty( i ); + bool add = true; + if ( menu == NormalModePopupObjects::ConstructMenu ) + { + // we don't want imp's like DoubleImp, since we can't show them + // anyway.. + add &= ! prop->inherits( BogusImp::stype() ); + // we don't want to construct PointImp's coordinate property, + // since it would construct a point at the same place as its + // parent.. + add &= ! ( o->imp()->inherits( PointImp::stype() ) && + prop->inherits( PointImp::stype() ) ); + } + else if ( menu == NormalModePopupObjects::ShowMenu ) + add &= prop->canFillInNextEscape(); + if ( add ) + { + if ( iconfile && *iconfile ) + { + QPixmap pix = popup.part().instance()->iconLoader()->loadIcon( iconfile, KIcon::Toolbar, 22, KIcon::DefaultState, 0L, true ); + popup.addAction( menu, pix, i18n( o->imp()->properties()[i] ), nextfree++ ); + } + else + { + popup.addAction( menu, i18n( o->imp()->properties()[i] ), nextfree++ ); + }; + mprops[menu-1].push_back( i ); + }; + delete prop; + }; +} + +bool PropertiesActionsProvider::executeAction( + int menu, int& id, const std::vector<ObjectHolder*>& os, + NormalModePopupObjects& popup, + KigPart& doc, KigWidget& w, NormalMode& ) +{ + if ( menu != NormalModePopupObjects::ConstructMenu && + menu != NormalModePopupObjects::ShowMenu ) + return false; + if ( (uint) id >= mprops[menu - 1].size() ) + { + id -= mprops[menu - 1].size(); + return false; + } + int propid = mprops[menu-1][id]; + assert( os.size() == 1 ); + ObjectHolder* parent = os[0]; + if ( menu == NormalModePopupObjects::ShowMenu ) + { + std::vector<ObjectCalcer*> args; + args.push_back( new ObjectPropertyCalcer( parent->calcer(), propid ) ); + args.back()->calc( doc.document() ); +// TODO: recover the cursor position somehow... the following does not work +// in general... +// Coordinate c = w.fromScreen( w.mapFromGlobal( popup.mapToGlobal( QPoint( 5, 0 ) ) ) ); +// mp: it seems that we have no idea where to position the label, +// btw what's the meaning of (5,0)? let the +// attach method decide what to do... (passing an invalidCoord) +// /////// Coordinate c = Coordinate::invalidCoord(); + Coordinate c = w.fromScreen( popup.plc() ); + ObjectHolder* label = ObjectFactory::instance()->attachedLabel( + QString::fromLatin1( "%1" ), parent->calcer(), c, + false, args, doc.document() ); + doc.addObject( label ); + } + else + { + ObjectHolder* h = new ObjectHolder( + new ObjectPropertyCalcer( parent->calcer(), propid ) ); + h->calc( doc.document() ); + doc.addObject( h ); + }; + return true; +} + +void ObjectTypeActionsProvider::fillUpMenu( + NormalModePopupObjects& popup, int menu, int& nextfree ) +{ + if ( popup.objects().size() != 1 ) return; + if ( menu != NormalModePopupObjects::ToplevelMenu ) return; + ObjectHolder* to = popup.objects()[0]; + ObjectTypeCalcer* c = dynamic_cast<ObjectTypeCalcer*>( to->calcer() ); + if ( ! c ) return; + const ObjectType* t = c->type(); + + QStringList l = t->specialActions(); + mnoa = l.count(); + for ( int i = 0; i < mnoa; ++i ) + popup.addAction( menu, l[i], nextfree++ ); +} + +bool ObjectTypeActionsProvider::executeAction( + int menu, int& id, const std::vector<ObjectHolder*>& os, + NormalModePopupObjects&, + KigPart& doc, KigWidget& w, NormalMode& m ) +{ + if ( menu != NormalModePopupObjects::ToplevelMenu ) return false; + if ( id >= mnoa ) + { + id -= mnoa; + return false; + } + assert( os.size() == 1 ); + ObjectTypeCalcer* oc = dynamic_cast<ObjectTypeCalcer*>( os[0]->calcer() ); + assert( oc ); + + oc->type()->executeAction( id, *os[0], *oc, doc, w, m ); + return true; +} + +void BuiltinDocumentActionsProvider::fillUpMenu( NormalModePopupObjects& popup, int menu, int& nextfree ) +{ + if ( menu == NormalModePopupObjects::ToplevelMenu ) + { + popup.addAction( menu, i18n( "U&nhide All" ), nextfree++ ); + popup.part().action( "view_zoom_in" )->plug( &popup ); + popup.part().action( "view_zoom_out" )->plug( &popup ); + popup.part().action( "fullscreen" )->plug( &popup ); + nextfree += 3; + } + else if ( menu == NormalModePopupObjects::SetCoordinateSystemMenu ) + { + int idoffset = nextfree; + QStringList l = CoordinateSystemFactory::names(); + mnumberofcoordsystems = l.count(); + for ( uint i = 0; i < l.count(); ++i ) + popup.addAction( menu, l[i], nextfree++ ); + int current = popup.part().document().coordinateSystem().id(); + popup.setChecked( menu, idoffset + current, true ); + } +} + +bool BuiltinDocumentActionsProvider::executeAction( + int menu, int& id, const std::vector<ObjectHolder*>&, + NormalModePopupObjects&, + KigPart& doc, KigWidget&, NormalMode& m ) +{ + if ( menu == NormalModePopupObjects::ToplevelMenu ) + { + kdDebug() << "id: " << id << endl; + if ( id == 0 ) + { + doc.showHidden(); + m.clearSelection(); + return true; + } + id -= 1; + return false; + } + else if ( menu == NormalModePopupObjects::SetCoordinateSystemMenu ) + { + if ( id >= mnumberofcoordsystems ) + { + id -= mnumberofcoordsystems; + return false; + }; + CoordinateSystem* sys = CoordinateSystemFactory::build( id ); + assert( sys ); + doc.history()->addCommand( KigCommand::changeCoordSystemCommand( doc, sys ) ); + m.clearSelection(); + return true; + } + else return false; +} + +void NormalModePopupObjects::setChecked( int menu, int n, bool checked ) +{ + mmenus[menu]->setItemChecked( n, checked ); +} + +#ifdef KIG_ENABLE_PYTHON_SCRIPTING +/** + * this is a local function that looks for a python script associated + * to a clicked object + */ +static ObjectTypeCalcer* getPythonExecuteTypeFromCalcer( ObjectCalcer* o ) +{ + ObjectTypeCalcer* oc = dynamic_cast<ObjectTypeCalcer *>( o ); + if ( !oc ) return 0; + const PythonExecuteType* pythonexec = dynamic_cast<const PythonExecuteType*>( oc->type() ); + if ( pythonexec ) return oc; + + return 0; +} + +void ScriptActionsProvider::fillUpMenu( NormalModePopupObjects& popup, int menu, int& nextfree ) +{ + if ( menu == NormalModePopupObjects::StartMenu ) + { + KIconLoader* l = popup.part().instance()->iconLoader(); + QPixmap p = l->loadIcon( ScriptType::icon( ScriptType::Python ), KIcon::Toolbar, 22, KIcon::DefaultState, 0L, true ); + popup.addAction( menu, p, i18n( "Python Script" ), nextfree++ ); + mns++; + } + else if ( menu == NormalModePopupObjects::ToplevelMenu ) + { + if ( !popup.objects().empty() && + getPythonExecuteTypeFromCalcer( popup.objects().front()->calcer() ) ) + { + popup.addAction( menu, i18n( "Edit Script..." ), nextfree ); + } + nextfree++; + } +} + +bool ScriptActionsProvider::executeAction( + int menu, int& id, const std::vector<ObjectHolder*>& os, + NormalModePopupObjects&, KigPart& doc, KigWidget& w, NormalMode& mode ) +{ + if ( menu == NormalModePopupObjects::StartMenu ) + { + if ( id == 0 ) + { + ScriptCreationMode m( doc ); + m.setScriptType( ScriptType::Python ); + if ( os.size() > 0 ) + { + mode.clearSelection(); + m.addArgs( os, w ); + m.goToCodePage(); + } + doc.runMode( &m ); + return true; + } + else + { + id -= mns; + } + } + else if ( menu == NormalModePopupObjects::ToplevelMenu ) + { + if ( id == 0 ) + { + ObjectTypeCalcer* oc = getPythonExecuteTypeFromCalcer( os.front()->calcer() ); + if ( oc ) + { + ScriptEditMode m( oc, doc ); + m.setScriptType( ScriptType::Python ); + doc.runMode( &m ); + } + return true; + } + else + { + id -= 1; + } + } + + return false; +} +#endif + +int ObjectChooserPopup::getObjectFromList( const QPoint& p, KigWidget* w, + const std::vector<ObjectHolder*>& objs, + bool givepopup ) +{ + int size = objs.size(); + + // no objects + if ( size == 0 ) + return -1; + + int id = -1; + + int numpoints = 0; + int numpolygons = 0; + int numothers = 0; + + for ( std::vector<ObjectHolder*>::const_iterator i = objs.begin(); + i != objs.end(); ++i ) + { + if ( (*i)->imp()->inherits( PointImp::stype() ) ) numpoints++; + else if ( (*i)->imp()->inherits( PolygonImp::stype() ) ) numpolygons++; + else numothers++; + } + + // simply cases: + // - only 1 point ( and eventually other objects ) + // - no points and an object which is not a polygon + // - only one object + // FIXME: we assume that our objects are sorted ( points, others, polygons )! + if ( ( numpoints == 1 ) || + ( ( numpoints == 0 ) && ( numothers == 1 ) ) || + ( size == 1 ) ) + id = 0; + else + { + if ( givepopup ) + { + ObjectChooserPopup* ppp = new ObjectChooserPopup( p, *w, objs ); + ppp->exec( QCursor::pos() ); + + id = ppp->mselected; + + delete ppp; + ppp = 0; + } + else + { + // we don't want to show a popup to the user, so let's give a + // value > 0 to indicate that it's not the first + id = 1; + } + } +// kdDebug() << "numpoints: " << numpoints << endl +// << "numothers: " << numothers << endl +// << "numpolygons: " << numpolygons << endl +// << "id: " << id << endl; + + return id; +} + +ObjectChooserPopup::ObjectChooserPopup( const QPoint& p, KigWidget& view, + const std::vector<ObjectHolder*>& objs ) + : KPopupMenu(), mplc( p ), mview( view ), mobjs( objs ), mselected( -1 ) +{ + for ( uint i = 0; i < mobjs.size(); i++ ) + { + insertItem( !mobjs[i]->name().isEmpty() + ? QString::fromLatin1( "%1 %2" ).arg( mobjs[i]->imp()->type()->translatedName() ).arg( mobjs[i]->name() ) + : mobjs[i]->imp()->type()->translatedName(), + i ); + } + + connect( this, SIGNAL( activated( int ) ), this, SLOT( actionActivatedSlot( int ) ) ); +} + +ObjectChooserPopup::~ObjectChooserPopup() +{ +} + +void ObjectChooserPopup::actionActivatedSlot( int which ) +{ + mselected = which; +} diff --git a/kig/modes/popup.h b/kig/modes/popup.h new file mode 100644 index 00000000..405bcee6 --- /dev/null +++ b/kig/modes/popup.h @@ -0,0 +1,153 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#ifndef KIG_MODES_POPUP_H +#define KIG_MODES_POPUP_H + +#include <kpopupmenu.h> + +#include <vector> + +class KigDocument; +class KigPart; +class KigWidget; +class NormalMode; +class PopupActionProvider; +class ObjectHolder; + +/** + * This is the popup menu that appears when you click on selected + * objects in NormalMode.. It's quite complex, since it has to fetch + * a lot of information from various places, and dispatch it again + * when the user selects something. + * Update: I'm also using it for when you clicked on an empty space in + * the document, because the difference between the two cases is not + * that important, and this class is generic enough to handle both + * cases.. When this is the case, mobjs is empty, some + * PopupActionProviders are disabled, and some others enabled.. + */ +class NormalModePopupObjects + : public KPopupMenu +{ + Q_OBJECT + +public: + NormalModePopupObjects( KigPart& part, KigWidget& view, + NormalMode& mode, + const std::vector<ObjectHolder*>& objs, const QPoint& p ); + ~NormalModePopupObjects(); + + // the different "menu's", the toplevel is considered as just + // another menu.. + enum { TransformMenu = 0, TestMenu, ConstructMenu, StartMenu, ShowMenu, + SetColorMenu, SetSizeMenu, SetStyleMenu, ToplevelMenu, + SetCoordinateSystemMenu, NumberOfMenus }; + + // used by the PopupActionProvider's to add actions to us.. + void addAction( int menu, const QString& name, int id ); + void addAction( int menu, const QPixmap& icon, const QString& name, int id ); + void addAction( int menu, const QPixmap& pix, int id ); + + /** + * set the checked state of the \p n 'th item in \p menu to \p checked .. + */ + void setChecked( int menu, int n, bool checked ); + + std::vector<ObjectHolder*> objects() const { return mobjs; } + KigPart& part() { return mpart; } + KigWidget& widget() { return mview; } + QPoint plc() { return mplc; } + + bool onlyLabels() const { return monlylabels; } + +protected: + void activateAction( int menu, int action ); + +private slots: + void transformMenuSlot( int ); + void testMenuSlot( int ); + void constructMenuSlot( int ); + void startMenuSlot( int ); + void showMenuSlot( int ); + void setColorMenuSlot( int ); + void setSizeMenuSlot( int ); + void setStyleMenuSlot( int ); + void toplevelMenuSlot( int ); + void setCoordinateSystemMenuSlot( int ); + +protected: + QPoint mplc; + KigPart& mpart; + KigWidget& mview; + std::vector<ObjectHolder*> mobjs; + NormalMode& mmode; + + std::vector<PopupActionProvider*> mproviders; + + QPopupMenu* mmenus[NumberOfMenus]; + +private: + bool monlylabels; +}; + +/** + * This class is useful to choose one object from a list of some, by + * querying the user via popup menu. + * + * You can't use this class directly, but these's a convenience method. + */ +class ObjectChooserPopup + : public KPopupMenu +{ + Q_OBJECT + +public: + /** + * Get the index of the choosen object from a list of objects. + * + * \param p is the point whene executre the popup + * \param w is the pointer to a KigWidget + * \param objs is the vector with the objects to chose from + * \param givepopup true means that we have to show a popup to the user + * + * \return the index of the chosen element ( starting from 0 ), or -1 + * if none was selected. + */ + static int getObjectFromList( const QPoint& p, KigWidget* w, + const std::vector<ObjectHolder*>& objs, + bool givepopup = true ); + +protected: + ObjectChooserPopup( const QPoint& p, KigWidget& view, + const std::vector<ObjectHolder*>& objs ); + ~ObjectChooserPopup(); + +protected slots: + void actionActivatedSlot( int ); + +protected: + QPoint mplc; + KigWidget& mview; + std::vector<ObjectHolder*> mobjs; + + int mselected; +}; + +#endif diff --git a/kig/modes/textlabelwizard.cc b/kig/modes/textlabelwizard.cc new file mode 100644 index 00000000..803ca819 --- /dev/null +++ b/kig/modes/textlabelwizard.cc @@ -0,0 +1,95 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "textlabelwizard.h" +#include "textlabelwizard.moc" + +#include "label.h" +#include "linkslabel.h" + +#include <qtextedit.h> +#include <kapplication.h> + +#include <qlayout.h> + +TextLabelWizard::TextLabelWizard( QWidget* parent, TextLabelModeBase* mode ) + : TextLabelWizardBase( parent, "TextLabelWizard", false ), mmode( mode ) +{ + connect( labelTextInput, SIGNAL( textChanged() ), + SLOT( textChanged() ) ); + connect( myCustomWidget1, SIGNAL( linkClicked( int ) ), + SLOT( linkClicked( int ) ) ); + connect( this, SIGNAL( helpClicked() ), + this, SLOT( slotHelpClicked() ) ); + labelTextInput->setFocus(); +} + +TextLabelWizard::~TextLabelWizard() +{ +} + +void TextLabelWizard::back() +{ + if ( currentPage() == select_arguments_page ) + { + mmode->enterTextPageEntered(); + } + TextLabelWizardBase::back(); +} + +void TextLabelWizard::next() +{ + if ( currentPage() == enter_text_page ) + { + mmode->selectArgumentsPageEntered(); + } + TextLabelWizardBase::next(); +} + +void TextLabelWizard::reject() +{ + TextLabelWizardBase::reject(); + mmode->cancelPressed(); +} + +void TextLabelWizard::accept() +{ + mmode->finishPressed(); +} + +void TextLabelWizard::textChanged() +{ + mmode->labelTextChanged(); +} + +void TextLabelWizard::linkClicked( int which ) +{ + mmode->linkClicked( which ); +} + +void TextLabelWizard::relayoutArgsPage() +{ + select_arguments_pageLayout->activate(); + repaint(); +} + +void TextLabelWizard::slotHelpClicked() +{ + kapp->invokeHelp( QString::fromLatin1( "text-labels" ), + QString::fromLatin1( "kig" ) ); +} + diff --git a/kig/modes/textlabelwizard.h b/kig/modes/textlabelwizard.h new file mode 100644 index 00000000..787e0803 --- /dev/null +++ b/kig/modes/textlabelwizard.h @@ -0,0 +1,46 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_MODES_TEXTLABELWIZARD_H +#define KIG_MODES_TEXTLABELWIZARD_H + +#include "textlabelwizardbase.h" + +class TextLabelModeBase; + +class TextLabelWizard : public TextLabelWizardBase +{ + Q_OBJECT +public: + TextLabelWizard( QWidget* parent, TextLabelModeBase* mode ); + ~TextLabelWizard(); + + void back(); + void next(); + void reject(); + void accept(); + + void relayoutArgsPage(); +private slots: + void textChanged(); + void linkClicked( int which ); + void slotHelpClicked(); +private: + TextLabelModeBase* mmode; +}; + +#endif // TEXTLABELWIZARD_H diff --git a/kig/modes/textlabelwizardbase.ui b/kig/modes/textlabelwizardbase.ui new file mode 100644 index 00000000..4e195b0d --- /dev/null +++ b/kig/modes/textlabelwizardbase.ui @@ -0,0 +1,113 @@ +<!DOCTYPE UI><UI version="3.1.2" stdsetdef="1"> +<class>TextLabelWizardBase</class> +<widget class="QWizard"> + <property name="name"> + <cstring>TextLabelWizardBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>488</width> + <height>300</height> + </rect> + </property> + <property name="caption"> + <string>Construct Label</string> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>enter_text_page</cstring> + </property> + <attribute name="title"> + <string>Enter Label Text</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>enterTextLabel</cstring> + </property> + <property name="text"> + <string>Enter the text for your label here and press "Next". +If you want to show variable parts, then put %1, %2, ... at the appropriate places (e.g. "This segment is %1 units long.").</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + <widget class="QTextEdit"> + <property name="name"> + <cstring>labelTextInput</cstring> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>needFrameCheckBox</cstring> + </property> + <property name="text"> + <string>Show text in a frame</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>select_arguments_page</cstring> + </property> + <attribute name="title"> + <string>Select Arguments</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>selectArgsLabel</cstring> + </property> + <property name="text"> + <string>Now select the argument(s) you need. For every argument, click on it, select an object and a property in the Kig window, and click finish when you are done...</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + <widget class="LinksLabel"> + <property name="name"> + <cstring>myCustomWidget1</cstring> + </property> + </widget> + </vbox> + </widget> +</widget> +<customwidgets> + <customwidget> + <class>LinksLabel</class> + <header location="local">linkslabel.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>5</hordata> + <verdata>5</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="XPM.GZ" length="4462">789c9d97c76e24490e86effd1442f3d65870d2451a0ce6206f5adeb4cc620f8c34f2553225b5a4c1befb46927fe6a1d4c0ccac4287fa8a0c26834193f5dbb785b3fd9d856fbf7d799ec9ecba5ea8afe469e15bf3727ffffeeffffcf1e797af49b2d0ffc7d142f2f55f5fbe1ecc16ea85dde9a4ed81290045faa77ca49cf4ab67ba1e3953969173651ab9d4fdf1c8a27c3872ad7c3c72d3b32c2a67c3f3444636fbef23eb7e590267e68fcc4656395d8dacf6d938ef97eaef289781cddebdb2846fccff44b989ba58e3411f3dc75158e6df1d3889539517ca49bf54fe43398d1dec4f46567f685fd9c539f43fc025f80c1c3ce8d93f28e77185f8bf0c6cfae4c0b5f9c3c6e5c0542a4b5876fe13708df39d2bd7716372aa8c93c4e4723bb2f977aadc26ceec4bdb7310e6b0bfab9c2445ecd49f1370694cebca65d260ffa6711a41aef14d24e94cee6be3348e0ae5e9c8765f07ca3e8db17f0f9c825794eb3489351fe9bb7217e4969f29388bd51ee9fda5715a19730696b852fea95c6471647ca95cf64bcf43e0cef4657b647bfe6acf213d6bb32f9a9f5992b5a64f9a8f990b6cf9ecc15d6cf9acf6b2da55a8a75cb973dee4b2d1b38b5c05de02434e87e01aacfb351df5bebddea74b5c67f9c795711ea15e357e2ecde358fb87efc0a867df8cac728a074e62e527706afb59f3cf6583be5c28bb3c35f693812d1fbdc6dbe57966fec932d899ffa4f7e38adca17e34beaeca4b9c271a18f1d5fc739257b0d70d9c68be7bed7fcee7827ab91cd8e4740cf6b19d4ffb87ab07b9bf516e72d4a3ac821b9cef6e60d84f47367ded2fae1d9f7704f6163f79000ffde27660f387ed3c5dbf54dfeebb0bf6acbe6b706bfa5ef32f8f8b18f5bf0f463f20edb77956a4e68fbc80b3c4facd163847be6bfde72ec86dbeac82715fbc0286be683cf3bc081d44f7df80d344fb0b6b7de64581fb916765297263d6fccd9b7ee97e566e8b06cfdb1f59cf2bda2f8bbc2c11df0c5c41aefdb328ca22b1feb0012eedbce24736f91b18fb796d64eb87cb60817dcdafb07d906bfd14d22f659d8785ef97b2f6d7b2df6ef1d6fb2babaac6f92e8c25b27ecc1f239b7f3a5fca5a8678ea7c2d9bc0e6ef1238b3fca2c7814d5f4cbf9334b5f833384bed3c7a5f5514f4adbe2ec07962e7590457f06f6f649b977afe2a16f42f5e070bfc591e18f7a5f1a812a9e0df39d827da4fbd8c6ccfd7785569bf945f953371a9d5a3de67950bfa2d75c63eb2fb15bdcfaaf011e6c7263846bf8f4636f91618f3c7d3c0f047fb69550efab2074e12bd6fd27957553ec33c5b043bcc7f9d0795f818fdb501a3df8ae673e507ff69021eea37063bc45ffb73550736ff36c0a84f3e0317a86f8b5f139e6ffe1f821de6d9127898ff3be00a7c3ab2cd03e3d697560ff40016e47b3bb2e96bbd559d8f32bbff4b7065f193042c98873acf240af6adbf3c83c5f2954bb0c7bcd77c107d81d2fdebc63e33ff640aaecc9e1c8151df5c803dfaadc64b52dfa03ed64636ffb4ff4b2867f453ede7227586fcd3f9264ded91fffafe236d5da37fe83c91ceb7b9e5b7f6731ff92ed7f747d6fbf7e185cf58347e3e6932e47b3430fa893edf87d795c2fc3f003b67f5f902cec17abfde0dfa3c053b3c5ffb832fc2eb8fddcf233887fc195c801f46b6f3cdc02558fba72f7d2d1a7f7e321ee58fe0cad86bbff24dd3e2fe74fef836b0c6eb60d62fa6bf5e07b3419f853dd7dc70fb8bd5f1255fd90ed30f9f3c5ff30ddff21ddff384a7fcc08ffcc4cf61cdf8855ff9e79c7e1db4dff89d3f7891977899577895d7789d377893b7f83b6fcfe937bc13b477798ff7f9800ff928ac633ee11f7cca6761d7f99c7e1b3cb908da11c7413be1943376e153cc39175c72f549ff9e17c31744429e6a6aa8a58e2ee98aaee9866e7f617fc24b7417a4f734a1293dd0233dd133cd8285177aa5f9f3b63ca5377aa78f607b919668995682e62aadd17ab0b1419b9ff41f682b48bed336edd02eed05ed7d5ea3033a0cdf1ed1f127fd273ae123fa41a774a6b685cee982228a837e42e927fd47caf8985c38651eb40b2ac38e4a5842658a97fab33fd248cb87d2c9a55cc9b5dcc82d1fc99ddccb44a6f230af2f8ff224cf417f262ff22a3fe54ddee543166549966545567f617f4dd66523dc6b2c9bb225df655b7682f692ecca9eeccfe97772c09b722847722c27c1f3eb70f66bf9116c9fca999ccbc59cfe25bf4a143a5ef8992561324a78bd92522acf9ebc78efe7cf7b153276db37bef59dbff457fedadff85b7f27abfede4ffcd4cf9ff76faeff4fffefeff8c7f5fedfdfbffc0fa355c495</data> + </image> +</images> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>linkslabel.h</includehint> +</includehints> +</UI> diff --git a/kig/modes/typesdialog.cpp b/kig/modes/typesdialog.cpp new file mode 100644 index 00000000..67621c20 --- /dev/null +++ b/kig/modes/typesdialog.cpp @@ -0,0 +1,282 @@ +/** + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +**/ + +#include "typesdialog.h" +#include "typesdialog.moc" + +#include "edittype.h" +#include "../kig/kig_part.h" +#include "../misc/guiaction.h" +#include "../misc/object_constructor.h" + +#include <kapplication.h> +#include <kdebug.h> +#include <kfiledialog.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kpushbutton.h> +#include <kstdguiitem.h> + +#include <qfile.h> +#include <qpixmap.h> +#include <qstringlist.h> +#include <qtextstream.h> + +#include <algorithm> +#include <vector> + +class MacroListElement + : public QListViewItem +{ + Macro* macro; +public: + MacroListElement( KListView* lv, Macro* m ); + Macro* getMacro() const { return macro; } +}; + +MacroListElement::MacroListElement( KListView* lv, Macro* m ) + : QListViewItem( lv, QString::null, m->action->descriptiveName(), m->action->description() ), + macro( m ) +{ +} + +TypesDialog::TypesDialog( QWidget* parent, KigPart& part ) + : TypesDialogBase( parent, "types_dialog", true ), mpart( part ) +{ + // improving GUI look'n'feel... + buttonHelp->setGuiItem( KStdGuiItem::help() ); + buttonOk->setGuiItem( KStdGuiItem::ok() ); + buttonCancel->setGuiItem( KStdGuiItem::cancel() ); + il = part.instance()->iconLoader(); + buttonEdit->setIconSet( QIconSet( il->loadIcon( "edit", KIcon::Small ) ) ); + buttonRemove->setIconSet( QIconSet( il->loadIcon( "editdelete", KIcon::Small ) ) ); + buttonExport->setIconSet( QIconSet( il->loadIcon( "fileexport", KIcon::Small ) ) ); + buttonImport->setIconSet( QIconSet( il->loadIcon( "fileimport", KIcon::Small ) ) ); + + typeList->setColumnWidth( 0, 22 ); + typeList->setColumnWidth( 1, 140 ); + typeList->setColumnWidth( 2, 240 ); + + // loading macros... + loadAllMacros(); + + popup = new QPopupMenu( this ); + popup->insertItem( SmallIcon( "edit" ), i18n( "&Edit..." ), this, SLOT( editType() ) ); + popup->insertItem( SmallIcon( "editdelete" ), i18n( "&Delete" ), this, SLOT( deleteType() ) ); + popup->insertSeparator(); + popup->insertItem( SmallIcon( "fileexport" ), i18n( "E&xport..." ), this, SLOT( exportType() ) ); + + // saving types + part.saveTypes(); +} + +QListViewItem* TypesDialog::newListItem( Macro* m ) +{ + MacroListElement* e = new MacroListElement( typeList, m ); + QCString ifn = m->action->iconFileName(); + if ( !ifn.isNull() ) + { + QPixmap p = il->loadIcon( ifn, KIcon::Small ); + e->setPixmap( 0, p ); + } + return e; +} + +TypesDialog::~TypesDialog() +{ +} + +void TypesDialog::helpSlot() +{ + kapp->invokeHelp( QString::fromLatin1( "working-with-types" ), + QString::fromLatin1( "kig" ) ); +} + +void TypesDialog::okSlot() +{ + mpart.saveTypes(); + mpart.deleteTypes(); + mpart.loadTypes(); + accept(); +} + +void TypesDialog::deleteType() +{ + std::vector<QListViewItem*> items; + std::vector<Macro*> selectedTypes; + QListViewItemIterator it( typeList ); + while ( it.current() ) { + if ( ( it.current() )->isSelected() ) + { + items.push_back( it.current() ); + selectedTypes.push_back( static_cast<MacroListElement*>( it.current() )->getMacro() ); + } + ++it; + } + if (selectedTypes.empty()) return; + QStringList types; + for ( std::vector<Macro*>::iterator j = selectedTypes.begin(); + j != selectedTypes.end(); ++j ) + types << ( *j )->action->descriptiveName(); + if ( KMessageBox::warningContinueCancelList( this, + i18n( "Are you sure you want to delete this type?", + "Are you sure you want to delete these %n types?", selectedTypes.size() ), + types, i18n("Are You Sure?"), KStdGuiItem::cont(), + "deleteTypeWarning") == KMessageBox::Cancel ) + return; + for ( std::vector<QListViewItem*>::iterator i = items.begin(); i != items.end(); ++i) + { + int appel = typeList->itemIndex(*i); + assert (appel != -1); + delete *i; + }; + for ( std::vector<Macro*>::iterator j = selectedTypes.begin(); + j != selectedTypes.end(); ++j) + MacroList::instance()->remove( *j ); +} + +void TypesDialog::exportType() +{ + std::vector<Macro*> types; + QListViewItemIterator it( typeList ); + while ( it.current() ) { + if ( ( it.current() )->isSelected() ) + { + types.push_back( static_cast<MacroListElement*>( it.current() )->getMacro() ); + } + ++it; + } + if (types.empty()) return; + QString file_name = KFileDialog::getSaveFileName(":macro", i18n("*.kigt|Kig Types Files\n*|All Files"), this, i18n( "Export Types" ) ); + if ( file_name.isNull() ) + return; + QFile fi( file_name ); + if ( fi.exists() ) + if ( KMessageBox::warningContinueCancel( this, i18n( "The file \"%1\" already exists. " + "Do you wish to overwrite it?" ).arg( fi.name() ), + i18n( "Overwrite File?" ), i18n("Overwrite") ) == KMessageBox::Cancel ) + return; + MacroList::instance()->save( types, file_name ); +} + +void TypesDialog::importTypes() +{ + QStringList file_names = + KFileDialog::getOpenFileNames(":importTypes", i18n("*.kigt|Kig Types Files\n*|All Files"), this, i18n( "Import Types" )); + + std::vector<Macro*> macros; + + for ( QStringList::Iterator i = file_names.begin(); + i != file_names.end(); ++i) + { + std::vector<Macro*> nmacros; + bool ok = MacroList::instance()->load( *i, nmacros, mpart ); + if ( ! ok ) + continue; + std::copy( nmacros.begin(), nmacros.end(), std::back_inserter( macros ) ); + }; + MacroList::instance()->add( macros ); + + for ( uint i = 0; i < macros.size(); ++i ) + typeList->insertItem( newListItem( macros[i] ) ); +} + +QString TypesDialog::fetchIconFromListItem( QListViewItem* i ) +{ + QListViewItemIterator it( typeList ); + Macro* ai = static_cast<MacroListElement*>( i )->getMacro(); + while ( it.current() ) { + if ( ( it.current() )->isSelected() ) + { + Macro* ait = static_cast<MacroListElement*>( it.current() )->getMacro(); + if ( ai == ait ) + { + return ai->ctor->iconFileName( true ); + } + } + ++it; + } + return "gear"; +} + +void TypesDialog::editType() +{ + std::vector<QListViewItem*> items; + QListViewItemIterator it( typeList ); + while ( it.current() ) { + if ( ( it.current() )->isSelected() ) + items.push_back( it.current() ); + ++it; + } + if ( items.size() == 0 ) + return; + if ( items.size() > 1 ) + { + KMessageBox::sorry( this, + i18n( "There is more than one type selected. You can " + "only edit one type at a time. Please select " + "only the type you want to edit and try again." ), + i18n( "More Than One Type Selected" ) ); + return; + } + QListViewItem* i = items[0]; + EditType* d = new EditType( this, i->text( 1 ), i->text( 2 ), fetchIconFromListItem( i ) ); + if ( d->exec() ) + { + QString newname = d->name(); + QString newdesc = d->description(); + QString newicon = d->icon(); + + Macro* oldmacro = static_cast<MacroListElement*>( i )->getMacro(); +// mpart.unplugActionLists(); + oldmacro->ctor->setName( newname ); + oldmacro->ctor->setDescription( newdesc ); + QCString ncicon( newicon.utf8() ); + oldmacro->ctor->setIcon( ncicon ); +// mpart.plugActionLists(); + + typeList->clear(); + + loadAllMacros(); + } + delete d; +} + +void TypesDialog::contextMenuRequested( QListViewItem*, const QPoint& p, int ) +{ + popup->exec( p ); +} + +void TypesDialog::loadAllMacros() +{ + const vec& macros = MacroList::instance()->macros(); + for ( vec::const_reverse_iterator i = macros.rbegin(); i != macros.rend(); ++i ) + { + typeList->insertItem( newListItem( *i ) ); + } +} + +void TypesDialog::cancelSlot() +{ + mpart.deleteTypes(); + mpart.loadTypes(); + reject(); +} diff --git a/kig/modes/typesdialog.h b/kig/modes/typesdialog.h new file mode 100644 index 00000000..6f0e4702 --- /dev/null +++ b/kig/modes/typesdialog.h @@ -0,0 +1,70 @@ +/* + This file is part of Kig, a KDE program for Interactive Geometry... + Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#ifndef KIG_MODES_TYPESDIALOG_H +#define KIG_MODES_TYPESDIALOG_H + +#include "typesdialogbase.h" + +#include "../misc/lists.h" + +#include <qpopupmenu.h> + +#include <klistview.h> +#include <kiconloader.h> + +class KigPart; +class KigDocument; + +/** + * Manage the macro types... + */ +class TypesDialog : public TypesDialogBase +{ + Q_OBJECT + + // necessary because some MacroList functions need it.. + KigPart& mpart; + const KIconLoader* il; + QPopupMenu* popup; +public: + TypesDialog( QWidget* parent, KigPart& ); + ~TypesDialog(); + +public slots: + void helpSlot(); + void okSlot(); + void cancelSlot(); + +protected slots: + void deleteType(); + void exportType(); + void importTypes(); + void editType(); + void contextMenuRequested( QListViewItem* i, const QPoint& p, int c ); + +private: + QListViewItem* newListItem( Macro* m ); + QString fetchIconFromListItem( QListViewItem* i ); + void loadAllMacros(); + typedef MacroList::vectype vec; +}; + +#endif diff --git a/kig/modes/typesdialogbase.ui b/kig/modes/typesdialogbase.ui new file mode 100644 index 00000000..7cc7effb --- /dev/null +++ b/kig/modes/typesdialogbase.ui @@ -0,0 +1,337 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>TypesDialogBase</class> +<widget class="QDialog"> + <property name="name"> + <cstring>TypesDialogBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>456</width> + <height>249</height> + </rect> + </property> + <property name="caption"> + <string>Manage Types</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Here you can manage types; you can remove them, and load and save them from and to files...</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout1</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KListView"> + <column> + <property name="text"> + <string>Icon</string> + </property> + <property name="clickable"> + <bool>false</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Name</string> + </property> + <property name="clickable"> + <bool>false</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Description</string> + </property> + <property name="clickable"> + <bool>false</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>typeList</cstring> + </property> + <property name="selectionMode" stdset="0"> + <enum>Extended</enum> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Select types here...</string> + </property> + <property name="whatsThis" stdset="0"> + <string>This is a list of the current macro types... You can select, edit, delete, export and import them...</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KPushButton"> + <property name="name"> + <cstring>buttonEdit</cstring> + </property> + <property name="text"> + <string>Edit...</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Edit the selected type.</string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>buttonRemove</cstring> + </property> + <property name="text"> + <string>Delete</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Delete all the selected types in the list.</string> + </property> + </widget> + </vbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KPushButton"> + <property name="name"> + <cstring>buttonExport</cstring> + </property> + <property name="text"> + <string>Export...</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Export all the selected types to a file.</string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>buttonImport</cstring> + </property> + <property name="text"> + <string>Import...</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Import macros that are contained in one or more files.</string> + </property> + </widget> + </vbox> + </widget> + </hbox> + </widget> + <widget class="Line"> + <property name="name"> + <cstring>Line1</cstring> + </property> + <property name="frameShape"> + <enum>HLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout1</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="KPushButton"> + <property name="name"> + <cstring>buttonHelp</cstring> + </property> + <property name="text"> + <string>&Help</string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>Horizontal Spacing2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KPushButton"> + <property name="name"> + <cstring>buttonOk</cstring> + </property> + <property name="text"> + <string>&OK</string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>buttonCancel</cstring> + </property> + <property name="text"> + <string>&Cancel</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<connections> + <connection> + <sender>buttonExport</sender> + <signal>clicked()</signal> + <receiver>TypesDialogBase</receiver> + <slot>exportType()</slot> + </connection> + <connection> + <sender>buttonHelp</sender> + <signal>clicked()</signal> + <receiver>TypesDialogBase</receiver> + <slot>helpSlot()</slot> + </connection> + <connection> + <sender>buttonImport</sender> + <signal>clicked()</signal> + <receiver>TypesDialogBase</receiver> + <slot>importTypes()</slot> + </connection> + <connection> + <sender>buttonOk</sender> + <signal>clicked()</signal> + <receiver>TypesDialogBase</receiver> + <slot>okSlot()</slot> + </connection> + <connection> + <sender>buttonRemove</sender> + <signal>clicked()</signal> + <receiver>TypesDialogBase</receiver> + <slot>deleteType()</slot> + </connection> + <connection> + <sender>buttonEdit</sender> + <signal>clicked()</signal> + <receiver>TypesDialogBase</receiver> + <slot>editType()</slot> + </connection> + <connection> + <sender>typeList</sender> + <signal>contextMenuRequested(QListViewItem*,const QPoint&,int)</signal> + <receiver>TypesDialogBase</receiver> + <slot>contextMenuRequested(QListViewItem*,const QPoint&,int)</slot> + </connection> + <connection> + <sender>buttonCancel</sender> + <signal>clicked()</signal> + <receiver>TypesDialogBase</receiver> + <slot>cancelSlot()</slot> + </connection> +</connections> +<slots> + <slot access="protected">deleteType()</slot> + <slot access="protected">exportType()</slot> + <slot>helpSlot()</slot> + <slot access="protected">importTypes()</slot> + <slot>okSlot()</slot> + <slot access="protected">editType()</slot> + <slot access="protected">contextMenuRequested( QListViewItem* i, const QPoint& p, int c )</slot> + <slot>cancelSlot()</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klistview.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/kig/objects/Makefile.am b/kig/objects/Makefile.am new file mode 100644 index 00000000..21a02c6f --- /dev/null +++ b/kig/objects/Makefile.am @@ -0,0 +1,81 @@ +INCLUDES=$(all_includes) +noinst_LTLIBRARIES=libobjects.la +noinst_HEADERS=\ + angle_type.h \ + arc_type.h \ + base_type.h \ + bogus_imp.h \ + circle_imp.h \ + circle_type.h \ + polygon_type.h \ + common.h \ + conic_imp.h \ + conic_types.h \ + cubic_imp.h \ + cubic_type.h \ + curve_imp.h \ + intersection_types.h \ + inversion_type.h \ + line_imp.h \ + line_type.h \ + locus_imp.h \ + object_calcer.h \ + object_drawer.h \ + object_factory.h \ + object_holder.h \ + object_imp.h \ + object_imp_factory.h \ + object_type.h \ + object_type_factory.h \ + other_imp.h \ + other_type.h \ + point_imp.h \ + polygon_imp.h \ + tangent_type.h \ + centerofcurvature_type.h \ + tests_type.h \ + text_imp.h \ + text_type.h \ + transform_types.h \ + vector_type.h +libobjects_la_SOURCES=\ + angle_type.cc \ + arc_type.cc \ + base_type.cc \ + bogus_imp.cc \ + circle_imp.cc \ + circle_type.cc \ + polygon_type.cc \ + common.cc \ + conic_imp.cc \ + conic_types.cc \ + cubic_imp.cc \ + cubic_type.cc \ + curve_imp.cc \ + intersection_types.cc \ + inversion_type.cc \ + line_imp.cc \ + line_type.cc \ + locus_imp.cc \ + object_calcer.cc \ + object_drawer.cc \ + object_factory.cc \ + object_holder.cc \ + object_imp.cc \ + object_imp_factory.cc \ + object_type.cc \ + object_type_factory.cc \ + other_imp.cc \ + other_type.cc \ + point_imp.cc \ + point_type.cc \ + polygon_imp.cc \ + tangent_type.cc \ + centerofcurvature_type.cc \ + tests_type.cc \ + text_imp.cc \ + text_type.cc \ + transform_types.cc \ + vector_type.cc +libobjects_la_LIBADD=-lm +METASOURCES=AUTO diff --git a/kig/objects/angle_type.cc b/kig/objects/angle_type.cc new file mode 100644 index 00000000..89a17131 --- /dev/null +++ b/kig/objects/angle_type.cc @@ -0,0 +1,208 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "angle_type.h" + +#include "bogus_imp.h" +#include "other_imp.h" +#include "point_imp.h" +#include "../misc/calcpaths.h" +#include "../misc/common.h" +#include "../misc/goniometry.h" +#include "../misc/kiginputdialog.h" +#include "../kig/kig_commands.h" +#include "../kig/kig_part.h" +#include "../kig/kig_view.h" + +#include <functional> +#include <algorithm> +#include <cmath> + +#include <qstringlist.h> + +static const char* constructanglethroughpoint = + I18N_NOOP( "Construct an angle through this point" ); + +static const ArgsParser::spec argsspecAngle[] = +{ + { PointImp::stype(), constructanglethroughpoint, + I18N_NOOP( "Select a point that the first half-line of the angle should go through..." ), true }, + { PointImp::stype(), I18N_NOOP( "Construct an angle at this point" ), + I18N_NOOP( "Select the point to construct the angle in..." ), true }, + { PointImp::stype(), constructanglethroughpoint, + I18N_NOOP( "Select a point that the second half-line of the angle should go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( AngleType ) + +AngleType::AngleType() + : ArgsParserObjectType( "Angle", argsspecAngle, 3 ) +{ +} + +AngleType::~AngleType() +{ +} + +const AngleType* AngleType::instance() +{ + static const AngleType t; + return &t; +} + +ObjectImp* AngleType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents, 2 ) ) return new InvalidImp; + + std::vector<Coordinate> points; + for ( uint i = 0; i < parents.size(); ++i ) + points.push_back( + static_cast<const PointImp*>( parents[i] )->coordinate() ); + + Coordinate lvect = points[0] - points[1]; + Coordinate rvect; + if ( points.size() == 3 ) + rvect = points[2] - points[1]; + else + { + rvect = lvect.orthogonal(); + } + + double startangle = atan2( lvect.y, lvect.x ); + double endangle = atan2( rvect.y, rvect.x ); + double anglelength = endangle - startangle; + if ( anglelength < 0 ) anglelength += 2* M_PI; + if ( startangle < 0 ) startangle += 2*M_PI; + + return new AngleImp( points[1], startangle, anglelength ); +} + +const ObjectImpType* AngleType::resultId() const +{ + return AngleImp::stype(); +} + +QStringList AngleType::specialActions() const +{ + QStringList ret; + ret << i18n( "Set Si&ze" ); + return ret; +} + +void AngleType::executeAction( + int i, ObjectHolder&, ObjectTypeCalcer& t, + KigPart& d, KigWidget& w, NormalMode& ) const +{ + assert( i == 0 ); + // pretend to use this var.. + (void) i; + + std::vector<ObjectCalcer*> parents = t.parents(); + + assert( margsparser.checkArgs( parents ) ); + + Coordinate a = static_cast<const PointImp*>( parents[0]->imp() )->coordinate(); + Coordinate b = static_cast<const PointImp*>( parents[1]->imp() )->coordinate(); + Coordinate c = static_cast<const PointImp*>( parents[2]->imp() )->coordinate(); + + Coordinate lvect = a - b; + Coordinate rvect = c - b; + + double startangle = atan2( lvect.y, lvect.x ); + double endangle = atan2( rvect.y, rvect.x ); + double anglelength = endangle - startangle; + if ( anglelength < 0 ) anglelength += 2* M_PI; + if ( startangle < 0 ) startangle += 2*M_PI; + + Goniometry go( anglelength, Goniometry::Rad ); + go.convertTo( Goniometry::Deg ); + + bool ok; + Goniometry newsize = KigInputDialog::getAngle( &w, &ok, go ); + if ( !ok ) + return; + newsize.convertTo( Goniometry::Rad ); + + double newcangle = startangle + newsize.value(); + Coordinate cdir( cos( newcangle ), sin( newcangle ) ); + Coordinate nc = b + cdir.normalize( rvect.length() ); + + MonitorDataObjects mon( getAllParents( parents ) ); + parents[2]->move( nc, d.document() ); + KigCommand* kc = new KigCommand( d, i18n( "Resize Angle" ) ); + mon.finish( kc ); + d.history()->addCommand( kc ); +} + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( HalfAngleType ) + +HalfAngleType::HalfAngleType() + : ArgsParserObjectType( "HalfAngle", argsspecAngle, 3 ) +{ +} + +HalfAngleType::~HalfAngleType() +{ +} + +const HalfAngleType* HalfAngleType::instance() +{ + static const HalfAngleType t; + return &t; +} + +ObjectImp* HalfAngleType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents, 2 ) ) return new InvalidImp; + + std::vector<Coordinate> points; + for ( uint i = 0; i < parents.size(); ++i ) + points.push_back( + static_cast<const PointImp*>( parents[i] )->coordinate() ); + + Coordinate lvect = points[0] - points[1]; + Coordinate rvect; + if ( points.size() == 3 ) + rvect = points[2] - points[1]; + else + { + rvect = lvect.orthogonal(); + } + + double startangle = atan2( lvect.y, lvect.x ); + double endangle = atan2( rvect.y, rvect.x ); + double anglelength = endangle - startangle; + if ( anglelength < 0 ) anglelength += 2 * M_PI; + if ( startangle < 0 ) startangle += 2 * M_PI; + + if ( anglelength > M_PI ) + { + startangle += anglelength; + anglelength = 2 * M_PI - anglelength; + if ( startangle > 2 * M_PI ) startangle -= 2 * M_PI; + if ( anglelength < 0 ) anglelength += 2 * M_PI; + } + + return new AngleImp( points[1], startangle, anglelength ); +} + +const ObjectImpType* HalfAngleType::resultId() const +{ + return AngleImp::stype(); +} + diff --git a/kig/objects/angle_type.h b/kig/objects/angle_type.h new file mode 100644 index 00000000..796143b4 --- /dev/null +++ b/kig/objects/angle_type.h @@ -0,0 +1,50 @@ +// Copyright (C) 2003-2004 Dominique Devriese <devriese@kde.org> +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_MISC_ANGLE_TYPE_H +#define KIG_MISC_ANGLE_TYPE_H + +#include "base_type.h" + +class AngleType + : public ArgsParserObjectType +{ + AngleType(); + ~AngleType(); +public: + static const AngleType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + QStringList specialActions() const; + void executeAction( int i, ObjectHolder& o, ObjectTypeCalcer& c, + KigPart& d, KigWidget& w, NormalMode& m ) const; +}; + +class HalfAngleType + : public ArgsParserObjectType +{ + HalfAngleType(); + ~HalfAngleType(); +public: + static const HalfAngleType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/arc_type.cc b/kig/objects/arc_type.cc new file mode 100644 index 00000000..f6c660f2 --- /dev/null +++ b/kig/objects/arc_type.cc @@ -0,0 +1,199 @@ +// Copyright (C) 2003-2004 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "arc_type.h" + +#include "bogus_imp.h" +#include "other_imp.h" +#include "point_imp.h" +#include "line_imp.h" +#include "locus_imp.h" + +#include "../misc/common.h" +#include "../misc/calcpaths.h" +#include "../misc/goniometry.h" +#include "../kig/kig_part.h" +#include "../kig/kig_view.h" +#include "../kig/kig_commands.h" + +#include <functional> +#include <algorithm> +#include <cmath> + +using std::find; + +#include <qstringlist.h> + +static const char constructarcstartingstat[] = I18N_NOOP( "Construct an arc starting at this point" ); + +static const ArgsParser::spec argsspecArcBTP[] = +{ + { PointImp::stype(), constructarcstartingstat, + I18N_NOOP( "Select the start point of the new arc..." ), true }, + { PointImp::stype(), I18N_NOOP( "Construct an arc through this point" ), + I18N_NOOP( "Select a point for the new arc to go through..." ), true }, + { PointImp::stype(), I18N_NOOP( "Construct an arc ending at this point" ), + I18N_NOOP( "Select the end point of the new arc..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ArcBTPType ) + +ArcBTPType::ArcBTPType() + : ArgsParserObjectType( "ArcBTP", argsspecArcBTP, 3 ) +{ +} + +ArcBTPType::~ArcBTPType() +{ +} + +const ArcBTPType* ArcBTPType::instance() +{ + static const ArcBTPType t; + return &t; +} + +ObjectImp* ArcBTPType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args, 2 ) ) + return new InvalidImp; + + const Coordinate a = + static_cast<const PointImp*>( args[0] )->coordinate(); + const Coordinate b = + static_cast<const PointImp*>( args[1] )->coordinate(); + Coordinate center; + double angle = 0.; + double startangle = 0.; + if ( args.size() == 3 ) + { + Coordinate c = static_cast<const PointImp*>( args[2] )->coordinate(); + center = calcCenter( a, b, c ); + if ( ! center.valid() ) return new InvalidImp; + Coordinate ad = a - center; + Coordinate bd = b - center; + Coordinate cd = c - center; + double anglea = atan2( ad.y, ad.x ); + double angleb = atan2( bd.y, bd.x ); + double anglec = atan2( cd.y, cd.x ); + + // anglea should be smaller than anglec + if ( anglea > anglec ) + { + double t = anglea; + anglea = anglec; + anglec = t; + }; + if ( angleb > anglec || angleb < anglea ) + { + startangle = anglec; + angle = 2 * M_PI + anglea - startangle; + } + else + { + startangle = anglea; + angle = anglec - anglea; + }; + } + else + { + // find a center and angles that look natural.. + center = (b+a)/2 + .6*(b-a).orthogonal(); + Coordinate bd = b - center; + Coordinate ad = a - center; + startangle = atan2( ad.y, ad.x ); + double halfangle = atan2( bd.y, bd.x ) - startangle; + if ( halfangle < - M_PI ) halfangle += 2*M_PI; + angle = 2 * halfangle; + }; + + double radius = ( a - center ).length(); + return new ArcImp( center, radius, startangle, angle ); +} + +const ObjectImpType* ArcBTPType::impRequirement( const ObjectImp*, const Args& ) const +{ + return PointImp::stype(); +} + +bool ArcBTPType::inherits( int type ) const +{ + return Parent::inherits( type ); +} + +const ObjectImpType* ArcBTPType::resultId() const +{ + return ArcImp::stype(); +} + +static const ArgsParser::spec argsspecArcBCPA[] = +{ + { PointImp::stype(), I18N_NOOP( "Construct an arc with this center" ), + I18N_NOOP( "Select the center of the new arc..." ), true }, + { PointImp::stype(), constructarcstartingstat, + I18N_NOOP( "Select the start point of the new arc..." ), true }, + { AngleImp::stype(), I18N_NOOP( "Construct an arc with this angle" ), + I18N_NOOP( "Select the angle of the new arc..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ArcBCPAType ) + +ArcBCPAType::ArcBCPAType() + : ArgsParserObjectType( "ArcBCPA", argsspecArcBCPA, 3 ) +{ +} + +ArcBCPAType::~ArcBCPAType() +{ +} + +const ArcBCPAType* ArcBCPAType::instance() +{ + static const ArcBCPAType t; + return &t; +} + +ObjectImp* ArcBCPAType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) + return new InvalidImp; + + const Coordinate center = static_cast<const PointImp*>( args[0] )->coordinate(); + const Coordinate p = static_cast<const PointImp*>( args[1] )->coordinate(); + const AngleImp* a = static_cast<const AngleImp*>( args[2] ); + const double angle = a->angle(); + const Coordinate dir = p - center; + const double startangle = atan2( dir.y, dir.x ); + const double radius = center.distance( p ); + + return new ArcImp( center, radius, startangle, angle ); +} + +const ObjectImpType* ArcBCPAType::impRequirement( const ObjectImp*, const Args& ) const +{ + return PointImp::stype(); +} + +bool ArcBCPAType::inherits( int type ) const +{ + return Parent::inherits( type ); +} + +const ObjectImpType* ArcBCPAType::resultId() const +{ + return ArcImp::stype(); +} diff --git a/kig/objects/arc_type.h b/kig/objects/arc_type.h new file mode 100644 index 00000000..cdfe0294 --- /dev/null +++ b/kig/objects/arc_type.h @@ -0,0 +1,64 @@ +// Copyright (C) 2003-2004 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_ARC_TYPE_H +#define KIG_OBJECTS_ARC_TYPE_H + +#include "base_type.h" +#include "../misc/object_hierarchy.h" + +/** + * an arc by a start point, an intermediate point and an end point + */ +class ArcBTPType + : public ArgsParserObjectType +{ + typedef ArgsParserObjectType Parent; + ArcBTPType(); + ~ArcBTPType(); +public: + static const ArcBTPType* instance(); + + ObjectImp* calc( const Args& args, const KigDocument& ) const; + + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + + bool inherits( int type ) const; + const ObjectImpType* resultId() const; +}; + +/** + * an arc by a point (center), a starting point and an angle + */ +class ArcBCPAType + : public ArgsParserObjectType +{ + typedef ArgsParserObjectType Parent; + ArcBCPAType(); + ~ArcBCPAType(); +public: + static const ArcBCPAType* instance(); + + ObjectImp* calc( const Args& args, const KigDocument& ) const; + + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + + bool inherits( int type ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/base_type.cc b/kig/objects/base_type.cc new file mode 100644 index 00000000..0f8eecec --- /dev/null +++ b/kig/objects/base_type.cc @@ -0,0 +1,112 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "base_type.h" + +#include "point_imp.h" +#include "line_imp.h" +#include "bogus_imp.h" +#include "object_calcer.h" + +#include "../misc/common.h" + +ObjectABType::ObjectABType( const char* fulltypename, const ArgsParser::spec* spec, int n ) + : ArgsParserObjectType( fulltypename, spec, n ) +{ +} + +ObjectABType::~ObjectABType() +{ +} + +ObjectImp* ObjectABType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) + return new InvalidImp; + + Coordinate a = static_cast<const PointImp*>( parents[0] )->coordinate(); + Coordinate b = static_cast<const PointImp*>( parents[1] )->coordinate(); + + return calc( a, b ); +} + +bool ObjectABType::canMove( const ObjectTypeCalcer& o ) const +{ + return isFreelyTranslatable( o ); +/* + * as observed by domi: this object is actually movable also + * if one point is FreelyTranslatable and the other is + * only movable, but then the "move" itself requires some + * trickery. + */ +} + +bool ObjectABType::isFreelyTranslatable( const ObjectTypeCalcer& o ) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + return parents[0]->isFreelyTranslatable() && parents[1]->isFreelyTranslatable(); +} + +void ObjectABType::move( ObjectTypeCalcer& o, const Coordinate& to, + const KigDocument& d ) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + assert( margsparser.checkArgs( parents ) ); + const Coordinate a = static_cast<const PointImp*>( parents[0]->imp() )->coordinate(); + const Coordinate b = static_cast<const PointImp*>( parents[1]->imp() )->coordinate(); + const Coordinate dist = b - a; + if ( parents[0]->canMove() ) + parents[0]->move( to, d ); + if ( parents[1]->canMove() ) + parents[1]->move( to + dist, d ); +} + +ObjectLPType::ObjectLPType( const char* fullname, const ArgsParser::spec* spec, int n ) + : ArgsParserObjectType( fullname, spec, n ) +{ +} + +ObjectLPType::~ObjectLPType() +{ +} + +ObjectImp* ObjectLPType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + LineData l = static_cast<const AbstractLineImp*>( args[0] )->data(); + Coordinate c = static_cast<const PointImp*>( args[1] )->coordinate(); + return calc( l, c ); +} + +const Coordinate ObjectABType::moveReferencePoint( const ObjectTypeCalcer& o ) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + assert( margsparser.checkArgs( parents ) ); + return static_cast<const PointImp*>( parents[0]->imp() )->coordinate(); +} + +std::vector<ObjectCalcer*> ObjectABType::movableParents( const ObjectTypeCalcer& ourobj ) const +{ + std::vector<ObjectCalcer*> parents = ourobj.parents(); + std::set<ObjectCalcer*> ret; + std::vector<ObjectCalcer*> tmp = parents[0]->movableParents(); + ret.insert( tmp.begin(), tmp.end() ); + tmp = parents[1]->movableParents(); + ret.insert( tmp.begin(), tmp.end() ); + ret.insert( parents.begin(), parents.end() ); + return std::vector<ObjectCalcer*>( ret.begin(), ret.end() ); +} diff --git a/kig/objects/base_type.h b/kig/objects/base_type.h new file mode 100644 index 00000000..ff9c1983 --- /dev/null +++ b/kig/objects/base_type.h @@ -0,0 +1,57 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_BASE_TYPE_H +#define KIG_OBJECTS_BASE_TYPE_H + +#include "object_type.h" + +#include "../misc/argsparser.h" + +class LineData; + +class ObjectABType + : public ArgsParserObjectType +{ +protected: + ObjectABType( const char* fulltypename, const ArgsParser::spec* argsspec, int n ); + ~ObjectABType(); +public: + ObjectImp* calc( const Args& args, const KigDocument& ) const; + bool canMove( const ObjectTypeCalcer& o ) const; + bool isFreelyTranslatable( const ObjectTypeCalcer& o ) const; + std::vector<ObjectCalcer*> movableParents( const ObjectTypeCalcer& ourobj ) const; + void move( ObjectTypeCalcer& o, const Coordinate& to, + const KigDocument& d ) const; + const Coordinate moveReferencePoint( const ObjectTypeCalcer& o ) const; + + virtual ObjectImp* calc( const Coordinate& a, const Coordinate& b ) const = 0; +}; + +class ObjectLPType + : public ArgsParserObjectType +{ +protected: + ObjectLPType( const char* fullname, const ArgsParser::spec* spec, int n ); + ~ObjectLPType(); +public: + ObjectImp* calc( const Args& args, const KigDocument& ) const; + + virtual ObjectImp* calc( const LineData& a, const Coordinate& b ) const = 0; +}; + +#endif diff --git a/kig/objects/bogus_imp.cc b/kig/objects/bogus_imp.cc new file mode 100644 index 00000000..c1ed6526 --- /dev/null +++ b/kig/objects/bogus_imp.cc @@ -0,0 +1,388 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "bogus_imp.h" + +#include <qcstring.h> +#include <qstringlist.h> +#include <klocale.h> + +#include "../misc/rect.h" + +Coordinate BogusImp::attachPoint( ) const +{ + return Coordinate::invalidCoord(); +} + +void BogusImp::draw( KigPainter& ) const +{ +} + +bool BogusImp::contains( const Coordinate&, int, const KigWidget& ) const +{ + return false; +} + +bool BogusImp::inRect( const Rect&, int, const KigWidget& ) const +{ + return false; +} + +DoubleImp::DoubleImp( const double d ) + : mdata( d ) +{ +} + +IntImp::IntImp( const int d ) + : mdata( d ) +{ +} + +StringImp::StringImp( const QString& d ) + : mdata( d ) +{ +} + +DoubleImp* DoubleImp::copy() const +{ + return new DoubleImp( mdata ); +} + +IntImp* IntImp::copy() const +{ + return new IntImp( mdata ); +} + +StringImp* StringImp::copy() const +{ + return new StringImp( mdata ); +} + +ObjectImp* BogusImp::transform( const Transformation& ) const +{ + return copy(); +} + +InvalidImp* InvalidImp::copy() const +{ + return new InvalidImp(); +} + +InvalidImp::InvalidImp() +{ +} + +void InvalidImp::fillInNextEscape( QString& s, const KigDocument& ) const +{ + s = s.arg( "[invalid]" ); +} + +void DoubleImp::fillInNextEscape( QString& s, const KigDocument& ) const +{ + s = s.arg( mdata ); +} + +void IntImp::fillInNextEscape( QString& s, const KigDocument& ) const +{ + s = s.arg( mdata ); +} + +void StringImp::fillInNextEscape( QString& s, const KigDocument& ) const +{ + s = s.arg( mdata ); +} + +HierarchyImp::HierarchyImp( const ObjectHierarchy& h ) + : BogusImp(), mdata( h ) +{ +} + +HierarchyImp* HierarchyImp::copy() const +{ + return new HierarchyImp( mdata ); +} + +void InvalidImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +void DoubleImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +void IntImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +void StringImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +void HierarchyImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +TransformationImp::TransformationImp( const Transformation& h ) + : mdata( h ) +{ +} + +TransformationImp* TransformationImp::copy() const +{ + return new TransformationImp( mdata ); +} + +void TransformationImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +bool InvalidImp::equals( const ObjectImp& rhs ) const +{ + return !rhs.valid(); +} + +bool DoubleImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( DoubleImp::stype() ) && + static_cast<const DoubleImp&>( rhs ).data() == mdata; +} + +bool IntImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( IntImp::stype() ) && + static_cast<const IntImp&>( rhs ).data() == mdata; +} + +bool StringImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( StringImp::stype() ) && + static_cast<const StringImp&>( rhs ).data() == mdata; +} + +bool HierarchyImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( HierarchyImp::stype() ) && + static_cast<const HierarchyImp&>( rhs ).data() == mdata; +} + +bool TransformationImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( TransformationImp::stype() ) && + static_cast<const TransformationImp&>( rhs ).data() == mdata; +} + +bool InvalidImp::canFillInNextEscape() const +{ + return true; +} + +bool DoubleImp::canFillInNextEscape() const +{ + return true; +} + +bool IntImp::canFillInNextEscape() const +{ + return true; +} + +bool StringImp::canFillInNextEscape() const +{ + return true; +} + +const ObjectImpType* InvalidImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "invalid", "", "", "", "", "", "", "", "", "" ); + return &t; +} + +const ObjectImpType* StringImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "string", + "string", "", "", "", "", "", "", "", "" ); + return &t; +} +const ObjectImpType* HierarchyImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "hierarchy", "", "", "", "", "", "", "", "", "" ); + return &t; +} +const ObjectImpType* TransformationImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "transformation", "", "", "", "", "", "", "", "", ""); + return &t; +} + +const ObjectImpType* InvalidImp::type() const +{ + return InvalidImp::stype(); +} + +const ObjectImpType* DoubleImp::type() const +{ + return DoubleImp::stype(); +} + +const ObjectImpType* IntImp::type() const +{ + return IntImp::stype(); +} + +const ObjectImpType* StringImp::type() const +{ + return StringImp::stype(); +} + +const ObjectImpType* HierarchyImp::type() const +{ + return HierarchyImp::stype(); +} + +const ObjectImpType* TransformationImp::type() const +{ + return TransformationImp::stype(); +} + +const ObjectImpType* DoubleImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "double", + "double", "", "", "", "", "", "", "", "" ); + return &t; +} + +const ObjectImpType* IntImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "int", + "int", "", "", "", "", "", "", "", "" ); + return &t; +} + +const ObjectImpType* BogusImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "bogus", + "", "", "", "", "", "", "", "", "" ); + return &t; +} + +const ObjectImpType* TestResultImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "testresult", "", "", "", "", "", "", "", "", "" ); + return &t; + +} + +TestResultImp::TestResultImp( const QString& s ) + : mdata( s ) +{ +} + +TestResultImp* TestResultImp::copy() const +{ + return new TestResultImp( mdata ); +} + +const ObjectImpType* TestResultImp::type() const +{ + return stype(); +} + +void TestResultImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +bool TestResultImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( TestResultImp::stype() ) && + static_cast<const TestResultImp&>( rhs ).mdata == mdata; + +} + +const uint TestResultImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 1; +} + +const QCStringList TestResultImp::properties() const +{ + QCStringList l = Parent::properties(); + l << I18N_NOOP( "Test Result" ); + assert( l.size() == TestResultImp::numberOfProperties() ); + return l; +} + +const QCStringList TestResultImp::propertiesInternalNames() const +{ + QCStringList s = Parent::propertiesInternalNames(); + s << "test-result"; + assert( s.size() == TestResultImp::numberOfProperties() ); + return s; +} + +ObjectImp* TestResultImp::property( uint which, const KigDocument& d ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, d ); + if ( which == Parent::numberOfProperties() ) + return new StringImp( data() ); + else assert( false ); + return new InvalidImp; +} + +const char* TestResultImp::iconForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + if ( which == Parent::numberOfProperties() ) + return ""; // test-result + else assert( false ); + return ""; +} + +const ObjectImpType* TestResultImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else return TestResultImp::stype(); +} + +bool TestResultImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else return false; +} + +Rect BogusImp::surroundingRect() const +{ + return Rect::invalidRect(); +} diff --git a/kig/objects/bogus_imp.h b/kig/objects/bogus_imp.h new file mode 100644 index 00000000..8e9386a8 --- /dev/null +++ b/kig/objects/bogus_imp.h @@ -0,0 +1,281 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef BOGUS_IMP_H +#define BOGUS_IMP_H + +#include "object_imp.h" +#include "../misc/object_hierarchy.h" +#include "../misc/kigtransform.h" + +#include <qstring.h> + +/** + * This is the base class for the so-called BogusImp's. These + * ObjectImp's are not really ObjectImp's, in that they don't + * represent objects. They exist because ObjectImp's also serve + * another purpose, namely containing data. They can all be loaded + * and saved, and the only difference between these objects and normal + * objects are that these serve *only* to be loaded and saved. This + * approach adds a lot of flexibility to the Kig system, and has + * certainly proven itself very valuable. + */ +class BogusImp + : public ObjectImp +{ + typedef ObjectImp Parent; +public: + /** + * Returns the ObjectImpType representing the BogusImp type. + */ + static const ObjectImpType* stype(); + + Coordinate attachPoint( ) const; + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& w ) const; + bool inRect( const Rect& r, int width, const KigWidget& w ) const; + Rect surroundingRect() const; + + ObjectImp* transform( const Transformation& ) const; +}; + +/** + * This ObjectImp represents an invalid object. If a calculation + * fails, then often an InvalidImp is returned, indicating that the + * generated object is invalid. + */ +class InvalidImp + : public BogusImp +{ +public: + /** + * Returns the ObjectImpType representing the InvalidImp type. + */ + static const ObjectImpType* stype(); + typedef BogusImp Parent; + + /** + * Construct a new InvalidImp. + */ + InvalidImp(); + InvalidImp* copy() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool canFillInNextEscape() const; + void fillInNextEscape( QString& s, const KigDocument& ) const; + + bool equals( const ObjectImp& rhs ) const; +}; + +/** + * This ObjectImp is a BogusImp containing only a double value. + */ +class DoubleImp + : public BogusImp +{ + double mdata; +public: + /** + * Returns the ObjectImpType representing the DoubleImp type. + */ + static const ObjectImpType* stype(); + typedef BogusImp Parent; + + /** + * Construct a new DoubleImp containing the value d. + */ + DoubleImp( const double d ); + + /** + * Get hold of the contained data. + */ + double data() const { return mdata; } + /** + * Set the contained data to d. + */ + void setData( double d ) { mdata = d; } + + DoubleImp* copy() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool canFillInNextEscape() const; + void fillInNextEscape( QString& s, const KigDocument& ) const; + + bool equals( const ObjectImp& rhs ) const; +}; + +/** + * This ObjectImp is a BogusImp containing only an int value. + */ +class IntImp + : public BogusImp +{ + int mdata; +public: + /** + * Returns the ObjectImpType representing the IntImp type.. + */ + static const ObjectImpType* stype(); + typedef BogusImp Parent; + + /** + * Construct a new IntImp containing the value d. + */ + IntImp( const int d ); + + /** + * Get hold of the contained data. + */ + int data() const { return mdata; } + /** + * Set the contained data to d. + */ + void setData( int d ) { mdata = d; } + + IntImp* copy() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool canFillInNextEscape() const; + void fillInNextEscape( QString& s, const KigDocument& ) const; + + bool equals( const ObjectImp& rhs ) const; +}; + +/** + * This ObjectImp is a BogusImp containing only a string value. + */ +class StringImp + : public BogusImp +{ + QString mdata; +public: + /** + * Returns the ObjectImpType representing the StringImp type.. + */ + static const ObjectImpType* stype(); + typedef BogusImp Parent; + + /** + * Construct a new StringImp containing the string d. + */ + StringImp( const QString& d ); + + /** + * Get hold of the contained data. + */ + const QString& data() const { return mdata; } + /** + * Set the contained data. + */ + void setData( const QString& s ) { mdata = s; } + + StringImp* copy() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool canFillInNextEscape() const; + void fillInNextEscape( QString& s, const KigDocument& ) const; + + bool equals( const ObjectImp& rhs ) const; +}; + +class HierarchyImp + : public BogusImp +{ + ObjectHierarchy mdata; +public: + static const ObjectImpType* stype(); + typedef BogusImp Parent; + + HierarchyImp( const ObjectHierarchy& h ); + + const ObjectHierarchy& data() const { return mdata; } + void setData( const ObjectHierarchy& h ) { mdata = h; } + + HierarchyImp* copy() const; + const char* baseName() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool equals( const ObjectImp& rhs ) const; +}; + +/** + * \internal Don't mistake this imp for something that draws a + * transformed object. It does something completely different. It's + * a pure data Imp, like DoubleImp and friends that serves only to + * store the data of a transformation ( see the Transformation class + * in ../misc/kigtransform.h + */ +class TransformationImp + : public BogusImp +{ + Transformation mdata; +public: + static const ObjectImpType* stype(); + typedef BogusImp Parent; + + TransformationImp( const Transformation& h ); + + const Transformation& data() const { return mdata; } + void setData( const Transformation& h ) { mdata = h; } + + TransformationImp* copy() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool equals( const ObjectImp& rhs ) const; +}; + +class TestResultImp + : public BogusImp +{ + const QString mdata; +public: + static const ObjectImpType* stype(); + typedef BogusImp Parent; + + TestResultImp( const QString& s ); + + TestResultImp* copy() const; + + const QString& data() const { return mdata; } + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& d ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + bool equals( const ObjectImp& rhs ) const; +}; + +#endif diff --git a/kig/objects/centerofcurvature_type.cc b/kig/objects/centerofcurvature_type.cc new file mode 100644 index 00000000..8111410f --- /dev/null +++ b/kig/objects/centerofcurvature_type.cc @@ -0,0 +1,304 @@ +// Copyright (C) 2004 Maurizio Paolini <paolini@dmf.unicatt.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "centerofcurvature_type.h" + +#include "bogus_imp.h" +#include "conic_imp.h" +#include "cubic_imp.h" +//#include "other_imp.h" +#include "point_imp.h" +//#include "line_imp.h" + +#include "../misc/common.h" +#include "../misc/conic-common.h" +//#include "../misc/calcpaths.h" +#include "../kig/kig_part.h" +#include "../kig/kig_view.h" + +static const char constructcenterofcurvaturepoint[] = "SHOULDNOTBESEEN"; +// I18N_NOOP( "Construct the center of curvature corresponding to this point" ); +static const char selectcoc1[] = I18N_NOOP( "Select the curve..." ); +static const char selectcoc2[] = I18N_NOOP( "Select a point on the curve..." ); + +static const ArgsParser::spec argsspecCocConic[] = +{ + { ConicImp::stype(), "SHOULDNOTBESEEN", selectcoc1, false }, + { PointImp::stype(), constructcenterofcurvaturepoint, selectcoc2, false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CocConicType ) + +CocConicType::CocConicType() + : ArgsParserObjectType( "CocConic", argsspecCocConic, 2 ) +{ +} + +CocConicType::~CocConicType() +{ +} + +const CocConicType* CocConicType::instance() +{ + static const CocConicType t; + return &t; +} + +ObjectImp* CocConicType::calc( const Args& args, const KigDocument& doc ) const +{ + if ( !margsparser.checkArgs( args ) ) + return new InvalidImp; + + const ConicImp* conic = static_cast<const ConicImp*>( args[0] ); + const Coordinate& p = static_cast<const PointImp*>( args[1] )->coordinate(); + + if ( !conic->containsPoint( p, doc ) ) + return new InvalidImp; + + double x = p.x; + double y = p.y; + ConicCartesianData data = conic->cartesianData(); +// double aconst = data.coeffs[5]; + double ax = data.coeffs[3]; + double ay = data.coeffs[4]; + double axx = data.coeffs[0]; + double axy = data.coeffs[2]; + double ayy = data.coeffs[1]; + +/* + * mp: we need to compute the normal vector and the curvature + * of the curve. The curve (conic) is given in implicit form + * f(x,y) = 0; the gradient of f gives the direction of the + * normal; for the curvature we can use the following formula: + * k = div(grad f/|grad f|) + * + * the hessian matrix has elements [hfxx, hfxy] + * [hfxy, hfyy] + * + * kgf is the curvature multiplied by the norm of grad f + */ + + double gradfx = 2*axx*x + axy*y + ax; + double gradfy = axy*x + 2*ayy*y + ay; + Coordinate gradf = Coordinate ( gradfx, gradfy ); + + double hfxx = 2*axx; + double hfyy = 2*ayy; + double hfxy = axy; + + double kgf = hfxx + hfyy + - (hfxx*gradfx*gradfx + hfyy*gradfy*gradfy + 2*hfxy*gradfx*gradfy) + /(gradfx*gradfx + gradfy*gradfy); + + bool ok = true; + + const Coordinate coc = p - 1/kgf*gradf; + + if ( !ok ) + return new InvalidImp; + + return new PointImp( coc ); +} + +const ObjectImpType* CocConicType::resultId() const +{ + return PointImp::stype(); +} + +/**** Cubic starts here ****/ + +static const ArgsParser::spec argsspecCocCubic[] = +{ + { CubicImp::stype(), "SHOULDNOTBESEEN", selectcoc1, false }, + { PointImp::stype(), constructcenterofcurvaturepoint, selectcoc2, false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CocCubicType ) + +CocCubicType::CocCubicType() + : ArgsParserObjectType( "CocCubic", argsspecCocCubic, 2 ) +{ +} + +CocCubicType::~CocCubicType() +{ +} + +const CocCubicType* CocCubicType::instance() +{ + static const CocCubicType t; + return &t; +} + +ObjectImp* CocCubicType::calc( const Args& args, const KigDocument& doc ) const +{ + if ( !margsparser.checkArgs( args ) ) + return new InvalidImp; + + const CubicImp* cubic = static_cast<const CubicImp*>( args[0] ); + const Coordinate& p = static_cast<const PointImp*>( args[1] )->coordinate(); + + if ( !cubic->containsPoint( p, doc ) ) + return new InvalidImp; + + double x = p.x; + double y = p.y; + CubicCartesianData data = cubic->data(); +// double aconst = data.coeffs[0]; + double ax = data.coeffs[1]; + double ay = data.coeffs[2]; + double axx = data.coeffs[3]; + double axy = data.coeffs[4]; + double ayy = data.coeffs[5]; + double axxx = data.coeffs[6]; + double axxy = data.coeffs[7]; + double axyy = data.coeffs[8]; + double ayyy = data.coeffs[9]; + + /* + * we use here the same mechanism as for the + * conics, see above + */ + + double gradfx = 3*axxx*x*x + 2*axxy*x*y + axyy*y*y + 2*axx*x + axy*y + ax; + double gradfy = axxy*x*x + 2*axyy*x*y + 3*ayyy*y*y + axy*x + 2*ayy*y + ay; + Coordinate gradf = Coordinate ( gradfx, gradfy ); + + double hfxx = 6*axxx*x + 2*axxy*y + 2*axx; + double hfyy = 6*ayyy*y + 2*axyy*x + 2*ayy; + double hfxy = 2*axxy*x + 2*axyy*y + axy; + + double kgf = hfxx + hfyy + - (hfxx*gradfx*gradfx + hfyy*gradfy*gradfy + 2*hfxy*gradfx*gradfy) + /(gradfx*gradfx + gradfy*gradfy); + + bool ok = true; + + const Coordinate coc = p - 1/kgf*gradf; + + if ( !ok ) + return new InvalidImp; + + return new PointImp( coc ); +} + +const ObjectImpType* CocCubicType::resultId() const +{ + return PointImp::stype(); +} + +/**** Curve starts here ****/ + +static const ArgsParser::spec argsspecCocCurve[] = +{ + { CurveImp::stype(), "SHOULDNOTBESEEN", selectcoc1, false }, + { PointImp::stype(), constructcenterofcurvaturepoint, selectcoc2, false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CocCurveType ) + +CocCurveType::CocCurveType() + : ArgsParserObjectType( "CocCurve", argsspecCocCurve, 2 ) +{ +} + +CocCurveType::~CocCurveType() +{ +} + +const CocCurveType* CocCurveType::instance() +{ + static const CocCurveType t; + return &t; +} + +ObjectImp* CocCurveType::calc( const Args& args, const KigDocument& doc ) const +{ + if ( !margsparser.checkArgs( args ) ) + return new InvalidImp; + + const CurveImp* curve = static_cast<const CurveImp*>( args[0] ); + const Coordinate& p = static_cast<const PointImp*>( args[1] )->coordinate(); + + if ( !curve->containsPoint( p, doc ) ) + return new InvalidImp; + + + const double t = curve->getParam( p, doc ); + const double tau0 = 5e-4; + const double sigmasq = 1e-12; + const int maxiter = 20; + + double tau = tau0; + Coordinate gminus, g, gplus, tang, acc, curv, err; + double velsq, curvsq; + double tplus = t + tau; + double tminus = t - tau; + double t0 = t; + if ( tplus > 1 ) {tplus = 1; t0 = 1 - tau; tminus = 1 - 2*tau;} + if ( tminus < 0 ) {tminus = 0; t0 = tau; tplus = 2*tau;} + gminus = curve->getPoint( tminus, doc ); + g = curve->getPoint( t0, doc ); + gplus = curve->getPoint( tplus, doc ); + tang = (gplus - gminus)/(2*tau); + acc = (gminus + gplus - 2*g)/(tau*tau); + velsq = tang.x*tang.x + tang.y*tang.y; + tang = tang/velsq; + Coordinate curvold = acc/velsq - (acc.x*tang.x + acc.y*tang.y)*tang; + curvsq = curvold.x*curvold.x + curvold.y*curvold.y; + curvold = curvold/curvsq; + + for (int i = 0; i < maxiter; i++) + { + tau = tau/2; + tplus = t + tau; + tminus = t - tau; + t0 = t; + if ( tplus > 1 ) {tplus = 1; t0 = 1 - tau; tminus = 1 - 2*tau;} + if ( tminus < 0 ) {tminus = 0; t0 = tau; tplus = 2*tau;} + + gminus = curve->getPoint( tminus, doc ); + g = curve->getPoint( t0, doc ); + gplus = curve->getPoint( tplus, doc ); + tang = (gplus - gminus)/(2*tau); + acc = (gminus + gplus - 2*g)/(tau*tau); + velsq = tang.x*tang.x + tang.y*tang.y; + tang = tang/velsq; + curv = acc/velsq - (acc.x*tang.x + acc.y*tang.y)*tang; + curvsq = curv.x*curv.x + curv.y*curv.y; + curv = curv/curvsq; + + err = (curvold - curv)/3; + /* + * curvsq is the inverse squared of the norm of curvsq + * so this is actually a relative test + * in the end we return an extrapolated value + */ + if (err.squareLength() < sigmasq/curvsq) + { + curv = (4*curv - curvold)/3; + return new PointImp( p + curv ); + } + curvold = curv; + } + return new InvalidImp; +} + +const ObjectImpType* CocCurveType::resultId() const +{ + return PointImp::stype(); +} diff --git a/kig/objects/centerofcurvature_type.h b/kig/objects/centerofcurvature_type.h new file mode 100644 index 00000000..18dfc42a --- /dev/null +++ b/kig/objects/centerofcurvature_type.h @@ -0,0 +1,68 @@ +// Copyright (C) 2004 Maurizio Paolini <paolini@dmf.unicatt.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_CENTEROFCURVATURE_TYPE_H +#define KIG_OBJECTS_CENTEROFCURVATURE_TYPE_H + +#include "base_type.h" + +/** + * the center of curvature of a conic at a point + */ +class CocConicType + : public ArgsParserObjectType +{ + typedef ArgsParserObjectType Parent; + CocConicType(); + ~CocConicType(); +public: + static const CocConicType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +/** + * the center of curvature of a cubic at a point + */ +class CocCubicType + : public ArgsParserObjectType +{ + typedef ArgsParserObjectType Parent; + CocCubicType(); + ~CocCubicType(); +public: + static const CocCubicType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +/** + * the center of curvature of a curve at a point + */ +class CocCurveType + : public ArgsParserObjectType +{ + typedef ArgsParserObjectType Parent; + CocCurveType(); + ~CocCurveType(); +public: + static const CocCurveType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/circle_imp.cc b/kig/objects/circle_imp.cc new file mode 100644 index 00000000..13ceef93 --- /dev/null +++ b/kig/objects/circle_imp.cc @@ -0,0 +1,356 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "circle_imp.h" + +#include "bogus_imp.h" +#include "point_imp.h" + +#include "../misc/kigtransform.h" +#include "../misc/kigpainter.h" +#include "../misc/coordinate_system.h" + +#include "../kig/kig_document.h" +#include "../kig/kig_view.h" + +#include <klocale.h> + +#include <math.h> + +CircleImp::CircleImp( const Coordinate& center, double radius ) + : mcenter( center ), mradius( radius ) +{ +} + +CircleImp::~CircleImp() +{ +} + +ObjectImp* CircleImp::transform( const Transformation& t ) const +{ + if ( t.isHomothetic() ) + { + Coordinate nc = t.apply( mcenter ); + double nr = t.apply( mradius ); + if ( nc.valid() ) + return new CircleImp( nc, nr ); + else return new InvalidImp; + } + else + { + // domi: i should return a ConicImp here, but i don't know how to + // calculate it.. + return Parent::transform( t ); + }; +} + +void CircleImp::draw( KigPainter& p ) const +{ + p.drawCircle( mcenter, mradius ); +} + +bool CircleImp::contains( const Coordinate& p, int width, const KigWidget& w ) const +{ + return fabs((mcenter - p).length() - mradius) <= w.screenInfo().normalMiss( width ); +} + +bool CircleImp::inRect( const Rect& r, int width, const KigWidget& w ) const +{ + // first we check if the rect contains at least one of the + // north/south/east/west points of the circle + if ( r.contains( mcenter + Coordinate( 0, -mradius ) ) ) return true; + if ( r.contains( mcenter + Coordinate( mradius, 0 ) ) ) return true; + if ( r.contains( mcenter + Coordinate( 0, mradius ) ) ) return true; + if ( r.contains( mcenter + Coordinate( -mradius, 0 ) ) ) return true; + + // we allow a miss of some pixels .. + double miss = w.screenInfo().normalMiss( width ); + double bigradius = mradius + miss; + bigradius *= bigradius; + double smallradius = mradius - miss; + smallradius *= smallradius; + + const int in = -1; + const int undecided = 0; + const int out = 1; + + int inorout = undecided; + + Coordinate coords[4]; + coords[0] = r.topLeft(); + coords[1] = r.topRight(); + coords[2] = r.bottomRight(); + coords[3] = r.bottomLeft(); + + // we check if the corners of the rect are either + for ( Coordinate* i = coords; i < coords + 4; ++i ) + { + double t = ( *i - mcenter ).squareLength(); + if ( t >= bigradius ) + { + if ( inorout == in ) return true; + inorout = out; + } + else if ( t <= smallradius ) + { + if ( inorout == out ) return true; + inorout = in; + } + } + return inorout == undecided; +} + +bool CircleImp::valid() const +{ + return true; +} + +const uint CircleImp::numberOfProperties() const +{ + // We _intentionally_ do not use the Conic properties.. + return CurveImp::numberOfProperties() + 7; +} + +const QCStringList CircleImp::propertiesInternalNames() const +{ + QCStringList l = CurveImp::propertiesInternalNames(); + l << "surface"; + l << "circumference"; + l << "radius"; + l << "center"; + l << "cartesian-equation"; + l << "simply-cartesian-equation"; + l << "polar-equation"; + assert( l.size() == CircleImp::numberOfProperties() ); + return l; +} + +const QCStringList CircleImp::properties() const +{ + QCStringList l = CurveImp::properties(); + l << I18N_NOOP( "Surface" ); + l << I18N_NOOP( "Circumference" ); + l << I18N_NOOP( "Radius" ); + l << I18N_NOOP( "Center" ); + l << I18N_NOOP( "Expanded Cartesian Equation" ); + l << I18N_NOOP( "Cartesian Equation" ); + l << I18N_NOOP( "Polar Equation" ); + assert( l.size() == CircleImp::numberOfProperties() ); + return l; +} + +const ObjectImpType* CircleImp::impRequirementForProperty( uint which ) const +{ + if ( which < CurveImp::numberOfProperties() ) + return CurveImp::impRequirementForProperty( which ); + else return CircleImp::stype(); +} + +const char* CircleImp::iconForProperty( uint which ) const +{ + assert( which < CircleImp::numberOfProperties() ); + if ( which < CurveImp::numberOfProperties() ) + return CurveImp::iconForProperty( which ); + else if ( which == CurveImp::numberOfProperties() ) + return "areaCircle"; // surface + else if ( which == CurveImp::numberOfProperties() + 1 ) + return "circumference"; // circumference + else if ( which == CurveImp::numberOfProperties() + 2 ) + return ""; //radius + else if ( which == CurveImp::numberOfProperties() + 3 ) + return "baseCircle"; // circle center + else if ( which == CurveImp::numberOfProperties() + 4 ) + return "kig_text"; // cartesian equation + else if ( which == CurveImp::numberOfProperties() + 5 ) + return "kig_text"; // simply cartesian equation + else if ( which == CurveImp::numberOfProperties() + 6 ) + return "kig_text"; // polar equation + else assert( false ); + return ""; +} + +ObjectImp* CircleImp::property( uint which, const KigDocument& w ) const +{ + assert( which < CircleImp::numberOfProperties() ); + if ( which < CurveImp::numberOfProperties() ) + return CurveImp::property( which, w ); + if ( which == CurveImp::numberOfProperties() ) + return new DoubleImp( surface() ); + else if ( which == CurveImp::numberOfProperties() + 1 ) + return new DoubleImp( circumference() ); + else if ( which == CurveImp::numberOfProperties() + 2 ) + return new DoubleImp( radius() ); + else if ( which == CurveImp::numberOfProperties() + 3 ) + return new PointImp( center() ); + else if ( which == CurveImp::numberOfProperties() + 4 ) + return new StringImp( cartesianEquationString( w ) ); + else if ( which == CurveImp::numberOfProperties() + 5 ) + return new StringImp( simplyCartesianEquationString( w ) ); + else if ( which == CurveImp::numberOfProperties() + 6 ) + return new StringImp( polarEquationString( w ) ); + else assert( false ); + return new InvalidImp; +} + +const Coordinate CircleImp::center() const +{ + return mcenter; +} + +double CircleImp::radius() const +{ + return mradius; +} + +double CircleImp::surface() const +{ + return M_PI * squareRadius(); +} + +double CircleImp::squareRadius() const +{ + return mradius * mradius; +} + +double CircleImp::circumference() const +{ + return 2 * M_PI * radius(); +} + +QString CircleImp::polarEquationString( const KigDocument& w ) const +{ + QString ret = i18n( "rho = %1 [centered at %2]" ); + ConicPolarData data = polarData(); + ret = ret.arg( data.pdimen, 0, 'g', 3 ); + ret = ret.arg( w.coordinateSystem().fromScreen( data.focus1, w ) ); + return ret; +} + +QString CircleImp::cartesianEquationString( const KigDocument& ) const +{ + QString ret = i18n( "x² + y² + %1 x + %2 y + %3 = 0" ); + ConicCartesianData data = cartesianData(); + ret = ret.arg( data.coeffs[3], 0, 'g', 3 ); + ret = ret.arg( data.coeffs[4], 0, 'g', 3 ); + ret = ret.arg( data.coeffs[5], 0, 'g', 3 ); + return ret; +} + +QString CircleImp::simplyCartesianEquationString( const KigDocument& ) const +{ + QString ret = i18n( "( x - %1 )² + ( y - %2 )² = %3" ); + ret = ret.arg( mcenter.x, 0, 'g', 3 ); + ret = ret.arg( mcenter.y, 0, 'g', 3 ); + ret = ret.arg( mradius * mradius, 0, 'g', 3 ); + return ret; +} + +Coordinate CircleImp::focus1() const +{ + return center(); +} + +Coordinate CircleImp::focus2() const +{ + return center(); +} + +int CircleImp::conicType() const +{ + return 1; +} + +const ConicCartesianData CircleImp::cartesianData() const +{ + Coordinate c = center(); + double sqr = squareRadius(); + ConicCartesianData data( + 1.0, 1.0, 0.0, -2*c.x, -2*c.y, + c.x*c.x + c.y*c.y - sqr ); + return data; +} + +const ConicPolarData CircleImp::polarData() const +{ + return ConicPolarData( center(), radius(), 0, 0 ); +} + +CircleImp* CircleImp::copy() const +{ + return new CircleImp( mcenter, mradius ); +} + +double CircleImp::getParam( const Coordinate& point, const KigDocument& ) const +{ + Coordinate tmp = point - mcenter; + double ret = atan2(tmp.y, tmp.x) / ( 2 * M_PI ); + if ( ret > 0 ) return ret; + else return ret + 1; +} + +const Coordinate CircleImp::getPoint( double p, const KigDocument& ) const +{ + return mcenter + Coordinate (cos(p * 2 * M_PI), sin(p * 2 * M_PI)) * mradius; +} + +void CircleImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +bool CircleImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( CircleImp::stype() ) && + static_cast<const CircleImp&>( rhs ).center() == center() && + static_cast<const CircleImp&>( rhs ).radius() == radius(); +} + +const ObjectImpType* CircleImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "circle", + I18N_NOOP( "circle" ), + I18N_NOOP( "Select this circle" ), + I18N_NOOP( "Select circle %1" ), + I18N_NOOP( "Remove a Circle" ), + I18N_NOOP( "Add a Circle" ), + I18N_NOOP( "Move a Circle" ), + I18N_NOOP( "Attach to this circle" ), + I18N_NOOP( "Show a Circle" ), + I18N_NOOP( "Hide a Circle" ) + ); + return &t; +} + +const ObjectImpType* CircleImp::type() const +{ + return CircleImp::stype(); +} + +bool CircleImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + assert( which < CircleImp::numberOfProperties() ); + if ( which < CurveImp::numberOfProperties() ) + return CurveImp::isPropertyDefinedOnOrThroughThisImp( which ); + return false; +} + +Rect CircleImp::surroundingRect() const +{ + Coordinate d( mradius, mradius ); + return Rect( mcenter - d, mcenter + d ); +} diff --git a/kig/objects/circle_imp.h b/kig/objects/circle_imp.h new file mode 100644 index 00000000..d38716a6 --- /dev/null +++ b/kig/objects/circle_imp.h @@ -0,0 +1,125 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_CIRCLE_IMP_H +#define KIG_OBJECTS_CIRCLE_IMP_H + +#include "conic_imp.h" + +/** + * An ObjectImp representing a circle. This class is a subclass of + * ConicImp, ensuring that a circle can be used as a conic. + */ +class CircleImp + : public ConicImp +{ + Coordinate mcenter; + double mradius; +public: + typedef ConicImp Parent; + /** + * Returns the ObjectImpType representing the CircleImp type.. + */ + static const ObjectImpType* stype(); + + /** + * Construct a Circle with a given center and radius. + */ + CircleImp( const Coordinate& center, double radius ); + ~CircleImp(); + CircleImp* copy() const; + + ObjectImp* transform( const Transformation& ) const; + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& ) const; + bool inRect( const Rect& r, int width, const KigWidget& ) const; + bool valid() const; + Rect surroundingRect() const; + + double getParam( const Coordinate& point, const KigDocument& ) const; + const Coordinate getPoint( double param, const KigDocument& ) const; + + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& w ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + /** + * Return the center of this circle. + */ + const Coordinate center() const; + /** + * Return the radius of this circle. + */ + double radius() const; + /** + * Return the square radius of this circle. Use this in preference + * to sqr( radius() ). + */ + double squareRadius() const; + /** + * Return the surface of this circle. + */ + double surface() const; + /** + * Return the circumference of this circle. + */ + double circumference() const; + + // trivial versions of the conic information functions.. + /** + * Always returns 1, since a circle always is an ellipse. + */ + int conicType() const; + const ConicCartesianData cartesianData() const; + const ConicPolarData polarData() const; + /** + * The first focus of a circle is simply its center. + */ + Coordinate focus1() const; + /** + * The second focus of a circle is simply its center. + */ + Coordinate focus2() const; + + /** + * Return a string containing the cartesian equation of this circle. + * This will be of the form "x^2 + y^2 + a x + b y + c = 0" + */ + QString cartesianEquationString( const KigDocument& w ) const; + /** + * Return a string containing the cartesian equation of this circle. + * This will be of the form "( x - x0 )^2 + ( y - y0 )^2 = r^2" + */ + QString simplyCartesianEquationString( const KigDocument& w ) const; + /** + * Return a string containing the polar equation of this circle. + * This will be of the form "rho = r [centered at p]" + */ + QString polarEquationString( const KigDocument& w ) const; + + bool equals( const ObjectImp& rhs ) const; +}; + +#endif diff --git a/kig/objects/circle_type.cc b/kig/objects/circle_type.cc new file mode 100644 index 00000000..97d2f615 --- /dev/null +++ b/kig/objects/circle_type.cc @@ -0,0 +1,181 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "circle_type.h" + +#include "circle_imp.h" +#include "bogus_imp.h" +#include "line_imp.h" +#include "point_imp.h" + +#include "../misc/common.h" + +#include <klocale.h> + +static const char constructcirclethroughpointstat[] = I18N_NOOP( "Construct a circle through this point" ); + +static const char constructcirclewithcenterstat[] = I18N_NOOP( "Construct a circle with this center" ); + +static const ArgsParser::spec argsspecCircleBCP[] = +{ + { PointImp::stype(), constructcirclewithcenterstat, + I18N_NOOP( "Select the center of the new circle..." ), false }, + { PointImp::stype(), constructcirclethroughpointstat, + I18N_NOOP( "Select a point for the new circle to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CircleBCPType ) + +CircleBCPType::CircleBCPType() + : ObjectABType( "CircleBCP", argsspecCircleBCP, 2 ) +{ +} + +CircleBCPType::~CircleBCPType() +{ +} + +const CircleBCPType* CircleBCPType::instance() +{ + static const CircleBCPType s; + return &s; +} + +ObjectImp* CircleBCPType::calc( const Coordinate& a, const Coordinate& b ) const +{ + return new CircleImp( a, ( b - a ).length() ); +} + +const CircleBTPType* CircleBTPType::instance() +{ + static const CircleBTPType t; + return &t; +} + +static const ArgsParser::spec argsspecCircleBTP[] = +{ + { PointImp::stype(), constructcirclethroughpointstat, + I18N_NOOP( "Select a point for the new circle to go through..." ), true }, + { PointImp::stype(), constructcirclethroughpointstat, + I18N_NOOP( "Select a point for the new circle to go through..." ), true }, + { PointImp::stype(), constructcirclethroughpointstat, + I18N_NOOP( "Select a point for the new circle to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CircleBTPType ) + +CircleBTPType::CircleBTPType() + : ArgsParserObjectType( "CircleBTP", argsspecCircleBTP, 3 ) +{ +} + +CircleBTPType::~CircleBTPType() +{ +} + +ObjectImp* CircleBTPType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args, 2 ) ) return new InvalidImp; + + const Coordinate a = static_cast<const PointImp*>( args[0] )->coordinate(); + const Coordinate b = static_cast<const PointImp*>( args[1] )->coordinate(); + Coordinate c; + if ( args.size() == 3 ) + c = static_cast<const PointImp*>( args[2] )->coordinate(); + else + { + // we pick the third point so that the three points form a + // triangle with equal sides... + + // midpoint: + Coordinate m = ( b + a ) / 2; + if ( b.y != a.y ) + { + // direction of the perpend: + double d = -(b.x-a.x)/(b.y-a.y); + + // length: + // sqrt( 3 ) == tan( 60° ) == sqrt( 2^2 - 1^2 ) + double l = 1.73205080756 * (a-b).length() / 2; + + double d2 = d*d; + double l2 = l*l; + double dx = sqrt( l2 / ( d2 + 1 ) ); + double dy = sqrt( l2 * d2 / ( d2 + 1 ) ); + if( d < 0 ) dy = -dy; + + c.x = m.x + dx; + c.y = m.y + dy; + } + else + { + c.x = m.x; + c.y = m.y + ( a.x - b.x ); + }; + }; + + const Coordinate center = calcCenter( a, b, c ); + if ( center.valid() ) + return new CircleImp( center, (center - a ).length() ); + else return new InvalidImp; +} + +const ObjectImpType* CircleBCPType::resultId() const +{ + return CircleImp::stype(); +} + +const ObjectImpType* CircleBTPType::resultId() const +{ + return CircleImp::stype(); +} + +static const ArgsParser::spec argsspecCircleBPR[] = +{ + { PointImp::stype(), "SHOULD NOT BE SEEN", "SHOULD NOT BE SEEN", false }, + { DoubleImp::stype(), "SHOULD NOT BE SEEN", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CircleBPRType ) + +CircleBPRType::CircleBPRType() + : ArgsParserObjectType( "CircleBPR", argsspecCircleBPR, 2 ) +{ +} + +CircleBPRType::~CircleBPRType() +{ +} + +const CircleBPRType* CircleBPRType::instance() +{ + static const CircleBPRType t; + return &t; +} + +ObjectImp* CircleBPRType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + const Coordinate c = static_cast<const PointImp*>( args[0] )->coordinate(); + double r = static_cast<const DoubleImp*>( args[1] )->data(); + return new CircleImp( c, r ); +} + +const ObjectImpType* CircleBPRType::resultId() const +{ + return CircleImp::stype(); +} diff --git a/kig/objects/circle_type.h b/kig/objects/circle_type.h new file mode 100644 index 00000000..874d0b69 --- /dev/null +++ b/kig/objects/circle_type.h @@ -0,0 +1,69 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_CIRCLE_TYPE_H +#define KIG_OBJECTS_CIRCLE_TYPE_H + +#include "base_type.h" + +/** + * Circle by center and point + */ +class CircleBCPType + : public ObjectABType +{ + CircleBCPType(); + ~CircleBCPType(); +public: + static const CircleBCPType* instance(); + + ObjectImp* calc( const Coordinate& a, const Coordinate& b ) const; + const ObjectImpType* resultId() const; +}; + +/** + * Circle by point and radius. + */ +class CircleBPRType + : public ArgsParserObjectType +{ + CircleBPRType(); + ~CircleBPRType(); +public: + static const CircleBPRType* instance(); + + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +/** + * Circle by three points + */ +class CircleBTPType + : public ArgsParserObjectType +{ + CircleBTPType(); + ~CircleBTPType(); + +public: + static const CircleBTPType* instance(); + + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/common.cc b/kig/objects/common.cc new file mode 100644 index 00000000..9932734c --- /dev/null +++ b/kig/objects/common.cc @@ -0,0 +1,43 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "common.h" +#include "object_holder.h" + +std::vector<ObjectCalcer*> getAllCalcers( const std::vector<ObjectHolder*>& os ) +{ + std::set<ObjectCalcer*> ret; + for ( std::vector<ObjectHolder*>::const_iterator i = os.begin(); + i != os.end(); ++i ) + { + if ( ( *i )->nameCalcer() ) + ret.insert( ( *i )->nameCalcer() ); + ret.insert( ( *i )->calcer() ); + } + return std::vector<ObjectCalcer*>( ret.begin(), ret.end() ); +} + +std::vector<ObjectCalcer*> getCalcers( const std::vector<ObjectHolder*>& os ) +{ + std::vector<ObjectCalcer*> ret; + ret.reserve( os.size() ); + for ( std::vector<ObjectHolder*>::const_iterator i = os.begin(); + i != os.end(); ++i ) + ret.push_back( ( *i )->calcer() ); + return ret; +} + diff --git a/kig/objects/common.h b/kig/objects/common.h new file mode 100644 index 00000000..a26a92cd --- /dev/null +++ b/kig/objects/common.h @@ -0,0 +1,107 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_COMMON_H +#define KIG_OBJECTS_COMMON_H + +#include <set> +#include <vector> +#include <qcstring.h> +#include <qvaluelist.h> +#include <qstringlist.h> +#include <cassert> +#include <klocale.h> + +class Coordinate; +class KigDocument; +class KigPainter; +class KigPart; +class KigWidget; +class NormalMode; +class ObjectCalcer; +class ObjectDrawer; +class ObjectHolder; +class ObjectImp; +class ObjectImpType; +class ObjectPropertyCalcer; +class ObjectType; +class ObjectTypeCalcer; +class QDomDocument; +class QDomElement; +class Rect; +class ScreenInfo; +class Transformation; + +typedef std::vector<const ObjectImp*> Args; +typedef QValueList<QCString> QCStringList; + +template<typename T> +void delete_all( T begin, T end ) +{ + for( ; begin != end; ++begin ) + { + delete *begin; + } +} + +/** + * get the calcers that the holders represent and their namecalcers + */ +std::vector<ObjectCalcer*> getAllCalcers( const std::vector<ObjectHolder*>& os ); + +/** + * get the calcers that the holders represent ( not their namecalcers ) + */ +std::vector<ObjectCalcer*> getCalcers( const std::vector<ObjectHolder*>& os ); + +/** + * The below is a trick. ObjectType's implement the singleton + * pattern. There can be only one of them at each time. This one + * instance of them needs to be constructed at some time, within the + * following restrictions: + * + * 1. They need to be constructed in the right order: if one ObjectType + * uses another in its constructor, the other needs to be constructed + * first. To achieve this, we use a scheme with ::instance() + * functions, that have a static variable in the body of the function + * ( if we would define them static outside of the function body, C++ + * would provide no guarantee on the order they would be called in ). + * + * 2. They need to be constructed before Kig tries to use them. They + * need to be added to the ObjectTypeFactory before anyone tries to + * use that class to fetch a type. The below is a trick to achieve + * that in combination with the previous. Basically, we add a fake + * static instance of an empty class that receives the instance of the + * ObjectType as an argument to its constructor. C++ then guarantees + * that these things will be constructed before main() is entered. + * + * Thus, for your own ObjectType classes: use the scheme with the + * static ::instance methods, and add + * \code + * KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( MyObjectType) + * \endcode + * to the .cpp file of your class. + */ +class FakeClass { +public: + FakeClass( const ObjectType* ) { + } +}; + +#define KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( type ) static class FakeClass _fake_class_instance_##type( type::instance() ); + +#endif diff --git a/kig/objects/conic_imp.cc b/kig/objects/conic_imp.cc new file mode 100644 index 00000000..a65d8511 --- /dev/null +++ b/kig/objects/conic_imp.cc @@ -0,0 +1,385 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "conic_imp.h" + +#include "bogus_imp.h" +#include "point_imp.h" + +#include "../misc/kigpainter.h" +#include "../misc/common.h" +#include "../misc/coordinate_system.h" + +#include "../kig/kig_document.h" +#include "../kig/kig_view.h" + +#include <klocale.h> + +ObjectImp* ConicImp::transform( const Transformation& t ) const +{ + bool valid = true; + ConicCartesianData d = calcConicTransformation( cartesianData(), t, valid ); + if ( ! valid ) return new InvalidImp; + else return new ConicImpCart( d ); +} + +void ConicImp::draw( KigPainter& p ) const +{ + p.drawCurve( this ); +} + +bool ConicImp::valid() const +{ + return true; +} + +bool ConicImp::contains( const Coordinate& o, int width, const KigWidget& w ) const +{ + return internalContainsPoint( o, w.screenInfo().normalMiss( width ) ); +} + +bool ConicImp::inRect( const Rect&, int, const KigWidget& ) const +{ + // TODO + return false; +} + +const uint ConicImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 5; +} + +const QCStringList ConicImp::propertiesInternalNames() const +{ + QCStringList l = Parent::propertiesInternalNames(); + l << "type"; + l << "first-focus"; + l << "second-focus"; + l << "cartesian-equation"; + l << "polar-equation"; + assert( l.size() == ConicImp::numberOfProperties() ); + return l; +} + +const QCStringList ConicImp::properties() const +{ + QCStringList l = Parent::properties(); + l << I18N_NOOP( "Conic Type" ); + l << I18N_NOOP( "First Focus" ); + l << I18N_NOOP( "Second Focus" ); + l << I18N_NOOP( "Cartesian Equation" ); + l << I18N_NOOP( "Polar Equation" ); + assert( l.size() == ConicImp::numberOfProperties() ); + return l; +} + +const ObjectImpType* ConicImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else return ConicImp::stype(); +} + +const char* ConicImp::iconForProperty( uint which ) const +{ + int pnum = 0; + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + if ( which == Parent::numberOfProperties() + pnum++ ) + return "kig_text"; // conic type string + else if ( which == Parent::numberOfProperties() + pnum++ ) + return ""; // focus1 + else if ( which == Parent::numberOfProperties() + pnum++ ) + return ""; // focus2 + else if ( which == Parent::numberOfProperties() + pnum++ ) + return "kig_text"; // cartesian equation string + else if ( which == Parent::numberOfProperties() + pnum++ ) + return "kig_text"; // polar equation string + else assert( false ); + return ""; +} + +ObjectImp* ConicImp::property( uint which, const KigDocument& w ) const +{ + int pnum = 0; + + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, w ); + if ( which == Parent::numberOfProperties() + pnum++ ) + return new StringImp( conicTypeString() ); + else if ( which == Parent::numberOfProperties() + pnum++ ) + return new PointImp( focus1() ); + else if ( which == Parent::numberOfProperties() + pnum++ ) + return new PointImp( focus2() ); + else if ( which == Parent::numberOfProperties() + pnum++ ) + return new StringImp( cartesianEquationString( w ) ); + else if ( which == Parent::numberOfProperties() + pnum++ ) + return new StringImp( polarEquationString( w ) ); + else assert( false ); + return new InvalidImp; +} + +double ConicImp::getParam( const Coordinate& p, const KigDocument& ) const +{ + const ConicPolarData d = polarData(); + Coordinate tmp = p - d.focus1; + double l = tmp.length(); + double theta = atan2(tmp.y, tmp.x); + double costheta = cos(theta); + double sintheta = sin(theta); + double ecosthetamtheta0 = costheta*d.ecostheta0 + sintheta*d.esintheta0; + double esinthetamtheta0 = sintheta*d.ecostheta0 - costheta*d.esintheta0; + double oneplus = 1.0 + d.ecostheta0*d.ecostheta0 + d.esintheta0*d.esintheta0; + double fact = esinthetamtheta0*(1.0 - ecosthetamtheta0)/(oneplus - 2*ecosthetamtheta0); +// fact is sin(a)*cos(a) where a is the angle between the ray from the first +// focus and the normal to the conic. We need it in order to adjust the +// angle according to the projection onto the conic of our point + double rho1 = d.pdimen / (1 - ecosthetamtheta0); + double rho2 = - d.pdimen / (1 + ecosthetamtheta0); + if (fabs(rho1 - l) < fabs(rho2 - l)) + { + theta += (rho1 - l)*fact/rho1; + return fmod(theta / ( 2 * M_PI ) + 1, 1); + } else { + theta += (rho2 - l)*fact/rho2; + return fmod(theta / ( 2 * M_PI ) + 0.5, 1); + } +} + +const Coordinate ConicImp::getPoint( double p, const KigDocument& ) const +{ + const ConicPolarData d = polarData(); + + double costheta = cos(p * 2 * M_PI); + double sintheta = sin(p * 2 * M_PI); + double rho = d.pdimen / (1 - costheta* d.ecostheta0 - sintheta* d.esintheta0); + return d.focus1 + Coordinate (costheta, sintheta) * rho; +} + +int ConicImp::conicType() const +{ + const ConicPolarData d = polarData(); + double ec = d.ecostheta0; + double es = d.esintheta0; + double esquare = ec*ec + es*es; + const double parabolamiss = 1e-3; // don't know what a good value could be + + if (esquare < 1.0 - parabolamiss) return 1; + if (esquare > 1.0 + parabolamiss) return -1; + + return 0; +} + +QString ConicImp::conicTypeString() const +{ + switch (conicType()) + { + case 1: + return i18n("Ellipse"); + case -1: + return i18n("Hyperbola"); + case 0: + return i18n("Parabola"); + default: + assert( false ); + return ""; + } +} + +QString ConicImp::cartesianEquationString( const KigDocument& ) const +{ + QString ret = i18n( "%1 x² + %2 y² + %3 xy + %4 x + %5 y + %6 = 0" ); + ConicCartesianData data = cartesianData(); + ret = ret.arg( data.coeffs[0], 0, 'g', 3 ); + ret = ret.arg( data.coeffs[1], 0, 'g', 3 ); + ret = ret.arg( data.coeffs[2], 0, 'g', 3 ); + ret = ret.arg( data.coeffs[3], 0, 'g', 3 ); + ret = ret.arg( data.coeffs[4], 0, 'g', 3 ); + ret = ret.arg( data.coeffs[5], 0, 'g', 3 ); + return ret; +} + +QString ConicImp::polarEquationString( const KigDocument& w ) const +{ + QString ret = i18n( "rho = %1/(1 + %2 cos theta + %3 sin theta)\n [centered at %4]" ); + const ConicPolarData data = polarData(); + + ret = ret.arg( data.pdimen, 0, 'g', 3 ); + ret = ret.arg( -data.ecostheta0, 0, 'g', 3 ); + ret = ret.arg( -data.esintheta0, 0, 'g', 3 ); + + ret = ret.arg( w.coordinateSystem().fromScreen( data.focus1, w ) ); + return ret; +} + +const ConicCartesianData ConicImp::cartesianData() const +{ + return ConicCartesianData( polarData() ); +} + +Coordinate ConicImp::focus1() const +{ + return polarData().focus1; +} + +Coordinate ConicImp::focus2() const +{ + const ConicPolarData d = polarData(); + double ec = d.ecostheta0; + double es = d.esintheta0; + + double fact = 2*d.pdimen/(1 - ec*ec - es*es); + + return d.focus1 + fact*Coordinate(ec, es); +} + +const ConicPolarData ConicImpCart::polarData() const +{ + return mpolardata; +} + +const ConicCartesianData ConicImpCart::cartesianData() const +{ + return mcartdata; +} + +ConicImpCart::ConicImpCart( const ConicCartesianData& data ) + : ConicImp(), mcartdata( data ), mpolardata( data ) +{ + assert( data.valid() ); +} + +ConicImpPolar::ConicImpPolar( const ConicPolarData& data ) + : ConicImp(), mdata( data ) +{ +} + +ConicImpPolar::~ConicImpPolar() +{ +} + +const ConicPolarData ConicImpPolar::polarData() const +{ + return mdata; +} + +ConicImpCart* ConicImpCart::copy() const +{ + return new ConicImpCart( mcartdata ); +} + +ConicImpPolar* ConicImpPolar::copy() const +{ + return new ConicImpPolar( mdata ); +} + +ConicImp::ConicImp() +{ +} + +ConicImp::~ConicImp() +{ +} + +ConicImpCart::~ConicImpCart() +{ +} + +void ConicImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +bool ConicImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( ConicImp::stype() ) && + static_cast<const ConicImp&>( rhs ).polarData() == polarData(); +} + +const ObjectImpType* ConicImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "conic", + I18N_NOOP( "conic" ), + I18N_NOOP( "Select this conic" ), + I18N_NOOP( "Select conic %1" ), + I18N_NOOP( "Remove a Conic" ), + I18N_NOOP( "Add a Conic" ), + I18N_NOOP( "Move a Conic" ), + I18N_NOOP( "Attach to this conic" ), + I18N_NOOP( "Show a Conic" ), + I18N_NOOP( "Hide a Conic" ) + ); + return &t; +} + +const ObjectImpType* ConicImp::type() const +{ + return ConicImp::stype(); +} + +bool ConicImp::containsPoint( const Coordinate& p, const KigDocument& ) const +{ + const ConicPolarData d = polarData(); + +// the threshold is relative to the size of the conic (mp) + return internalContainsPoint( p, test_threshold*d.pdimen ); +} + +bool ConicImp::internalContainsPoint( const Coordinate& p, double threshold ) const +{ + const ConicPolarData d = polarData(); + + Coordinate focus1 = d.focus1; + double ecostheta0 = d.ecostheta0; + double esintheta0 = d.esintheta0; + double pdimen = d.pdimen; + + Coordinate pos = p - focus1; + double len = pos.length(); + double costheta = pos.x / len; + double sintheta = pos.y / len; + + double ecosthetamtheta0 = costheta*ecostheta0 + sintheta*esintheta0; + double rho = pdimen / (1.0 - ecosthetamtheta0); + + double oneplus = 1.0 + ecostheta0*ecostheta0 + esintheta0*esintheta0; + +// fact is the cosine of the angle between the ray from the first focus +// and the normal to the conic, so that we compute the real distance + + double fact = (1.0 - ecosthetamtheta0)/sqrt(oneplus - 2*ecosthetamtheta0); + if ( fabs((len - rho)*fact) <= threshold ) return true; + rho = - pdimen / ( 1.0 + ecosthetamtheta0 ); + fact = (1.0 + ecosthetamtheta0)/sqrt(oneplus + 2*ecosthetamtheta0); + return fabs(( len - rho )*fact) <= threshold; +} + +bool ConicImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::isPropertyDefinedOnOrThroughThisImp( which ); + return false; +} + +Rect ConicImp::surroundingRect() const +{ + // it's prolly possible to calculate this ( in the case that the + // conic is limited in size ), but for now we don't. + + return Rect::invalidRect(); +} diff --git a/kig/objects/conic_imp.h b/kig/objects/conic_imp.h new file mode 100644 index 00000000..55ba65ca --- /dev/null +++ b/kig/objects/conic_imp.h @@ -0,0 +1,157 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_CONIC_IMP_H +#define KIG_OBJECTS_CONIC_IMP_H + +#include "curve_imp.h" + +#include "../misc/conic-common.h" + +/** + * An ObjectImp representing a conic. + * + * A conic is a general second degree curve, and some beautiful theory + * has been developed about it.. See a math book for more + * information. This class is in fact an abstract base class hiding + * the fact that a ConicImp can be constructed in two ways. If only + * its Cartesian equation is known, then you should use ConicImpCart, + * otherwise, you should use ConicImpPolar. If the other + * representation is needed, it will be calculated, but a cartesian + * representation is rarely needed, and not calculating saves some CPU + * cycles. + */ +class ConicImp + : public CurveImp +{ +protected: + ConicImp(); + ~ConicImp(); +public: + typedef CurveImp Parent; + /** + * Returns the ObjectImpType representing the ConicImp type. + */ + static const ObjectImpType* stype(); + + ObjectImp* transform( const Transformation& ) const; + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& ) const; + bool inRect( const Rect& r, int width, const KigWidget& ) const; + bool valid() const; + Rect surroundingRect() const; + + const uint numberOfProperties() const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + const char* iconForProperty( uint which ) const; + ObjectImp* property( uint which, const KigDocument& w ) const; + + double getParam( const Coordinate& point, const KigDocument& ) const; + const Coordinate getPoint( double param, const KigDocument& ) const; + + // information about ourselves.. These are all virtual, because a + // trivial subclass like CircleImp can override these with trivial + // versions.. + + /** + * Type of conic. + * Return what type of conic this is: + * -1 for a hyperbola + * 0 for a parabola + * 1 for an ellipse + */ + virtual int conicType() const; + /** + * A string containing "Hyperbola", "Parabola" or "Ellipse". + */ + virtual QString conicTypeString() const; + /** + * A string containing the cartesian equation of the conic. This + * will be of the form "a x^2 + b y^2 + c xy + d x + e y + f = 0". + */ + virtual QString cartesianEquationString( const KigDocument& w ) const; + /** + * A string containing the polar equation of the conic. This will + * be of the form "rho = pdimen/(1 + ect cos( theta ) + est sin( + * theta ) )\n [centered at p]" + */ + virtual QString polarEquationString( const KigDocument& w ) const; + /** + * Return the cartesian representation of this conic. + */ + virtual const ConicCartesianData cartesianData() const; + /** + * Return the polar representation of this conic. + */ + virtual const ConicPolarData polarData() const = 0; + /** + * Return the first focus of this conic. + */ + virtual Coordinate focus1() const; + /** + * Return the second focus of this conic. + */ + virtual Coordinate focus2() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool equals( const ObjectImp& rhs ) const; + + bool containsPoint( const Coordinate& p, const KigDocument& doc ) const; + bool internalContainsPoint( const Coordinate& p, double threshold ) const; +}; + +/** + * An implementation of ConicImp to be used when only the cartesian + * equation of the conic is known. + */ +class ConicImpCart + : public ConicImp +{ + ConicCartesianData mcartdata; + ConicPolarData mpolardata; +public: + ConicImpCart( const ConicCartesianData& data ); + ~ConicImpCart(); + ConicImpCart* copy() const; + + const ConicCartesianData cartesianData() const; + const ConicPolarData polarData() const; +}; + +/** + * An implementation of ConicImp to be used when only the cartesian + * equation of the conic is known. + */ +class ConicImpPolar + : public ConicImp +{ + ConicPolarData mdata; +public: + ConicImpPolar( const ConicPolarData& data ); + ~ConicImpPolar(); + ConicImpPolar* copy() const; + + const ConicPolarData polarData() const; +}; + +#endif diff --git a/kig/objects/conic_types.cc b/kig/objects/conic_types.cc new file mode 100644 index 00000000..6e32f844 --- /dev/null +++ b/kig/objects/conic_types.cc @@ -0,0 +1,689 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "conic_types.h" + +#include "bogus_imp.h" +#include "conic_imp.h" +#include "point_imp.h" +#include "circle_imp.h" +#include "line_imp.h" +#include "object_calcer.h" +#include "../misc/conic-common.h" +#include "../misc/common.h" +#include "../kig/kig_commands.h" +#include "../kig/kig_part.h" + +#include <klocale.h> + +static const char conic_constructstatement[] = I18N_NOOP( "Construct a conic through this point" ); + +static const struct ArgsParser::spec argsspecConicB5P[] = +{ + { PointImp::stype(), conic_constructstatement, + I18N_NOOP( "Select a point for the new conic to go through..." ), true }, + { PointImp::stype(), conic_constructstatement, + I18N_NOOP( "Select a point for the new conic to go through..." ), true }, + { PointImp::stype(), conic_constructstatement, + I18N_NOOP( "Select a point for the new conic to go through..." ), true }, + { PointImp::stype(), conic_constructstatement, + I18N_NOOP( "Select a point for the new conic to go through..." ), true }, + { PointImp::stype(), conic_constructstatement, + I18N_NOOP( "Select a point for the new conic to go through..." ),true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConicB5PType ) + +ConicB5PType::ConicB5PType() + : ArgsParserObjectType( "ConicB5P", argsspecConicB5P, 5 ) +{ +} + +ConicB5PType::~ConicB5PType() +{ +} + +ObjectImp* ConicB5PType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents, 1 ) ) return new InvalidImp; + std::vector<Coordinate> points; + + for ( Args::const_iterator i = parents.begin(); i != parents.end(); ++i ) + points.push_back( static_cast<const PointImp*>( *i )->coordinate() ); + + ConicCartesianData d = + calcConicThroughPoints( points, zerotilt, parabolaifzt, ysymmetry ); + if ( d.valid() ) + return new ConicImpCart( d ); + else return new InvalidImp; +} + +const ConicB5PType* ConicB5PType::instance() +{ + static const ConicB5PType t; + return &t; +} + +static const ArgsParser::spec argsspecConicBAAP[] = +{ + { AbstractLineImp::stype(), I18N_NOOP( "Construct a conic with this asymptote" ), + I18N_NOOP( "Select the first asymptote of the new conic..." ), false }, + { AbstractLineImp::stype(), I18N_NOOP( "Construct a conic with this asymptote" ), + I18N_NOOP( "Select the second asymptote of the new conic..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct a conic through this point" ), + I18N_NOOP( "Select a point for the new conic to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConicBAAPType ) + +ConicBAAPType::ConicBAAPType() + : ArgsParserObjectType( "ConicBAAP", argsspecConicBAAP, 3 ) +{ +} + +ConicBAAPType::~ConicBAAPType() +{ +} + +const ConicBAAPType* ConicBAAPType::instance() +{ + static const ConicBAAPType t; + return &t; +} + +ObjectImp* ConicBAAPType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) + return new InvalidImp; + const LineData la = static_cast<const AbstractLineImp*>( parents[0] )->data(); + const LineData lb = static_cast<const AbstractLineImp*>( parents[1] )->data(); + const Coordinate c = static_cast<const PointImp*>( parents[2] )->coordinate(); + + return new ConicImpCart( calcConicByAsymptotes( la, lb, c ) ); +} + +ObjectImp* ConicBFFPType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents, 2 ) ) return new InvalidImp; + std::vector<Coordinate> cs; + + for ( Args::const_iterator i = parents.begin(); i != parents.end(); ++i ) + cs.push_back( static_cast<const PointImp*>( *i )->coordinate() ); + + return new ConicImpPolar( calcConicBFFP( cs, type() ) ); +} + +ConicBFFPType::ConicBFFPType( const char* fullname, const ArgsParser::spec* spec, int n ) + : ArgsParserObjectType( fullname, spec, n ) +{ +} + +ConicBFFPType::~ConicBFFPType() +{ +} + +static const char constructellipsewithfocusstat[] = + I18N_NOOP( "Construct an ellipse with this focus" ); + +static const ArgsParser::spec argsspecEllipseBFFP[] = +{ + { PointImp::stype(), constructellipsewithfocusstat, + I18N_NOOP( "Select the first focus of the new ellipse..." ), false }, + { PointImp::stype(), constructellipsewithfocusstat, + I18N_NOOP( "Select the second focus of the new ellipse..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct an ellipse through this point" ), + I18N_NOOP( "Select a point for the new ellipse to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( EllipseBFFPType ) + +EllipseBFFPType::EllipseBFFPType() + : ConicBFFPType( "EllipseBFFP", argsspecEllipseBFFP, 3 ) +{ +} + +EllipseBFFPType::~EllipseBFFPType() +{ +} + +int EllipseBFFPType::type() const +{ + return 1; +} + +const EllipseBFFPType* EllipseBFFPType::instance() +{ + static const EllipseBFFPType t; + return &t; +} + +static const char constructhyperbolawithfocusstat[] = + I18N_NOOP( "Construct a hyperbola with this focus" ); + +static const ArgsParser::spec argsspecHyperbolaBFFP[] = +{ + { PointImp::stype(), constructhyperbolawithfocusstat, + I18N_NOOP( "Select the first focus of the new hyperbola..." ), false }, + { PointImp::stype(), constructhyperbolawithfocusstat, + I18N_NOOP( "Select the second focus of the new hyperbola..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct a hyperbola through this point" ), + I18N_NOOP( "Select a point for the new hyperbola to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( HyperbolaBFFPType ) + +HyperbolaBFFPType::HyperbolaBFFPType() + : ConicBFFPType( "HyperbolaBFFP", argsspecHyperbolaBFFP, 3 ) +{ +} + +HyperbolaBFFPType::~HyperbolaBFFPType() +{ +} + +const HyperbolaBFFPType* HyperbolaBFFPType::instance() +{ + static const HyperbolaBFFPType t; + return &t; +} + +int HyperbolaBFFPType::type() const +{ + return -1; +} + +const ConicBDFPType* ConicBDFPType::instance() +{ + static const ConicBDFPType t; + return &t; +} + +static const struct ArgsParser::spec argsspecConicBDFP[] = +{ + { AbstractLineImp::stype(), I18N_NOOP( "Construct a conic with this line as directrix" ), + I18N_NOOP( "Select the directrix of the new conic..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct a conic with this point as focus" ), + I18N_NOOP( "Select the focus of the new conic..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct a conic through this point" ), + I18N_NOOP( "Select a point for the new conic to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConicBDFPType ) + +ConicBDFPType::ConicBDFPType() + : ArgsParserObjectType( "ConicBDFP", argsspecConicBDFP, 3 ) +{ +} + +ConicBDFPType::~ConicBDFPType() +{ +} + +ObjectImp* ConicBDFPType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents, 2 ) ) return new InvalidImp; + + const LineData line = static_cast<const AbstractLineImp*>( parents[0] )->data(); + const Coordinate focus = + static_cast<const PointImp*>( parents[1] )->coordinate(); + + Coordinate point; + if ( parents.size() == 3 ) + point = static_cast<const PointImp*>( parents[2] )->coordinate(); + else + { + /* !!!! costruisci point come punto medio dell'altezza tra fuoco e d. */ + Coordinate ba = line.dir(); + Coordinate fa = focus - line.b; + double balsq = ba.x*ba.x + ba.y*ba.y; + double scal = (fa.x*ba.x + fa.y*ba.y)/balsq; + point = 0.5*(line.a + focus + scal*ba); + }; + return new ConicImpPolar( calcConicBDFP( line, focus, point ) ); +} + +static const char constructparabolathroughpointstat[] = + I18N_NOOP( "Construct a parabola through this point" ); + +static const ArgsParser::spec argsspecParabolaBTP[] = +{ + { PointImp::stype(), constructparabolathroughpointstat, + I18N_NOOP( "Select a point for the new parabola to go through..." ), true }, + { PointImp::stype(), constructparabolathroughpointstat, + I18N_NOOP( "Select a point for the new parabola to go through..." ), true }, + { PointImp::stype(), constructparabolathroughpointstat, + I18N_NOOP( "Select a point for the new parabola to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ParabolaBTPType ) + +ParabolaBTPType::ParabolaBTPType() + : ArgsParserObjectType( "ParabolaBTP", argsspecParabolaBTP, 3 ) +{ +} + +ParabolaBTPType::~ParabolaBTPType() +{ +} + +const ParabolaBTPType* ParabolaBTPType::instance() +{ + static const ParabolaBTPType t; + return &t; +} + +ObjectImp* ParabolaBTPType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents, 2 ) ) return new InvalidImp; + + std::vector<Coordinate> points; + for ( Args::const_iterator i = parents.begin(); i != parents.end(); ++i ) + points.push_back( static_cast<const PointImp*>( *i )->coordinate() ); + + ConicCartesianData d = + calcConicThroughPoints( points, zerotilt, parabolaifzt, ysymmetry ); + if ( d.valid() ) + return new ConicImpCart( d ); + else + return new InvalidImp; +} + +static const ArgsParser::spec argsspecConicPolarPoint[] = +{ + { ConicImp::stype(), I18N_NOOP( "Construct a polar point wrt. this conic" ), + I18N_NOOP( "Select the conic wrt. which you want to construct a polar point..." ), false }, + { AbstractLineImp::stype(), I18N_NOOP( "Construct the polar point of this line" ), + I18N_NOOP( "Select the line of which you want to construct the polar point..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConicPolarPointType ) + +ConicPolarPointType::ConicPolarPointType() + : ArgsParserObjectType( "ConicPolarPoint", argsspecConicPolarPoint, 2 ) +{ +} + +ConicPolarPointType::~ConicPolarPointType() +{ +} + +const ConicPolarPointType* ConicPolarPointType::instance() +{ + static const ConicPolarPointType t; + return &t; +} + +ObjectImp* ConicPolarPointType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + const ConicCartesianData c = static_cast<const ConicImp*>( parents[0] )->cartesianData(); + const LineData l = static_cast<const AbstractLineImp*>( parents[1] )->data(); + const Coordinate p = calcConicPolarPoint( c, l ); + if ( p.valid() ) return new PointImp( p ); + else return new InvalidImp; +} + +static const ArgsParser::spec argsspecConicPolarLine[] = +{ + { ConicImp::stype(), I18N_NOOP( "Construct a polar line wrt. this conic" ), + I18N_NOOP( "Select the conic wrt. which you want to construct a polar point..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct the polar line of this point" ), + I18N_NOOP( "Select the line of which you want to construct the polar point..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConicPolarLineType ) + +ConicPolarLineType::ConicPolarLineType() + : ArgsParserObjectType( "ConicPolarLine", argsspecConicPolarLine, 2 ) +{ +} + +ConicPolarLineType::~ConicPolarLineType() +{ +} + +const ConicPolarLineType* ConicPolarLineType::instance() +{ + static const ConicPolarLineType t; + return &t; +} + +ObjectImp* ConicPolarLineType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + const ConicCartesianData c = static_cast<const ConicImp*>( parents[0] )->cartesianData(); + const Coordinate p = static_cast<const PointImp*>( parents[1] )->coordinate(); + bool valid = true; + const LineData l = calcConicPolarLine( c, p, valid ); + if ( valid ) return new LineImp( l ); + else return new InvalidImp; +} + +static const ArgsParser::spec argsspecConicDirectrix[] = +{ + { ConicImp::stype(), I18N_NOOP( "Construct the directrix of this conic" ), + I18N_NOOP( "Select the conic of which you want to construct the directrix..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConicDirectrixType ) + +ConicDirectrixType::ConicDirectrixType() + : ArgsParserObjectType( "ConicDirectrix", argsspecConicDirectrix, 1 ) +{ +} + +ConicDirectrixType::~ConicDirectrixType() +{ +} + +const ConicDirectrixType* ConicDirectrixType::instance() +{ + static const ConicDirectrixType t; + return &t; +} + +ObjectImp* ConicDirectrixType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + const ConicPolarData data = + static_cast<const ConicImp*>( parents[0] )->polarData(); + + double ec = data.ecostheta0; + double es = data.esintheta0; + double eccsq = ec*ec + es*es; + + Coordinate a = data.focus1 - data.pdimen/eccsq*Coordinate(ec,es); + Coordinate b = a + Coordinate(-es,ec); + return new LineImp( a, b ); +} + +static const char hyperbolatpstatement[] = I18N_NOOP( "Construct a hyperbola through this point" ); + +static const ArgsParser::spec argsspecHyperbolaB4P[] = +{ + { PointImp::stype(), hyperbolatpstatement, + I18N_NOOP( "Select a point for the new hyperbola to go through..." ), true }, + { PointImp::stype(), hyperbolatpstatement, + I18N_NOOP( "Select a point for the new hyperbola to go through..." ), true }, + { PointImp::stype(), hyperbolatpstatement, + I18N_NOOP( "Select a point for the new hyperbola to go through..." ), true }, + { PointImp::stype(), hyperbolatpstatement, + I18N_NOOP( "Select a point for the new hyperbola to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( EquilateralHyperbolaB4PType ) + +EquilateralHyperbolaB4PType::EquilateralHyperbolaB4PType() + : ArgsParserObjectType( "EquilateralHyperbolaB4P", argsspecHyperbolaB4P, 4 ) +{ +} + +EquilateralHyperbolaB4PType::~EquilateralHyperbolaB4PType() +{ +} + +const EquilateralHyperbolaB4PType* EquilateralHyperbolaB4PType::instance() +{ + static const EquilateralHyperbolaB4PType t; + return &t; +} + +ObjectImp* EquilateralHyperbolaB4PType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents, 1 ) ) return new InvalidImp; + + std::vector<Coordinate> pts; + for ( Args::const_iterator i = parents.begin(); i != parents.end(); ++i ) + pts.push_back( static_cast<const PointImp*>( *i )->coordinate() ); + + ConicCartesianData d = calcConicThroughPoints( pts, equilateral ); + if ( d.valid() ) + return new ConicImpCart( d ); + else + return new InvalidImp; +} + +static const ArgsParser::spec argsspecParabolaBDP[] = +{ + { AbstractLineImp::stype(), I18N_NOOP( "Construct a parabola with this directrix" ), + I18N_NOOP( "Select the directrix of the new parabola..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct a parabola with this focus" ), + I18N_NOOP( "Select the focus of the new parabola..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ParabolaBDPType ) + +ParabolaBDPType::ParabolaBDPType() + : ObjectLPType( "ParabolaBDP", argsspecParabolaBDP, 2 ) +{ +} + +ParabolaBDPType::~ParabolaBDPType() +{ +} + +const ParabolaBDPType* ParabolaBDPType::instance() +{ + static const ParabolaBDPType t; + return &t; +} + +ObjectImp* ParabolaBDPType::calc( const LineData& l, const Coordinate& c ) const +{ + ConicPolarData ret; + Coordinate ldir = l.dir(); + ldir = ldir.normalize(); + ret.focus1 = c; + ret.ecostheta0 = - ldir.y; + ret.esintheta0 = ldir.x; + Coordinate fa = c - l.a; + ret.pdimen = fa.y*ldir.x - fa.x*ldir.y; + ConicImpPolar* r = new ConicImpPolar( ret ); + kdDebug() << k_funcinfo << r->conicTypeString() << endl; + return r; +} + +static const ArgsParser::spec argsspecConicAsymptote[] = +{ + { ConicImp::stype(), I18N_NOOP( "Construct the asymptotes of this conic" ), + I18N_NOOP( "Select the conic of which you want to construct the asymptotes..." ), false }, + { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConicAsymptoteType ) + +ConicAsymptoteType::ConicAsymptoteType() + : ArgsParserObjectType( "ConicAsymptote", argsspecConicAsymptote, 2 ) +{ +} + +ConicAsymptoteType::~ConicAsymptoteType() +{ +} + +const ConicAsymptoteType* ConicAsymptoteType::instance() +{ + static const ConicAsymptoteType t; + return &t; +} + +ObjectImp* ConicAsymptoteType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + bool valid = true; + const LineData ret = calcConicAsymptote( + static_cast<const ConicImp*>( parents[0] )->cartesianData(), + static_cast<const IntImp*>( parents[1] )->data(), + valid ); + + if ( valid ) + return new LineImp( ret ); + else + return new InvalidImp; +} + +static const char radicallinesstatement[] = I18N_NOOP( "Construct the radical lines of this conic" ); + +static const ArgsParser::spec argsspecConicRadical[] = +{ + { ConicImp::stype(), radicallinesstatement, + I18N_NOOP( "Select the first of the two conics of which you want to construct the radical line..." ), false }, + { ConicImp::stype(), radicallinesstatement, + I18N_NOOP( "Select the other of the two conic of which you want to construct the radical line..." ), false }, + { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false }, + { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConicRadicalType ) + +ConicRadicalType::ConicRadicalType() + : ArgsParserObjectType( "ConicRadical", argsspecConicRadical, 4 ) +{ +} + +const ConicRadicalType* ConicRadicalType::instance() +{ + static const ConicRadicalType t; + return &t; +} + +ObjectImp* ConicRadicalType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + if ( parents[0]->inherits( CircleImp::stype() ) && + parents[1]->inherits( CircleImp::stype() ) ) + { + if( static_cast<const IntImp*>( parents[2] )->data() != 1 ) + return new InvalidImp; + else + { + const CircleImp* c1 = static_cast<const CircleImp*>( parents[0] ); + const CircleImp* c2 = static_cast<const CircleImp*>( parents[1] ); + const Coordinate a = calcCircleRadicalStartPoint( + c1->center(), c2->center(), c1->squareRadius(), c2->squareRadius() + ); + return new LineImp( a, calcPointOnPerpend( + LineData( c1->center(), c2->center() ), a ) ); + }; + } + else + { + bool valid = true; + const LineData l = calcConicRadical( + static_cast<const ConicImp*>( parents[0] )->cartesianData(), + static_cast<const ConicImp*>( parents[1] )->cartesianData(), + static_cast<const IntImp*>( parents[2] )->data(), + static_cast<const IntImp*>( parents[3] )->data(), valid ); + if ( valid ) + return new LineImp( l ); + else + return new InvalidImp; + }; +} + +ConicRadicalType::~ConicRadicalType() +{ +} + +const ObjectImpType* ConicB5PType::resultId() const +{ + return ConicImp::stype(); +} + +const ObjectImpType* ConicBAAPType::resultId() const +{ + return ConicImp::stype(); +} + +const ObjectImpType* ConicBFFPType::resultId() const +{ + return ConicImp::stype(); +} + +const ObjectImpType* ConicBDFPType::resultId() const +{ + return ConicImp::stype(); +} + +const ObjectImpType* ParabolaBTPType::resultId() const +{ + return ConicImp::stype(); +} + +const ObjectImpType* EquilateralHyperbolaB4PType::resultId() const +{ + return ConicImp::stype(); +} + +const ObjectImpType* ConicPolarPointType::resultId() const +{ + return PointImp::stype(); +} + +const ObjectImpType* ConicPolarLineType::resultId() const +{ + return LineImp::stype(); +} + +const ObjectImpType* ConicDirectrixType::resultId() const +{ + return LineImp::stype(); +} + +const ObjectImpType* ParabolaBDPType::resultId() const +{ + return ConicImp::stype(); +} + +const ObjectImpType* ConicAsymptoteType::resultId() const +{ + return LineImp::stype(); +} + +const ObjectImpType* ConicRadicalType::resultId() const +{ + return LineImp::stype(); +} + +QStringList ConicRadicalType::specialActions() const +{ + QStringList ret; + ret << i18n( "Switch Radical Lines" ); + return ret; +} + +void ConicRadicalType::executeAction( int i, ObjectHolder&, ObjectTypeCalcer& t, + KigPart& d, KigWidget&, NormalMode& ) const +{ + assert( i == 0 ); + std::vector<ObjectCalcer*> parents = t.parents(); + assert( dynamic_cast<ObjectConstCalcer*>( parents[3] ) ); + ObjectConstCalcer* zeroindexo = static_cast<ObjectConstCalcer*>( parents[3] ); + MonitorDataObjects mon( zeroindexo ); + assert( zeroindexo->imp()->inherits( IntImp::stype() ) ); + int oldzeroindex = static_cast<const IntImp*>( zeroindexo->imp() )->data(); + int newzeroindex = oldzeroindex % 3 + 1; + zeroindexo->setImp( new IntImp( newzeroindex ) ); + KigCommand* kc = new KigCommand( d, "Switch Conic Radical Lines" ); + mon.finish( kc ); + d.history()->addCommand( kc ); +} + diff --git a/kig/objects/conic_types.h b/kig/objects/conic_types.h new file mode 100644 index 00000000..e2a1881d --- /dev/null +++ b/kig/objects/conic_types.h @@ -0,0 +1,184 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_CONIC_TYPES_H +#define KIG_OBJECTS_CONIC_TYPES_H + +#include "base_type.h" + +class ConicB5PType + : public ArgsParserObjectType +{ + ConicB5PType(); + ~ConicB5PType(); +public: + static const ConicB5PType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ConicBAAPType + : public ArgsParserObjectType +{ + ConicBAAPType(); + ~ConicBAAPType(); +public: + static const ConicBAAPType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ConicBFFPType + : public ArgsParserObjectType +{ +protected: + ConicBFFPType( const char* fullname, const ArgsParser::spec*, int n ); + ~ConicBFFPType(); +public: + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + + /** + * -1 for hyperbola, 1 for ellipse.. + */ + virtual int type() const = 0; + const ObjectImpType* resultId() const; +}; + +class EllipseBFFPType + : public ConicBFFPType +{ + EllipseBFFPType(); + ~EllipseBFFPType(); +public: + static const EllipseBFFPType* instance(); + int type() const; +}; + +class HyperbolaBFFPType + : public ConicBFFPType +{ + HyperbolaBFFPType(); + ~HyperbolaBFFPType(); +public: + static const HyperbolaBFFPType* instance(); + int type() const; +}; + +class ConicBDFPType + : public ArgsParserObjectType +{ + ConicBDFPType(); + ~ConicBDFPType(); +public: + static const ConicBDFPType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ParabolaBTPType + : public ArgsParserObjectType +{ + ParabolaBTPType(); + ~ParabolaBTPType(); +public: + static const ParabolaBTPType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class EquilateralHyperbolaB4PType + : public ArgsParserObjectType +{ + EquilateralHyperbolaB4PType(); + ~EquilateralHyperbolaB4PType(); +public: + static const EquilateralHyperbolaB4PType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ConicPolarPointType + : public ArgsParserObjectType +{ + ConicPolarPointType(); + ~ConicPolarPointType(); +public: + static const ConicPolarPointType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ConicPolarLineType + : public ArgsParserObjectType +{ + ConicPolarLineType(); + ~ConicPolarLineType(); +public: + static const ConicPolarLineType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ConicDirectrixType + : public ArgsParserObjectType +{ + ConicDirectrixType(); + ~ConicDirectrixType(); +public: + static const ConicDirectrixType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ParabolaBDPType + : public ObjectLPType +{ + ParabolaBDPType(); + ~ParabolaBDPType(); +public: + static const ParabolaBDPType* instance(); + ObjectImp* calc( const LineData& l, const Coordinate& c ) const; + const ObjectImpType* resultId() const; +}; + +class ConicAsymptoteType + : public ArgsParserObjectType +{ + ConicAsymptoteType(); + ~ConicAsymptoteType(); +public: + static const ConicAsymptoteType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ConicRadicalType + : public ArgsParserObjectType +{ + ConicRadicalType(); + ~ConicRadicalType(); +public: + static const ConicRadicalType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; + QStringList specialActions() const; + void executeAction( int i, ObjectHolder& o, ObjectTypeCalcer& t, + KigPart& d, KigWidget& w, NormalMode& m ) const; +}; + +#endif + diff --git a/kig/objects/cubic_imp.cc b/kig/objects/cubic_imp.cc new file mode 100644 index 00000000..51bb5d9f --- /dev/null +++ b/kig/objects/cubic_imp.cc @@ -0,0 +1,437 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "cubic_imp.h" + +#include "bogus_imp.h" + +#include "../misc/kigpainter.h" +#include "../misc/screeninfo.h" +#include "../misc/kignumerics.h" +#include "../misc/common.h" +#include "../kig/kig_view.h" + +#include <math.h> +#include <klocale.h> + +CubicImp::CubicImp( const CubicCartesianData& data ) + : CurveImp(), mdata( data ) +{ +} + +CubicImp::~CubicImp() +{ +} + +ObjectImp* CubicImp::transform( const Transformation& t ) const +{ + bool valid = true; + CubicCartesianData d = calcCubicTransformation( data(), t, valid ); + if ( valid ) return new CubicImp( d ); + else return new InvalidImp; +} + +void CubicImp::draw( KigPainter& p ) const +{ + p.drawCurve( this ); +} + +bool CubicImp::contains( const Coordinate& o, int width, const KigWidget& w ) const +{ + return internalContainsPoint( o, w.screenInfo().normalMiss( width ) ); +} + +bool CubicImp::inRect( const Rect&, int, const KigWidget& ) const +{ + // TODO ? + return false; +} + +CubicImp* CubicImp::copy() const +{ + return new CubicImp( mdata ); +} + +double CubicImp::getParam( const Coordinate& p, const KigDocument& ) const +{ + double x = p.x; + double y = p.y; + double t; + + double a000 = mdata.coeffs[0]; + double a001 = mdata.coeffs[1]; + double a002 = mdata.coeffs[2]; + double a011 = mdata.coeffs[3]; + double a012 = mdata.coeffs[4]; + double a022 = mdata.coeffs[5]; + double a111 = mdata.coeffs[6]; + double a112 = mdata.coeffs[7]; + double a122 = mdata.coeffs[8]; + double a222 = mdata.coeffs[9]; + + /* + * first project p onto the cubic. This is done by computing the + * line through p in the direction of the gradient + */ + + double f = a000 + a001*x + a002*y + a011*x*x + a012*x*y + a022*y*y + + a111*x*x*x + a112*x*x*y + a122*x*y*y + a222*y*y*y; + if ( f != 0 ) + { + double fx = a001 + 2*a011*x + a012*y + 3*a111*x*x + 2*a112*x*y + a122*y*y; + double fy = a002 + 2*a022*y + a012*x + 3*a222*y*y + 2*a122*x*y + a112*x*x; + Coordinate v = Coordinate (fx, fy); + if ( f < 0 ) v = -v; // the line points away from the intersection + double a, b, c, d; + calcCubicLineRestriction ( mdata, p, v, a, b, c, d ); + if ( a < 0 ) + { + a *= -1; + b *= -1; + c *= -1; + d *= -1; + } + + // computing the coefficients of the Sturm sequence + double p1a = 2*b*b - 6*a*c; + double p1b = b*c - 9*a*d; + double p0a = c*p1a*p1a + p1b*(3*a*p1b - 2*b*p1a); + // compute the number of roots for negative lambda + int variations = calcCubicVariations ( 0, a, b, c, d, p1a, p1b, p0a ); + bool valid; + int numroots; + double lambda = calcCubicRoot ( -1e10, 1e10, a, b, c, d, variations, valid, + numroots ); + if ( valid ) + { + Coordinate pnew = p + lambda*v; + x = pnew.x; + y = pnew.y; + } + } + + if (x > 0) t = x/(1+x); + else t = x/(1-x); + t = 0.5*(t + 1); + t /= 3; + + Coordinate p1 = getPoint ( t ); + Coordinate p2 = getPoint ( t + 1.0/3.0 ); + Coordinate p3 = getPoint ( t + 2.0/3.0 ); + + double mint = t; + double mindist = p1.valid() ? fabs ( y - p1.y ) : double_inf; + if ( p2.valid() && fabs ( y - p2.y ) < mindist ) + { + mint = t + 1.0/3.0; + mindist = fabs ( y - p2.y ); + } + if ( p3.valid() && fabs ( y - p3.y ) < mindist ) + { + mint = t + 2.0/3.0; + } + + return mint; +} + +const Coordinate CubicImp::getPoint( double p, const KigDocument& ) const +{ + return getPoint( p ); +} + +const Coordinate CubicImp::getPoint( double p ) const +{ + /* + * this isn't really elegant... + * the magnitude of p tells which one of the maximum 3 intersections + * of the vertical line with the cubic to take. + */ + + p *= 3; + int root = (int) p; + assert ( root >= 0 ); + assert ( root <= 3 ); + if ( root == 3 ) root = 2; + + p -= root; + + assert ( 0 <= p && p <= 1 ); + if ( p <= 0. ) p = 1e-6; + if ( p >= 1. ) p = 1 - 1e-6; + root++; + p = 2*p - 1; + double x; + if (p > 0) x = p/(1 - p); + else x = p/(1 + p); + + // calc the third degree polynomial: + // compute the third degree polinomial: +// double a000 = mdata.coeffs[0]; +// double a001 = mdata.coeffs[1]; +// double a002 = mdata.coeffs[2]; +// double a011 = mdata.coeffs[3]; +// double a012 = mdata.coeffs[4]; +// double a022 = mdata.coeffs[5]; +// double a111 = mdata.coeffs[6]; +// double a112 = mdata.coeffs[7]; +// double a122 = mdata.coeffs[8]; +// double a222 = mdata.coeffs[9]; +// +// // first the y^3 coefficient, it coming only from a222: +// double a = a222; +// // next the y^2 coefficient (from a122 and a022): +// double b = a122*x + a022; +// // next the y coefficient (from a112, a012 and a002): +// double c = a112*x*x + a012*x + a002; +// // finally the constant coefficient (from a111, a011, a001 and a000): +// double d = a111*x*x*x + a011*x*x + a001*x + a000; + +// commented out, since the bound is already computed when passing a huge +// interval; the normalization is not needed also + + // renormalize: positive a +// if ( a < 0 ) +// { +// a *= -1; +// b *= -1; +// c *= -1; +// d *= -1; +// } +// +// const double small = 1e-7; +// int degree = 3; +// if ( fabs(a) < small*fabs(b) || +// fabs(a) < small*fabs(c) || +// fabs(a) < small*fabs(d) ) +// { +// degree = 2; +// if ( fabs(b) < small*fabs(c) || +// fabs(b) < small*fabs(d) ) +// { +// degree = 1; +// } +// } + +// and a bound for all the real roots: + +// double bound; +// switch (degree) +// { +// case 3: +// bound = fabs(d/a); +// if ( fabs(c/a) + 1 > bound ) bound = fabs(c/a) + 1; +// if ( fabs(b/a) + 1 > bound ) bound = fabs(b/a) + 1; +// break; +// +// case 2: +// bound = fabs(d/b); +// if ( fabs(c/b) + 1 > bound ) bound = fabs(c/b) + 1; +// break; +// +// case 1: +// default: +// bound = fabs(d/c) + 1; +// break; +// } + + int numroots; + bool valid = true; + double y = calcCubicYvalue ( x, -double_inf, double_inf, root, mdata, valid, + numroots ); + if ( ! valid ) return Coordinate::invalidCoord(); + else return Coordinate(x,y); +// if ( valid ) return Coordinate(x,y); +// root--; if ( root <= 0) root += 3; +// y = calcCubicYvalue ( x, -bound, bound, root, mdata, valid, +// numroots ); +// if ( valid ) return Coordinate(x,y); +// root--; if ( root <= 0) root += 3; +// y = calcCubicYvalue ( x, -bound, bound, root, mdata, valid, +// numroots ); +// assert ( valid ); +// return Coordinate(x,y); +} + +const uint CubicImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 1; +} + +const QCStringList CubicImp::propertiesInternalNames() const +{ + QCStringList l = Parent::propertiesInternalNames(); + l << "cartesian-equation"; + assert( l.size() == CubicImp::numberOfProperties() ); + return l; + +} + +/* + * cartesian equation property contributed by Carlo Sardi + */ + +const QCStringList CubicImp::properties() const +{ + QCStringList l = Parent::properties(); + l << I18N_NOOP( "Cartesian Equation" ); + assert( l.size() == CubicImp::numberOfProperties() ); + return l; + +} + +const ObjectImpType* CubicImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else return CubicImp::stype(); +} + +const char* CubicImp::iconForProperty( uint which ) const +{ + int pnum = 0; + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + if ( which == Parent::numberOfProperties() + pnum++ ) + return "kig_text"; // cartesian equation string + else + assert( false ); + return ""; +} + +ObjectImp* CubicImp::property( uint which, const KigDocument& w ) const +{ + int pnum = 0; + + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, w ); + if ( which == Parent::numberOfProperties() + pnum++ ) + return new StringImp( cartesianEquationString( w ) ); + else + assert( false ); + return new InvalidImp; +} + +const CubicCartesianData CubicImp::data() const +{ + return mdata; +} + +void CubicImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +bool CubicImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( CubicImp::stype() ) && + static_cast<const CubicImp&>( rhs ).data() == data(); +} + +const ObjectImpType* CubicImp::type() const +{ + return CubicImp::stype(); +} + +const ObjectImpType* CubicImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "cubic", + I18N_NOOP( "cubic curve" ), + I18N_NOOP( "Select this cubic curve" ), + I18N_NOOP( "Select cubic curve %1" ), + I18N_NOOP( "Remove a Cubic Curve" ), + I18N_NOOP( "Add a Cubic Curve" ), + I18N_NOOP( "Move a Cubic Curve" ), + I18N_NOOP( "Attach to this cubic curve" ), + I18N_NOOP( "Show a Cubic Curve" ), + I18N_NOOP( "Hide a Cubic Curve" ) + ); + return &t; +} + +bool CubicImp::containsPoint( const Coordinate& p, const KigDocument& ) const +{ + return internalContainsPoint( p, test_threshold ); +} + +bool CubicImp::internalContainsPoint( const Coordinate& p, double threshold ) const +{ + double a000 = mdata.coeffs[0]; + double a001 = mdata.coeffs[1]; + double a002 = mdata.coeffs[2]; + double a011 = mdata.coeffs[3]; + double a012 = mdata.coeffs[4]; + double a022 = mdata.coeffs[5]; + double a111 = mdata.coeffs[6]; + double a112 = mdata.coeffs[7]; + double a122 = mdata.coeffs[8]; + double a222 = mdata.coeffs[9]; + + double x = p.x; + double y = p.y; + + double f = a000 + a001*x + a002*y + a011*x*x + a012*x*y + a022*y*y + + a111*x*x*x + a112*x*x*y + a122*x*y*y + a222*y*y*y; + double fx = a001 + 2*a011*x + a012*y + 3*a111*x*x + 2*a112*x*y + a122*y*y; + double fy = a002 + a012*x + 2*a022*y + a112*x*x + 2*a122*x*y + 3*a222*y*y; + + double dist = fabs(f)/(fabs(fx) + fabs(fy)); + + return dist <= threshold; +} + +bool CubicImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + return Parent::isPropertyDefinedOnOrThroughThisImp( which ); +} + +Rect CubicImp::surroundingRect() const +{ + // it's probably possible to calculate this if it exists, but for + // now we don't. + return Rect::invalidRect(); +} + +QString CubicImp::cartesianEquationString( const KigDocument& ) const +{ + /* + * unfortunately QStrings.arg method is limited to %1, %9, so we cannot + * treat all 10 arguments! Let's split the equation in two parts... + * Now this ends up also in the translation machinery, is this really + * necessary? Otherwise we could do a little bit of tidy up on the + * the equation (removal of zeros, avoid " ... + -1234 x ", etc.) + */ + + QString ret = i18n( "%6 x³ + %9 y³ + %7 x²y + %8 xy² + %5 y² + %3 x² + %4 xy + %1 x + %2 y" ); + ret = ret.arg( mdata.coeffs[1], 0, 'g', 3 ); + ret = ret.arg( mdata.coeffs[2], 0, 'g', 3 ); + ret = ret.arg( mdata.coeffs[3], 0, 'g', 3 ); + ret = ret.arg( mdata.coeffs[4], 0, 'g', 3 ); + ret = ret.arg( mdata.coeffs[5], 0, 'g', 3 ); + ret = ret.arg( mdata.coeffs[6], 0, 'g', 3 ); + ret = ret.arg( mdata.coeffs[7], 0, 'g', 3 ); + ret = ret.arg( mdata.coeffs[8], 0, 'g', 3 ); + ret = ret.arg( mdata.coeffs[9], 0, 'g', 3 ); + + ret.append( i18n( " + %1 = 0" ) ); + ret = ret.arg( mdata.coeffs[0], 0, 'g', 3 ); + + // we should find a common place to do this... + ret.replace( "+ -", "- " ); + ret.replace( "+-", "-" ); + return ret; +} diff --git a/kig/objects/cubic_imp.h b/kig/objects/cubic_imp.h new file mode 100644 index 00000000..bb7d89c7 --- /dev/null +++ b/kig/objects/cubic_imp.h @@ -0,0 +1,81 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_CUBIC_IMP_H +#define KIG_OBJECTS_CUBIC_IMP_H + +#include "curve_imp.h" + +#include "../misc/cubic-common.h" + +class LineData; + +/** + * An ObjectImp representing a cubic. + */ +class CubicImp + : public CurveImp +{ + const CubicCartesianData mdata; +public: + typedef CurveImp Parent; + static const ObjectImpType* stype(); + + CubicImp( const CubicCartesianData& data ); + ~CubicImp(); + + ObjectImp* transform( const Transformation& ) const; + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& ) const; + bool inRect( const Rect& r, int width, const KigWidget& ) const; + Rect surroundingRect() const; + QString cartesianEquationString( const KigDocument& ) const; + + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& w ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + CubicImp* copy() const; + + double getParam( const Coordinate& point, const KigDocument& ) const; + + // The getPoint function doesn't need the KigDocument argument, the + // first getPoint function is identical to the other one. It is + // only provided for implementing the CurveImp interface. + const Coordinate getPoint( double param, const KigDocument& ) const; + const Coordinate getPoint( double param ) const; + +public: + /** + * Return the cartesian representation of this cubic. + */ + const CubicCartesianData data() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool equals( const ObjectImp& rhs ) const; + + bool containsPoint( const Coordinate& p, const KigDocument& doc ) const; + bool internalContainsPoint( const Coordinate& p, double threshold ) const; +}; + +#endif diff --git a/kig/objects/cubic_type.cc b/kig/objects/cubic_type.cc new file mode 100644 index 00000000..c322b8c3 --- /dev/null +++ b/kig/objects/cubic_type.cc @@ -0,0 +1,185 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "cubic_type.h" + +#include "cubic_imp.h" +#include "point_imp.h" +#include "bogus_imp.h" + +#include <klocale.h> + +static const char cubictpstatement[] = I18N_NOOP( "Construct a cubic curve through this point" ); + +static const struct ArgsParser::spec argsspecCubicB9P[] = +{ + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CubicB9PType ) + +CubicB9PType::CubicB9PType() + : ArgsParserObjectType( "CubicB9P", argsspecCubicB9P, 9 ) +{ +} + +CubicB9PType::~CubicB9PType() +{ +} + +const CubicB9PType* CubicB9PType::instance() +{ + static const CubicB9PType t; + return &t; +} + +ObjectImp* CubicB9PType::calc( const Args& os, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( os, 2 ) ) return new InvalidImp; + + std::vector<Coordinate> points; + for ( uint i = 0; i < os.size(); ++i ) + points.push_back( static_cast<const PointImp*>( os[i] )->coordinate() ); + + CubicCartesianData d = calcCubicThroughPoints( points ); + if ( d.valid() ) + return new CubicImp( d ); + else + return new InvalidImp; +} + +static const ArgsParser::spec argsspecCubicNodeB6P[] = +{ + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CubicNodeB6PType ) + +CubicNodeB6PType::CubicNodeB6PType() + : ArgsParserObjectType( "CubicNodeB6P", argsspecCubicNodeB6P, 6 ) +{ +} + +CubicNodeB6PType::~CubicNodeB6PType() +{ +} + +const CubicNodeB6PType* CubicNodeB6PType::instance() +{ + static const CubicNodeB6PType t; + return &t; +} + +ObjectImp* CubicNodeB6PType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents, 2 ) ) return new InvalidImp; + + std::vector<Coordinate> points; + for ( Args::const_iterator i = parents.begin(); i != parents.end(); ++i ) + points.push_back( static_cast<const PointImp*>( *i )->coordinate() ); + + CubicCartesianData d = calcCubicNodeThroughPoints( points ); + if ( d.valid() ) + return new CubicImp( d ); + else + return new InvalidImp; +} + +static const ArgsParser::spec argsspecCubicCuspB4P[] = +{ + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CubicCuspB4PType ) + +CubicCuspB4PType::CubicCuspB4PType() + : ArgsParserObjectType( "CubicCuspB4P", argsspecCubicCuspB4P, 4 ) +{ +} + +CubicCuspB4PType::~CubicCuspB4PType() +{ +} + +const CubicCuspB4PType* CubicCuspB4PType::instance() +{ + static const CubicCuspB4PType t; + return &t; +} + +ObjectImp* CubicCuspB4PType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents, 2 ) ) return new InvalidImp; + + std::vector<Coordinate> points; + for ( Args::const_iterator i = parents.begin(); i != parents.end(); ++i ) + points.push_back( static_cast<const PointImp*>( *i )->coordinate() ); + + CubicCartesianData d = calcCubicCuspThroughPoints( points ); + if ( d.valid() ) return new CubicImp( d ); + else return new InvalidImp; +} + +const ObjectImpType* CubicB9PType::resultId() const +{ + return CubicImp::stype(); +} + +const ObjectImpType* CubicNodeB6PType::resultId() const +{ + return CubicImp::stype(); +} + +const ObjectImpType* CubicCuspB4PType::resultId() const +{ + return CubicImp::stype(); +} diff --git a/kig/objects/cubic_type.h b/kig/objects/cubic_type.h new file mode 100644 index 00000000..39be7387 --- /dev/null +++ b/kig/objects/cubic_type.h @@ -0,0 +1,56 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_CUBIC_TYPE_H +#define KIG_OBJECTS_CUBIC_TYPE_H + +#include "object_type.h" + +class CubicB9PType + : public ArgsParserObjectType +{ + CubicB9PType(); + ~CubicB9PType(); +public: + static const CubicB9PType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class CubicNodeB6PType + : public ArgsParserObjectType +{ + CubicNodeB6PType(); + ~CubicNodeB6PType(); +public: + static const CubicNodeB6PType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class CubicCuspB4PType + : public ArgsParserObjectType +{ + CubicCuspB4PType(); + ~CubicCuspB4PType(); +public: + static const CubicCuspB4PType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/curve_imp.cc b/kig/objects/curve_imp.cc new file mode 100644 index 00000000..315cd8cb --- /dev/null +++ b/kig/objects/curve_imp.cc @@ -0,0 +1,41 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "curve_imp.h" +#include "../misc/coordinate.h" + +const ObjectImpType* CurveImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "curve", + I18N_NOOP( "curve" ), + I18N_NOOP( "Select this curve" ), + I18N_NOOP( "Select curve %1" ), + I18N_NOOP( "Remove a Curve" ), + I18N_NOOP( "Add a Curve" ), + I18N_NOOP( "Move a Curve" ), + I18N_NOOP( "Attach to this curve" ), + I18N_NOOP( "Show a Curve" ), + I18N_NOOP( "Hide a Curve" ) + ); + return &t; +} + +Coordinate CurveImp::attachPoint() const +{ + return Coordinate::invalidCoord(); +} diff --git a/kig/objects/curve_imp.h b/kig/objects/curve_imp.h new file mode 100644 index 00000000..3a33a722 --- /dev/null +++ b/kig/objects/curve_imp.h @@ -0,0 +1,62 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_CURVE_IMP_H +#define KIG_OBJECTS_CURVE_IMP_H + +#include "object_imp.h" + +/** + * This class represents a curve: something which is composed of + * points, like a line, a circle, a locus... + */ +class CurveImp + : public ObjectImp +{ +public: + typedef ObjectImp Parent; + + /** + * Returns the ObjectImpType representing the CurveImp type. + */ + static const ObjectImpType* stype(); + + Coordinate attachPoint() const; + + // param is between 0 and 1. Note that 0 and 1 should be the + // end-points. E.g. for a Line, getPoint(0) returns a more or less + // infinite point. getPoint(0.5) should return the point in the + // middle. + virtual double getParam( const Coordinate& point, const KigDocument& ) const = 0; + // this should be the inverse function of getPoint(). + // Note that it should also do something reasonable when p is not on + // the curve. You can return an invalid Coordinate( + // Coordinate::invalidCoord() ) if you need to in some cases. + virtual const Coordinate getPoint( double param, const KigDocument& ) const = 0; + + virtual CurveImp* copy() const = 0; + + /** + * Return whether this Curve contains the given point. This is + * implemented as a numerical approximation. Implementations + * can/should use the value test_threshold in common.h as a + * threshold value. + */ + virtual bool containsPoint( const Coordinate& p, const KigDocument& ) const = 0; +}; + +#endif diff --git a/kig/objects/intersection_types.cc b/kig/objects/intersection_types.cc new file mode 100644 index 00000000..804d498d --- /dev/null +++ b/kig/objects/intersection_types.cc @@ -0,0 +1,338 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "intersection_types.h" + +#include "bogus_imp.h" +#include "circle_imp.h" +#include "conic_imp.h" +#include "cubic_imp.h" +#include "line_imp.h" +#include "other_imp.h" +#include "point_imp.h" + +#include <klocale.h> + +static const char intersectlinestat[] = I18N_NOOP( "Intersect with this line" ); + +static const ArgsParser::spec argsspecConicLineIntersection[] = +{ + { ConicImp::stype(), I18N_NOOP( "Intersect with this conic" ), + "SHOULD NOT BE SEEN", true }, + { AbstractLineImp::stype(), intersectlinestat, "SHOULD NOT BE SEEN", true }, + { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConicLineIntersectionType ) + +ConicLineIntersectionType::ConicLineIntersectionType() + : ArgsParserObjectType( "ConicLineIntersection", + argsspecConicLineIntersection, 3 ) +{ +} + +ConicLineIntersectionType::~ConicLineIntersectionType() +{ +} + +const ConicLineIntersectionType* ConicLineIntersectionType::instance() +{ + static const ConicLineIntersectionType t; + return &t; +} + +ObjectImp* ConicLineIntersectionType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + int side = static_cast<const IntImp*>( parents[2] )->data(); + assert( side == 1 || side == -1 ); + const LineData line = static_cast<const AbstractLineImp*>( parents[1] )->data(); + + Coordinate ret; + if ( parents[0]->inherits( CircleImp::stype() ) ) + { + // easy case.. + const CircleImp* c = static_cast<const CircleImp*>( parents[0] ); + ret = calcCircleLineIntersect( + c->center(), c->squareRadius(), line, side ); + } + else + { + // harder case.. + ret = calcConicLineIntersect( + static_cast<const ConicImp*>( parents[0] )->cartesianData(), + line, 0.0, side ); + } + if ( ret.valid() ) return new PointImp( ret ); + else return new InvalidImp; +} + +static const ArgsParser::spec argsspecConicLineOtherIntersection[] = +{ + { ConicImp::stype(), I18N_NOOP( "Intersect with this conic" ), + "SHOULD NOT BE SEEN", true }, + { AbstractLineImp::stype(), intersectlinestat, "SHOULD NOT BE SEEN", true }, + { PointImp::stype(), I18N_NOOP( "Already computed intersection point"), + "SHOULD NOT BE SEEN", true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConicLineOtherIntersectionType ) + +ConicLineOtherIntersectionType::ConicLineOtherIntersectionType() + : ArgsParserObjectType( "ConicLineOtherIntersection", + argsspecConicLineOtherIntersection, 3 ) +{ +} + +ConicLineOtherIntersectionType::~ConicLineOtherIntersectionType() +{ +} + +const ConicLineOtherIntersectionType* ConicLineOtherIntersectionType::instance() +{ + static const ConicLineOtherIntersectionType t; + return &t; +} + +ObjectImp* ConicLineOtherIntersectionType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + Coordinate p = static_cast<const PointImp*>( parents[2] )->coordinate(); + const LineData line = static_cast<const AbstractLineImp*>( parents[1] )->data(); + + Coordinate ret; +// if ( parents[0]->inherits( CircleImp::stype() ) ) +// { +// // easy case.. +// const CircleImp* c = static_cast<const CircleImp*>( parents[0] ); +// ret = calcCircleLineIntersect( +// c->center(), c->squareRadius(), line, side, valid ); +// } +// else +// { + // harder case.. + double pax = p.x - line.a.x; + double pay = p.y - line.a.y; + double bax = line.b.x - line.a.x; + double bay = line.b.y - line.a.y; + double knownparam = (pax*bax + pay*bay)/(bax*bax + bay*bay); + ret = calcConicLineIntersect( + static_cast<const ConicImp*>( parents[0] )->cartesianData(), + line, knownparam, 0 ); +// } + if ( ret.valid() ) return new PointImp( ret ); + else return new InvalidImp; +} + +static const ArgsParser::spec argsspecLineLineIntersection[] = +{ + { AbstractLineImp::stype(), intersectlinestat, "SHOULD NOT BE SEEN", true }, + { AbstractLineImp::stype(), intersectlinestat, "SHOULD NOT BE SEEN", true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( LineLineIntersectionType ) + +LineLineIntersectionType::LineLineIntersectionType() + : ArgsParserObjectType( "LineLineIntersection", + argsspecLineLineIntersection, 2 ) +{ +} + +LineLineIntersectionType::~LineLineIntersectionType() +{ +} + +const LineLineIntersectionType* LineLineIntersectionType::instance() +{ + static const LineLineIntersectionType t; + return &t; +} + +ObjectImp* LineLineIntersectionType::calc( const Args& parents, const KigDocument& d ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + Coordinate p = + calcIntersectionPoint( + static_cast<const AbstractLineImp*>( parents[0] )->data(), + static_cast<const AbstractLineImp*>( parents[1] )->data() ); + if ( static_cast<const AbstractLineImp*>( parents[0] )->containsPoint( p, d ) && + static_cast<const AbstractLineImp*>( parents[1] )->containsPoint( p, d ) ) + return new PointImp( p ); + else return new InvalidImp(); +} + +static const ArgsParser::spec argsspecLineCubicIntersection[] = +{ + { CubicImp::stype(), I18N_NOOP( "Intersect with this cubic curve" ), + "SHOULD NOT BE SEEN", true }, + { AbstractLineImp::stype(), intersectlinestat, "SHOULD NOT BE SEEN", true }, + { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( LineCubicIntersectionType ) + +LineCubicIntersectionType::LineCubicIntersectionType() + : ArgsParserObjectType( "LineCubicIntersection", + argsspecLineCubicIntersection, 3 ) +{ +} + +LineCubicIntersectionType::~LineCubicIntersectionType() +{ +} + +const LineCubicIntersectionType* LineCubicIntersectionType::instance() +{ + static const LineCubicIntersectionType t; + return &t; +} + +ObjectImp* LineCubicIntersectionType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + int which = static_cast<const IntImp*>( parents[2] )->data(); + bool valid = true; + const Coordinate c = calcCubicLineIntersect( + static_cast<const CubicImp*>( parents[0] )->data(), + static_cast<const AbstractLineImp*>( parents[1] )->data(), + which, valid ); + if ( valid ) return new PointImp( c ); + else return new InvalidImp; +} + +const ObjectImpType* ConicLineIntersectionType::resultId() const +{ + return PointImp::stype(); +} + +const ObjectImpType* ConicLineOtherIntersectionType::resultId() const +{ + return PointImp::stype(); +} + +const ObjectImpType* LineLineIntersectionType::resultId() const +{ + return PointImp::stype(); +} + +const ObjectImpType* LineCubicIntersectionType::resultId() const +{ + return PointImp::stype(); +} + +static const ArgsParser::spec argsspecCircleCircleIntersection[] = +{ + { CircleImp::stype(), I18N_NOOP( "Intersect with this circle" ), + "SHOULD NOT BE SEEN", true }, + { CircleImp::stype(), I18N_NOOP( "Intersect with this circle" ), + "SHOULD NOT BE SEEN", true }, + { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CircleCircleIntersectionType ) + +CircleCircleIntersectionType::CircleCircleIntersectionType() + : ArgsParserObjectType( "CircleCircleIntersection", + argsspecCircleCircleIntersection, 3 ) +{ +} + +CircleCircleIntersectionType::~CircleCircleIntersectionType() +{ +} + +const CircleCircleIntersectionType* CircleCircleIntersectionType::instance() +{ + static const CircleCircleIntersectionType t; + return &t; +} + +ObjectImp* CircleCircleIntersectionType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + int side = static_cast<const IntImp*>( parents[2] )->data(); + assert( side == 1 || side == -1 ); + const CircleImp* c1 = static_cast<const CircleImp*>( parents[0] ); + const CircleImp* c2 = static_cast<const CircleImp*>( parents[1] ); + const Coordinate o1 = c1->center(); + const Coordinate o2 = c2->center(); + const double r1sq = c1->squareRadius(); + const Coordinate a = calcCircleRadicalStartPoint( + o1, o2, r1sq, c2->squareRadius() + ); + const LineData line = LineData (a, Coordinate ( a.x -o2.y + o1.y, a.y + o2.x - o1.x )); + Coordinate ret = calcCircleLineIntersect( o1, r1sq, line, side ); + if ( ret.valid() ) return new PointImp( ret ); + else return new InvalidImp; +} + +const ObjectImpType* CircleCircleIntersectionType::resultId() const +{ + return PointImp::stype(); +} + +static const ArgsParser::spec argsspecArcLineIntersection[] = +{ + { ArcImp::stype(), I18N_NOOP( "Intersect with this arc" ), + "SHOULD NOT BE SEEN", true }, + { AbstractLineImp::stype(), intersectlinestat, "SHOULD NOT BE SEEN", true }, + { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ArcLineIntersectionType ) + +ArcLineIntersectionType::ArcLineIntersectionType() + : ArgsParserObjectType( "ArcLineIntersection", + argsspecArcLineIntersection, 3 ) +{ +} + +ArcLineIntersectionType::~ArcLineIntersectionType() +{ +} + +const ArcLineIntersectionType* ArcLineIntersectionType::instance() +{ + static const ArcLineIntersectionType t; + return &t; +} + +ObjectImp* ArcLineIntersectionType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + int side = static_cast<const IntImp*>( parents[2] )->data(); + assert( side == 1 || side == -1 ); + const LineData line = static_cast<const AbstractLineImp*>( parents[1] )->data(); + + const ArcImp* c = static_cast<const ArcImp*>( parents[0] ); + const double r = c->radius(); + Coordinate ret = calcArcLineIntersect( c->center(), r*r, c->startAngle(), + c->angle(), line, side ); + if ( ret.valid() ) return new PointImp( ret ); + else return new InvalidImp; +} + +const ObjectImpType* ArcLineIntersectionType::resultId() const +{ + return PointImp::stype(); +} diff --git a/kig/objects/intersection_types.h b/kig/objects/intersection_types.h new file mode 100644 index 00000000..9e1df62e --- /dev/null +++ b/kig/objects/intersection_types.h @@ -0,0 +1,105 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_INTERSECTION_TYPES_H +#define KIG_OBJECTS_INTERSECTION_TYPES_H + +#include "object_type.h" + +/** + * conic line intersection. This also serves as circle-line + * intersection, in which case it uses the easier way to calc + * ... There is no separate CircleLineIntersectionPoint, since the + * difference between both types is quite small ( same number of + * intersections with a line, for example.. ), and since with + * transformations, Circles might dynamically change types to + * Conics.. + */ +class ConicLineIntersectionType + : public ArgsParserObjectType +{ + ConicLineIntersectionType(); + ~ConicLineIntersectionType(); +public: + static const ConicLineIntersectionType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +/** + * conic line 'other' intersection. In case we already know one of the + * two intersections + */ +class ConicLineOtherIntersectionType + : public ArgsParserObjectType +{ + ConicLineOtherIntersectionType(); + ~ConicLineOtherIntersectionType(); +public: + static const ConicLineOtherIntersectionType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class LineLineIntersectionType + : public ArgsParserObjectType +{ + LineLineIntersectionType(); + ~LineLineIntersectionType(); +public: + static const LineLineIntersectionType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class LineCubicIntersectionType + : public ArgsParserObjectType +{ + LineCubicIntersectionType(); + ~LineCubicIntersectionType(); +public: + static const LineCubicIntersectionType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class CircleCircleIntersectionType + : public ArgsParserObjectType +{ + CircleCircleIntersectionType(); + ~CircleCircleIntersectionType(); +public: + static const CircleCircleIntersectionType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +/** + * arc line intersection. + */ +class ArcLineIntersectionType + : public ArgsParserObjectType +{ + ArcLineIntersectionType(); + ~ArcLineIntersectionType(); +public: + static const ArcLineIntersectionType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/inversion_type.cc b/kig/objects/inversion_type.cc new file mode 100644 index 00000000..0ab77780 --- /dev/null +++ b/kig/objects/inversion_type.cc @@ -0,0 +1,412 @@ +// Copyright (C) 2005 Maurizio Paolini <paolini@dmf.unicatt.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "inversion_type.h" +#include "point_imp.h" +#include "line_imp.h" +#include "circle_imp.h" +#include "other_imp.h" +#include "bogus_imp.h" + +#include "../misc/common.h" + +#include <klocale.h> + +static const char str1[] = I18N_NOOP( "Invert with respect to this circle" ); +static const char str2[] = I18N_NOOP( "Select the circle we want to invert against..." ); + +static const ArgsParser::spec argsspecInvertPoint[] = +{ + { PointImp::stype(), I18N_NOOP( "Compute the inversion of this point" ), + I18N_NOOP( "Select the point to invert..." ), false }, + { CircleImp::stype(), str1, str2, false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( InvertPointType ) + +InvertPointType::InvertPointType() + : ArgsParserObjectType( "InvertPoint", argsspecInvertPoint, 2 ) +{ +} + +InvertPointType::~InvertPointType() +{ +} + +const InvertPointType* InvertPointType::instance() +{ + static const InvertPointType s; + return &s; +} + +const ObjectImpType* InvertPointType::resultId() const +{ + return PointImp::stype(); +} + +ObjectImp* InvertPointType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + const CircleImp* c = static_cast<const CircleImp*>( args[1] ); + Coordinate center = c->center(); + Coordinate relp = static_cast<const PointImp*>( args[0] )->coordinate() - center; + double radiussq = c->squareRadius(); + double normsq = relp.x*relp.x + relp.y*relp.y; + if ( normsq == 0 ) return new InvalidImp; + return new PointImp( center + (radiussq/normsq)*relp ); +} + +/* + * inversion of a line + */ + +static const ArgsParser::spec argsspecInvertLine[] = +{ + { LineImp::stype(), I18N_NOOP( "Compute the inversion of this line" ), + I18N_NOOP( "Select the line to invert..." ), false }, + { CircleImp::stype(), str1, str2, false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( InvertLineType ) + +InvertLineType::InvertLineType() + : ArgsParserObjectType( "InvertLine", argsspecInvertLine, 2 ) +{ +} + +InvertLineType::~InvertLineType() +{ +} + +const InvertLineType* InvertLineType::instance() +{ + static const InvertLineType s; + return &s; +} + +const ObjectImpType* InvertLineType::resultId() const +{ + return CircleImp::stype(); +} + +ObjectImp* InvertLineType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + const CircleImp* c = static_cast<const CircleImp*>( args[1] ); + Coordinate center = c->center(); + double radiussq = c->squareRadius(); + const LineData line = static_cast<const AbstractLineImp*>( args[0] )->data(); + Coordinate relb = line.b - center; + Coordinate ab = line.b - line.a; + double t = (relb.x*ab.x + relb.y*ab.y)/(ab.x*ab.x + ab.y*ab.y); + Coordinate relh = relb - t*ab; + double normhsq = relh.x*relh.x + relh.y*relh.y; + if ( normhsq < 1e-12*radiussq ) return new LineImp( line.a, line.b ); + Coordinate newcenter = center + 0.5*radiussq/normhsq*relh; + double newradius = 0.5*radiussq/sqrt(normhsq); + + return new CircleImp( newcenter, newradius ); +} + +/* + * inversion of a segment + */ + +static const ArgsParser::spec argsspecInvertSegment[] = +{ + { SegmentImp::stype(), I18N_NOOP( "Compute the inversion of this segment" ), + I18N_NOOP( "Select the segment to invert..." ), false }, + { CircleImp::stype(), str1, str2, false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( InvertSegmentType ) + +InvertSegmentType::InvertSegmentType() + : ArgsParserObjectType( "InvertSegment", argsspecInvertSegment, 2 ) +{ +} + +InvertSegmentType::~InvertSegmentType() +{ +} + +const InvertSegmentType* InvertSegmentType::instance() +{ + static const InvertSegmentType s; + return &s; +} + +const ObjectImpType* InvertSegmentType::resultId() const +{ + return ArcImp::stype(); +} + +ObjectImp* InvertSegmentType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + const CircleImp* c = static_cast<const CircleImp*>( args[1] ); + Coordinate center = c->center(); + double radiussq = c->squareRadius(); + const LineData line = static_cast<const AbstractLineImp*>( args[0] )->data(); + Coordinate rela = line.a - center; + Coordinate relb = line.b - center; + Coordinate ab = relb - rela; + double t = (relb.x*ab.x + relb.y*ab.y)/(ab.x*ab.x + ab.y*ab.y); + Coordinate relh = relb - t*ab; + double normhsq = relh.x*relh.x + relh.y*relh.y; + + /* + * compute the inversion of the two endpoints + */ + + Coordinate newcenterrel = 0.5*radiussq/normhsq*relh; + Coordinate relainv = radiussq/rela.squareLength() * rela; + Coordinate relbinv = radiussq/relb.squareLength() * relb; + + if ( normhsq < 1e-12*radiussq ) + { + if ( rela.squareLength() < 1e-12 ) + { + return new RayImp( relbinv + center, 2*relbinv + center ); + } + if ( relb.squareLength() < 1e-12 ) + { + return new RayImp( relainv + center, 2*relainv + center ); + } + if ( relb.x*rela.x + relb.y*rela.y > 0 ) + { + return new SegmentImp( relainv + center, relbinv + center ); + } + return new InvalidImp(); + } + double newradius = 0.5*radiussq/sqrt(normhsq); + + relainv -= newcenterrel; + relbinv -= newcenterrel; + double angle1 = atan2( relainv.y, relainv.x ); + double angle2 = atan2( relbinv.y, relbinv.x ); + double angle = angle2 - angle1; + if ( ab.x*rela.y - ab.y*rela.x > 0 ) + { + angle1 = angle2; + angle = -angle; + } + while ( angle1 < 0 ) angle1 += 2*M_PI; + while ( angle1 >= 2*M_PI ) angle1 -= 2*M_PI; + while ( angle < 0 ) angle += 2*M_PI; + while ( angle >= 2*M_PI ) angle -= 2*M_PI; + return new ArcImp( newcenterrel + center, newradius, angle1, angle ); +} + +/* + * inversion of a circle + */ + +static const ArgsParser::spec argsspecInvertCircle[] = +{ + { CircleImp::stype(), I18N_NOOP( "Compute the inversion of this circle" ), + I18N_NOOP( "Select the circle to invert..." ), false }, + { CircleImp::stype(), str1, str2, false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( InvertCircleType ) + +InvertCircleType::InvertCircleType() + : ArgsParserObjectType( "InvertCircle", argsspecInvertCircle, 2 ) +{ +} + +InvertCircleType::~InvertCircleType() +{ +} + +const InvertCircleType* InvertCircleType::instance() +{ + static const InvertCircleType s; + return &s; +} + +const ObjectImpType* InvertCircleType::resultId() const +{ + return CircleImp::stype(); +} + +ObjectImp* InvertCircleType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + const CircleImp* refcircle = static_cast<const CircleImp*>( args[1] ); + Coordinate refc = refcircle->center(); + double refrsq = refcircle->squareRadius(); + const CircleImp* circle = static_cast<const CircleImp*>( args[0] ); + Coordinate c = circle->center() - refc; + double clength = c.length(); + Coordinate cnorm = Coordinate (1.,0.); + if ( clength != 0.0 ) cnorm = c/clength; + double r = circle->radius(); + Coordinate tc = r*cnorm; + Coordinate b = c + tc; //(1 + t)*c; + double bsq = b.x*b.x + b.y*b.y; + Coordinate bprime = refrsq*b/bsq; + if ( std::fabs( clength - r ) < 1e-6*clength ) // circle through origin -> line + { + return new LineImp( bprime+refc, bprime+refc+Coordinate( -c.y, c.x ) ); + } + + Coordinate a = c - tc; + double asq = a.x*a.x + a.y*a.y; + Coordinate aprime = refrsq*a/asq; + + Coordinate cprime = 0.5*(aprime + bprime); + double rprime = 0.5*( bprime - aprime ).length(); + + return new CircleImp( cprime + refc, rprime ); +} + +/* + * inversion of an arc + */ + +static const ArgsParser::spec argsspecInvertArc[] = +{ + { ArcImp::stype(), I18N_NOOP( "Compute the inversion of this arc" ), + I18N_NOOP( "Select the arc to invert..." ), false }, + { CircleImp::stype(), str1, str2, false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( InvertArcType ) + +InvertArcType::InvertArcType() + : ArgsParserObjectType( "InvertArc", argsspecInvertArc, 2 ) +{ +} + +InvertArcType::~InvertArcType() +{ +} + +const InvertArcType* InvertArcType::instance() +{ + static const InvertArcType s; + return &s; +} + +const ObjectImpType* InvertArcType::resultId() const +{ + return ArcImp::stype(); +} + +ObjectImp* InvertArcType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + const CircleImp* refcircle = static_cast<const CircleImp*>( args[1] ); + Coordinate refc = refcircle->center(); + double refrsq = refcircle->squareRadius(); + const ArcImp* arc = static_cast<const ArcImp*>( args[0] ); + Coordinate c = arc->center() - refc; + double clength = c.length(); + Coordinate cnorm = Coordinate (1.,0.); + if ( clength != 0.0 ) cnorm = c/clength; + double r = arc->radius(); + /* + * r > clength means center of inversion circle inside of circle supporting arc + */ + Coordinate tc = r*cnorm; + Coordinate b = c + tc; + double bsq = b.x*b.x + b.y*b.y; + Coordinate bprime = refrsq*b/bsq; + if ( std::fabs( clength - r ) < 1e-6*clength ) // support circle through origin -> + // segment, ray or invalid + // (reversed segment, union of two rays) + { + bool valid1 = false; + bool valid2 = false; + Coordinate ep1 = arc->firstEndPoint() - refc; + Coordinate ep2 = arc->secondEndPoint() - refc; + Coordinate ep1inv = Coordinate::invalidCoord(); + Coordinate ep2inv = Coordinate::invalidCoord(); + double ep1sq = ep1.squareLength(); + if ( ep1sq > 1e-12 ) + { + valid1 = true; + ep1inv = refrsq/ep1sq * ep1; + } + Coordinate rayendp = ep1inv; + int sign = 1; + double ep2sq = ep2.squareLength(); + if ( ep2sq > 1e-12 ) + { + valid2 = true; + ep2inv = refrsq/ep2sq * ep2; + rayendp = ep2inv; + sign = -1; + } + if ( valid1 || valid2 ) + { + if ( valid1 && valid2 ) + { + // this gives either a segment or the complement of a segment (relative + // to its support line). We return a segment in any case (fixme) + double ang = atan2( -c.y, -c.x ); + double sa = arc->startAngle(); + if ( ang < sa ) ang += 2*M_PI; + if ( ang - sa - arc->angle() < 0 ) return new InvalidImp(); + return new SegmentImp( ep1inv + refc, ep2inv + refc ); + } else + return new RayImp ( rayendp + refc, + rayendp + refc + sign*Coordinate( -c.y, c.x ) ); // this should give a Ray + } else + return new LineImp( bprime+refc, bprime+refc+Coordinate( -c.y, c.x ) ); + } + + Coordinate a = c - tc; + double asq = a.x*a.x + a.y*a.y; + Coordinate aprime = refrsq*a/asq; + + Coordinate cprime = 0.5*(aprime + bprime); + double rprime = 0.5*( bprime - aprime ).length(); + + Coordinate ep1 = arc->firstEndPoint() - refc; + double ang1 = arc->startAngle(); + double newstartangle = 2*atan2(ep1.y,ep1.x) - ang1; + Coordinate ep2 = arc->secondEndPoint() - refc; + double ang2 = ang1 + arc->angle(); + double newendangle = 2*atan2(ep2.y,ep2.x) - ang2; + double newangle = newendangle - newstartangle; + + /* + * newstartangle and newendangle might have to be exchanged: + * this is the case if the circle supporting our arc does not + * contain the center of the inversion circle + */ + if ( r < clength ) + { + newstartangle = newendangle - M_PI; + newangle = - newangle; + // newendangle is no-longer valid + } + while ( newstartangle < 0 ) newstartangle += 2*M_PI; + while ( newstartangle >= 2*M_PI ) newstartangle -= 2*M_PI; + while ( newangle < 0 ) newangle += 2*M_PI; + while ( newangle >= 2*M_PI ) newangle -= 2*M_PI; + return new ArcImp( cprime + refc, rprime, newstartangle, newangle ); +} + diff --git a/kig/objects/inversion_type.h b/kig/objects/inversion_type.h new file mode 100644 index 00000000..d7b97957 --- /dev/null +++ b/kig/objects/inversion_type.h @@ -0,0 +1,86 @@ +// Copyright (C) 2005 Maurizio Paolini <paolini@dmf.unicatt.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_INVERSION_TYPE_H +#define KIG_OBJECTS_INVERSION_TYPE_H + +#include "base_type.h" + +/** + * Inversion of a point, line + */ +class InvertPointType + : public ArgsParserObjectType +{ + InvertPointType(); + ~InvertPointType(); +public: + static const InvertPointType* instance(); + + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class InvertLineType + : public ArgsParserObjectType +{ + InvertLineType(); + ~InvertLineType(); +public: + static const InvertLineType* instance(); + + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class InvertSegmentType + : public ArgsParserObjectType +{ + InvertSegmentType(); + ~InvertSegmentType(); +public: + static const InvertSegmentType* instance(); + + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class InvertCircleType + : public ArgsParserObjectType +{ + InvertCircleType(); + ~InvertCircleType(); +public: + static const InvertCircleType* instance(); + + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class InvertArcType + : public ArgsParserObjectType +{ + InvertArcType(); + ~InvertArcType(); +public: + static const InvertArcType* instance(); + + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/line_imp.cc b/kig/objects/line_imp.cc new file mode 100644 index 00000000..6f3c6002 --- /dev/null +++ b/kig/objects/line_imp.cc @@ -0,0 +1,571 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "line_imp.h" + +#include "bogus_imp.h" +#include "point_imp.h" + +#include "../misc/rect.h" +#include "../misc/common.h" +#include "../misc/kigtransform.h" +#include "../misc/kigpainter.h" +#include "../kig/kig_view.h" + +#include <klocale.h> + +#include <cmath> +using namespace std; + +AbstractLineImp::AbstractLineImp( const Coordinate& a, const Coordinate& b ) + : mdata( a, b ) +{ +} + +AbstractLineImp::~AbstractLineImp() +{ +} + +bool AbstractLineImp::inRect( const Rect& r, int width, const KigWidget& w ) const +{ + return lineInRect( r, mdata.a, mdata.b, width, this, w ); +} + +const uint AbstractLineImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 2; +} + +const ObjectImpType* AbstractLineImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else return AbstractLineImp::stype(); +} + +const char* AbstractLineImp::iconForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + if ( which == Parent::numberOfProperties() ) + return "slope"; // slope + if ( which == Parent::numberOfProperties() + 1 ) + return "kig_text"; // equation + else assert( false ); + return ""; +} + +ObjectImp* AbstractLineImp::property( uint which, const KigDocument& w ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, w ); + if ( which == Parent::numberOfProperties() ) + return new DoubleImp( slope() ); + if ( which == Parent::numberOfProperties() + 1 ) + return new StringImp( equationString() ); + else assert( false ); + return new InvalidImp; +} + +const QCStringList AbstractLineImp::propertiesInternalNames() const +{ + QCStringList l = Parent::propertiesInternalNames(); + l << "slope"; + l << "equation"; + assert( l.size() == AbstractLineImp::numberOfProperties() ); + return l; +} + +const QCStringList AbstractLineImp::properties() const +{ + QCStringList l = Parent::properties(); + l << I18N_NOOP( "Slope" ); + l << I18N_NOOP( "Equation" ); + assert( l.size() == AbstractLineImp::numberOfProperties() ); + return l; +} + +const uint SegmentImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 4; +} + +const QCStringList SegmentImp::propertiesInternalNames() const +{ + QCStringList s = Parent::propertiesInternalNames(); + s << "length"; + s << "mid-point"; + s << "end-point-A"; + s << "end-point-B"; + assert( s.size() == SegmentImp::numberOfProperties() ); + return s; +} + +const QCStringList SegmentImp::properties() const +{ + QCStringList s = Parent::properties(); + s << I18N_NOOP( "Length" ); + s << I18N_NOOP( "Mid Point" ); + s << I18N_NOOP( "First End Point" ); + s << I18N_NOOP( "Second End Point" ); + assert( s.size() == SegmentImp::numberOfProperties() ); + return s; +} + +const ObjectImpType* SegmentImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else return SegmentImp::stype(); +} + +const char* SegmentImp::iconForProperty( uint which ) const +{ + int pnum = 0; + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + else if ( which == Parent::numberOfProperties() + pnum++ ) + return "distance"; // length + else if ( which == Parent::numberOfProperties() + pnum++ ) + return "segment_midpoint"; // mid point + else if ( which == Parent::numberOfProperties() + pnum++ ) + return "endpoint1"; // mid point + else if ( which == Parent::numberOfProperties() + pnum++ ) + return "endpoint2"; // mid point + else assert( false ); + return ""; +} + +ObjectImp* SegmentImp::property( uint which, const KigDocument& w ) const +{ + int pnum = 0; + + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, w ); + else if ( which == Parent::numberOfProperties() + pnum++ ) + return new DoubleImp( mdata.dir().length() ); + else if ( which == Parent::numberOfProperties() + pnum++ ) + return new PointImp( ( mdata.a + mdata.b ) / 2 ); + else if ( which == Parent::numberOfProperties() + pnum++ ) + return new PointImp( mdata.a ); + else if ( which == Parent::numberOfProperties() + pnum++ ) + return new PointImp( mdata.b ); + else assert( false ); + return new InvalidImp; +} + +double AbstractLineImp::slope() const +{ + Coordinate diff = mdata.dir(); + return diff.y / diff.x; +} + +const QString AbstractLineImp::equationString() const +{ + Coordinate p = mdata.a; + Coordinate q = mdata.b; + + double m = ( q.y - p.y ) / ( q.x - p.x ); + double r = - ( q.y - p.y ) * p.x / ( q.x - p.x ) + p.y; + + QString ret = QString::fromUtf8( "y = %1x " ) + + QString::fromUtf8( r > 0 ? "+" : "-" ) + + QString::fromUtf8( " %2" ); + + ret = ret.arg( m, 0, 'g', 3 ); + ret = ret.arg( abs( r ), 0, 'g', 3 ); + + return ret; +} + +void SegmentImp::draw( KigPainter& p ) const +{ + p.drawSegment( mdata ); +} + +bool SegmentImp::contains( const Coordinate& p, int width, const KigWidget& w ) const +{ + return internalContainsPoint( p, w.screenInfo().normalMiss( width ) ); +} + +void RayImp::draw( KigPainter& p ) const +{ + p.drawRay( mdata ); +} + +bool RayImp::contains( const Coordinate& p, int width, const KigWidget& w ) const +{ + return internalContainsPoint( p, w.screenInfo().normalMiss( width ) ); +} + +void LineImp::draw( KigPainter& p ) const +{ + p.drawLine( mdata ); +} + +bool LineImp::contains( const Coordinate& p, int width, const KigWidget& w ) const +{ + return internalContainsPoint( p, w.screenInfo().normalMiss( width ) ); +} + +SegmentImp::SegmentImp( const Coordinate& a, const Coordinate& b ) + : AbstractLineImp( a, b ) +{ +} + +RayImp::RayImp( const Coordinate& a, const Coordinate& b ) + : AbstractLineImp( a, b ) +{ +} + +LineImp::LineImp( const Coordinate& a, const Coordinate& b ) + : AbstractLineImp( a, b ) +{ +} + +SegmentImp* SegmentImp::copy() const +{ + return new SegmentImp( mdata ); +} + +RayImp* RayImp::copy() const +{ + return new RayImp( mdata ); +} + +LineImp* LineImp::copy() const +{ + return new LineImp( mdata ); +} + +const Coordinate SegmentImp::getPoint( double param, const KigDocument& ) const +{ + return mdata.a + mdata.dir()*param; +} + +double SegmentImp::getParam( const Coordinate& p, const KigDocument& ) const +{ + Coordinate pt = calcPointOnPerpend( data(), p ); + pt = calcIntersectionPoint( data(), LineData( p, pt ) ); + // if pt is over the end of the segment ( i.e. it's on the line + // which the segment is a part of, but not of the segment itself..; + // ) we set it to one of the end points of the segment... + if ((pt - mdata.a).length() > mdata.dir().length() ) + pt = mdata.b; + else if ( (pt- mdata.b).length() > mdata.dir().length() ) + pt = mdata.a; + if (mdata.b == mdata.a) return 0; + return ((pt - mdata.a).length())/(mdata.dir().length()); +} + +LineData AbstractLineImp::data() const +{ + return mdata; +} + +const Coordinate RayImp::getPoint( double param, const KigDocument& ) const +{ + param = 1.0/param - 1.0; + return mdata.a + mdata.dir()*param; +} + +double RayImp::getParam( const Coordinate& p, const KigDocument& ) const +{ + const LineData ld = data(); + Coordinate pt = calcPointOnPerpend( ld, p ); + pt = calcIntersectionPoint( ld, LineData( p, pt )); + // if pt is over the end of the ray ( i.e. it's on the line + // which the ray is a part of, but not of the ray itself..; + // ) we set it to the start point of the ray... + Coordinate dir = ld.dir(); + pt -= ld.a; + double param; + if ( dir.x != 0 ) param = pt.x / dir.x; + else if ( dir.y != 0 ) param = pt.y / dir.y; + else param = 0.; + if ( param < 0. ) param = 0.; + + // mp: let's try with 1/(x+1), this reverses the mapping, but + // should allow to take advantage of the tightness of floating point + // numbers near zero, in order to get more possible positions near + // infinity + + param = 1./( param + 1. ); + + assert( param >= 0. && param <= 1. ); + return param; +} + +const Coordinate LineImp::getPoint( double p, const KigDocument& ) const +{ + // inspired upon KSeg + + // we need to spread the points over the line, it should also come near + // the (infinite) end of the line, but most points should be near + // the two points we contain... + if ( p <= 0. ) p = 1e-6; + if ( p >= 1. ) p = 1 - 1e-6; + p = 2*p - 1; + if (p > 0) p = p/(1 - p); + else p = p/(1 + p); +// p *= 1024; // such multiplying factor could be useful in order to + // have more points near infinity, at the expense of + // points near ma and mb + return mdata.a + p*mdata.dir(); +} + +double LineImp::getParam( const Coordinate& point, const KigDocument& ) const +{ + // somewhat the reverse of getPoint, although it also supports + // points not on the line... + + Coordinate pa = point - mdata.a; + Coordinate ba = mdata.dir(); + double balsq = ba.x*ba.x + ba.y*ba.y; + assert (balsq > 0); + + double p = (pa.x*ba.x + pa.y*ba.y)/balsq; +// p /= 1024; + if (p > 0) p = p/(1+p); + else p = p/(1-p); + + return 0.5*(p + 1); +} + +ObjectImp* SegmentImp::transform( const Transformation& t ) const +{ + if ( ! t.isAffine() ) /* we need to check the position of the two points */ + { + if ( t.getProjectiveIndicator( mdata.a ) * + t.getProjectiveIndicator( mdata.b ) < 0 ) + return new InvalidImp(); + } + Coordinate na = t.apply( mdata.a ); + Coordinate nb = t.apply( mdata.b ); + if( na.valid() && nb.valid() ) return new SegmentImp( na, nb ); + else return new InvalidImp(); +} + +ObjectImp* LineImp::transform( const Transformation& t ) const +{ + Coordinate na = t.apply( mdata.a ); + Coordinate nb = t.apply( mdata.b ); + if ( na.valid() && nb.valid() ) return new LineImp( na, nb ); + else return new InvalidImp(); +} + +ObjectImp* RayImp::transform( const Transformation& t ) const +{ + if ( ! t.isAffine() ) /* we need to check the position of the two points */ + { + double pa = t.getProjectiveIndicator( mdata.a ); + double pb = t.getProjectiveIndicator( mdata.b ); + if ( pa < 0 ) pb = -pb; + if ( pb < fabs (pa) ) return new InvalidImp(); + Coordinate na = t.apply( mdata.a ); + Coordinate nb = t.apply0( mdata.b - mdata.a ); + if ( na.valid() && nb.valid() ) return new SegmentImp( na, nb ); + else return new InvalidImp(); + } + Coordinate na = t.apply( mdata.a ); + Coordinate nb = t.apply( mdata.b ); + if ( na.valid() && nb.valid() ) return new RayImp( na, nb ); + else return new InvalidImp(); +} + +AbstractLineImp::AbstractLineImp( const LineData& d ) + : mdata( d ) +{ +} + +SegmentImp::SegmentImp( const LineData& d ) + : AbstractLineImp( d ) +{ +} + +RayImp::RayImp( const LineData& d ) + : AbstractLineImp( d ) +{ +} + +LineImp::LineImp( const LineData& d ) + : AbstractLineImp( d ) +{ +} + +double SegmentImp::length() const +{ + return mdata.length(); +} + +void SegmentImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +void RayImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +void LineImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +bool AbstractLineImp::equals( const ObjectImp& rhs ) const +{ + return rhs.type() == type() && + static_cast<const AbstractLineImp&>( rhs ).data() == data(); +} + +const ObjectImpType* AbstractLineImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "line", I18N_NOOP( "line" ), + I18N_NOOP( "Select a Line" ), 0, 0, 0, 0, 0, 0, 0 ); + return &t; +} + +const ObjectImpType* LineImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "line", + I18N_NOOP( "line" ), + I18N_NOOP( "Select this line" ), + I18N_NOOP( "Select line %1" ), + I18N_NOOP( "Remove a Line" ), + I18N_NOOP( "Add a Line" ), + I18N_NOOP( "Move a Line" ), + I18N_NOOP( "Attach to this line" ), + I18N_NOOP( "Show a Line" ), + I18N_NOOP( "Hide a Line" ) + ); + return &t; +} + +const ObjectImpType* SegmentImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "segment", + I18N_NOOP( "segment" ), + I18N_NOOP( "Select this segment" ), + I18N_NOOP( "Select segment %1" ), + I18N_NOOP( "Remove a Segment" ), + I18N_NOOP( "Add a Segment" ), + I18N_NOOP( "Move a Segment" ), + I18N_NOOP( "Attach to this segment" ), + I18N_NOOP( "Show a Segment" ), + I18N_NOOP( "Hide a Segment" ) + ); + return &t; +} + +const ObjectImpType* RayImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "ray", + I18N_NOOP( "half-line" ), + I18N_NOOP( "Select this half-line" ), + I18N_NOOP( "Select half-line %1" ), + I18N_NOOP( "Remove a Half-Line" ), + I18N_NOOP( "Add a Half-Line" ), + I18N_NOOP( "Move a Half-Line" ), + I18N_NOOP( "Attach to this half-line" ), + I18N_NOOP( "Show a Half-Line" ), + I18N_NOOP( "Hide a Half-Line" ) + ); + return &t; +} + +const ObjectImpType* SegmentImp::type() const +{ + return SegmentImp::stype(); +} + +const ObjectImpType* RayImp::type() const +{ + return RayImp::stype(); +} + +const ObjectImpType* LineImp::type() const +{ + return LineImp::stype(); +} + +bool SegmentImp::containsPoint( const Coordinate& p, const KigDocument& ) const +{ + return internalContainsPoint( p, test_threshold ); +} + +bool SegmentImp::internalContainsPoint( const Coordinate& p, double threshold ) const +{ + return isOnSegment( p, mdata.a, mdata.b, threshold ); +} + +bool RayImp::containsPoint( const Coordinate& p, const KigDocument& ) const +{ + return internalContainsPoint( p, test_threshold ); +} + +bool RayImp::internalContainsPoint( const Coordinate& p, double threshold ) const +{ + return isOnRay( p, mdata.a, mdata.b, threshold ); +} + +bool LineImp::containsPoint( const Coordinate& p, const KigDocument& ) const +{ + return internalContainsPoint( p, test_threshold ); +} + +bool LineImp::internalContainsPoint( const Coordinate& p, double threshold ) const +{ + return isOnLine( p, mdata.a, mdata.b, threshold ); +} + +bool AbstractLineImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + int pnum = 0; + + if ( which < Parent::numberOfProperties() ) + return Parent::isPropertyDefinedOnOrThroughThisImp( which ); + else if ( which == Parent::numberOfProperties() + pnum++ ) + return false; + else if ( which == Parent::numberOfProperties() + pnum++ ) + return true; + else if ( which == Parent::numberOfProperties() + pnum++ ) + return true; + else if ( which == Parent::numberOfProperties() + pnum++ ) + return true; + else assert( false ); + return false; +} + +Rect SegmentImp::surroundingRect() const +{ + return Rect( mdata.a, mdata.b ); +} + +Rect RayImp::surroundingRect() const +{ + return Rect::invalidRect(); +} + +Rect LineImp::surroundingRect() const +{ + return Rect::invalidRect(); +} diff --git a/kig/objects/line_imp.h b/kig/objects/line_imp.h new file mode 100644 index 00000000..c9014613 --- /dev/null +++ b/kig/objects/line_imp.h @@ -0,0 +1,215 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_LINE_IMP_H +#define KIG_OBJECTS_LINE_IMP_H + +#include "curve_imp.h" + +#include "../misc/common.h" + +class LineData; + +/** + * An ObjectImp class that is the base of the line-like ObjectImp's: + * SegmentImp, LineImp and RayImp.. + */ +class AbstractLineImp + : public CurveImp +{ +protected: + LineData mdata; + AbstractLineImp( const LineData& d ); + AbstractLineImp( const Coordinate& a, const Coordinate& b ); + +public: + typedef CurveImp Parent; + /** + * Returns the ObjectImpType representing the AbstractLineImp + * type.. + */ + static const ObjectImpType* stype(); + + ~AbstractLineImp(); + + bool inRect( const Rect& r, int width, const KigWidget& ) const; + + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& d ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + /** + * Get the slope of this AbstractLineImp.. For a line through + * points a( xa, ya ) and b ( xb, yb ), this means the value ( yb - + * ya ) / ( xb - xa ). + */ + double slope() const; + /** + * Get a string containing the equation of this line in the form "y + * = a * x + b ". + */ + const QString equationString() const; + /** + * Get the LineData for this AbstractLineImp. + */ + LineData data() const; + + bool equals( const ObjectImp& rhs ) const; +}; + +/** + * An ObjectImp representing a segment + */ +class SegmentImp + : public AbstractLineImp +{ +public: + typedef AbstractLineImp Parent; + /** + * Returns the ObjectImpType representing the SegmentImp + * type.. + */ + static const ObjectImpType* stype(); + + /** + * Construct a new segment from point a to point b. + */ + SegmentImp( const Coordinate& a, const Coordinate& b ); + /** + * Construct a new segment from a LineData. + */ + SegmentImp( const LineData& d ); + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& si ) const; + Rect surroundingRect() const; + + ObjectImp* transform( const Transformation& ) const; + + const Coordinate getPoint( double param, const KigDocument& ) const; + double getParam( const Coordinate&, const KigDocument& ) const; + + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& d ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + + SegmentImp* copy() const; + + /** + * Get the length of this segment. + */ + double length() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool containsPoint( const Coordinate& p, const KigDocument& doc ) const; + bool internalContainsPoint( const Coordinate& p, double threshold ) const; +}; + +/** + * An ObjectImp representing a ray. This means half of a line, it is + * infinite in one direction, but ends at a certain point in the other + * direction.. + */ +class RayImp + : public AbstractLineImp +{ +public: + typedef AbstractLineImp Parent; + /** + * Returns the ObjectImpType representing the RayImp + * type.. + */ + static const ObjectImpType* stype(); + + /** + * Construct a ray, starting at a, and going through b. + */ + RayImp( const Coordinate& a, const Coordinate& b ); + /** + * Construct a ray from a LineData. + */ + RayImp( const LineData& d ); + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& si ) const; + Rect surroundingRect() const; + + ObjectImp* transform( const Transformation& ) const; + + const Coordinate getPoint( double param, const KigDocument& ) const; + double getParam( const Coordinate&, const KigDocument& ) const; + + RayImp* copy() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool containsPoint( const Coordinate& p, const KigDocument& doc ) const; + bool internalContainsPoint( const Coordinate& p, double threshold ) const; +}; + +/** + * An ObjectImp representing a line. + */ +class LineImp + : public AbstractLineImp +{ +public: + typedef AbstractLineImp Parent; + + /** + * Returns the ObjectImpType representing the LineImp + * type.. + */ + static const ObjectImpType* stype(); + + /** + * Construct a LineImp going through points a and b. + */ + LineImp( const Coordinate& a, const Coordinate& b ); + /** + * Construct a LineImp from a LineData. + */ + LineImp( const LineData& d ); + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& si ) const; + Rect surroundingRect() const; + + ObjectImp* transform( const Transformation& ) const; + + const Coordinate getPoint( double param, const KigDocument& ) const; + double getParam( const Coordinate&, const KigDocument& ) const; + + LineImp* copy() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool containsPoint( const Coordinate& p, const KigDocument& doc ) const; + bool internalContainsPoint( const Coordinate& p, double threshold ) const; +}; + +#endif diff --git a/kig/objects/line_type.cc b/kig/objects/line_type.cc new file mode 100644 index 00000000..a2c0734b --- /dev/null +++ b/kig/objects/line_type.cc @@ -0,0 +1,334 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "line_type.h" + +#include "bogus_imp.h" +#include "line_imp.h" +#include "object_holder.h" +#include "other_imp.h" +#include "point_imp.h" + +#include "../kig/kig_view.h" +#include "../kig/kig_part.h" +#include "../kig/kig_commands.h" +#include "../misc/common.h" +#include "../misc/calcpaths.h" + +#include <qstringlist.h> + +#include <klocale.h> + +static const ArgsParser::spec argsspecSegmentAB[] = +{ + { PointImp::stype(), I18N_NOOP( "Construct a segment starting at this point" ), + I18N_NOOP( "Select the start point of the new segment..." ), true }, + { PointImp::stype(), I18N_NOOP( "Construct a segment ending at this point" ), + I18N_NOOP( "Select the end point of the new segment..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( SegmentABType ) + +SegmentABType::SegmentABType() + : ObjectABType( "SegmentAB", argsspecSegmentAB, 2 ) +{ +} + +SegmentABType::~SegmentABType() +{ +} + +const SegmentABType* SegmentABType::instance() +{ + static const SegmentABType s; + return &s; +} + +ObjectImp* SegmentABType::calc( const Coordinate& a, const Coordinate& b ) const +{ + return new SegmentImp( a, b ); +} + +static const char constructlineabstat[] = I18N_NOOP( "Construct a line through this point" ); + +static const ArgsParser::spec argsspecLineAB[] = +{ + { PointImp::stype(), constructlineabstat, + I18N_NOOP( "Select a point for the line to go through..." ), true }, + { PointImp::stype(), constructlineabstat, + I18N_NOOP( "Select another point for the line to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( LineABType ) + +LineABType::LineABType() + : ObjectABType( "LineAB", argsspecLineAB, 2 ) +{ +} + +LineABType::~LineABType() +{ +} + +const LineABType* LineABType::instance() +{ + static const LineABType s; + return &s; +} + +ObjectImp* LineABType::calc( const Coordinate& a, const Coordinate& b ) const +{ + return new LineImp( a, b ); +} + +static const char constructhalflinestartingstat[] = I18N_NOOP( "Construct a half-line starting at this point" ); + +static const ArgsParser::spec argsspecRayAB[] = +{ + { PointImp::stype(), constructhalflinestartingstat, + I18N_NOOP( "Select the start point of the new half-line..." ), true }, + { PointImp::stype(), I18N_NOOP( "Construct a half-line through this point" ), + I18N_NOOP( "Select a point for the half-line to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( RayABType ) + +RayABType::RayABType() + : ObjectABType( "RayAB", argsspecRayAB, 2 ) +{ +} + +RayABType::~RayABType() +{ +} + +const RayABType* RayABType::instance() +{ + static const RayABType s; + return &s; +} + +ObjectImp* RayABType::calc( const Coordinate& a, const Coordinate& b ) const +{ + return new RayImp( a, b ); +} + +LinePerpendLPType* LinePerpendLPType::instance() +{ + static LinePerpendLPType l; + return &l; +} + +ObjectImp* LinePerpendLPType::calc( + const LineData& a, + const Coordinate& b ) const +{ + Coordinate p = calcPointOnPerpend( a, b ); + return new LineImp( b, p ); +} + +static const ArgsParser::spec argsspecLineParallel[] = +{ + { AbstractLineImp::stype(), I18N_NOOP( "Construct a line parallel to this line" ), + I18N_NOOP( "Select a line parallel to the new line..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct the parallel line through this point" ), + I18N_NOOP( "Select a point for the new line to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( LineParallelLPType ) + +LineParallelLPType::LineParallelLPType() + : ObjectLPType( "LineParallel", argsspecLineParallel, 2 ) +{ +} + +LineParallelLPType::~LineParallelLPType() +{ +} + +LineParallelLPType* LineParallelLPType::instance() +{ + static LineParallelLPType l; + return &l; +} + +ObjectImp* LineParallelLPType::calc( + const LineData& a, + const Coordinate& b ) const +{ + Coordinate r = calcPointOnParallel( a, b ); + return new LineImp( r, b ); +} + +static const ArgsParser::spec argsspecLinePerpend[] = +{ + { AbstractLineImp::stype(), I18N_NOOP( "Construct a line perpendicular to this line" ), + I18N_NOOP( "Select a line perpendicular to the new line..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct a perpendicular line through this point" ), + I18N_NOOP( "Select a point for the new line to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( LinePerpendLPType ) + +LinePerpendLPType::LinePerpendLPType() + : ObjectLPType( "LinePerpend", argsspecLinePerpend, 2 ) +{ +} + +LinePerpendLPType::~LinePerpendLPType() +{ +} + +const ObjectImpType* SegmentABType::resultId() const +{ + return SegmentImp::stype(); +} + +const ObjectImpType* LineABType::resultId() const +{ + return LineImp::stype(); +} + +const ObjectImpType* RayABType::resultId() const +{ + return RayImp::stype(); +} + +const ObjectImpType* LinePerpendLPType::resultId() const +{ + return LineImp::stype(); +} + +const ObjectImpType* LineParallelLPType::resultId() const +{ + return LineImp::stype(); +} + +QStringList SegmentABType::specialActions() const +{ + QStringList ret; + ret << i18n( "Set &Length..." ); + return ret; +} + +void SegmentABType::executeAction( int i, ObjectHolder&, ObjectTypeCalcer& c, + KigPart& d, KigWidget& w, NormalMode& ) const +{ + assert( i == 0 ); + // pretend to use this var.. + (void) i; + + std::vector<ObjectCalcer*> parents = c.parents(); + assert( margsparser.checkArgs( parents ) ); + + Coordinate a = static_cast<const PointImp*>( parents[0]->imp() )->coordinate(); + Coordinate b = static_cast<const PointImp*>( parents[1]->imp() )->coordinate(); + + bool ok = true; + double length = getDoubleFromUser( + i18n( "Set Segment Length" ), i18n( "Choose the new length: " ), + (b-a).length(), &w, &ok, -2147483647, 2147483647, 3 ); + if ( ! ok ) return; + + Coordinate nb = a + ( b - a ).normalize( length ); + + MonitorDataObjects mon( getAllParents( parents ) ); + parents[1]->move( nb, d.document() ); + KigCommand* cd = new KigCommand( d, i18n( "Resize Segment" ) ); + mon.finish( cd ); + d.history()->addCommand( cd ); +} + +static const ArgsParser::spec argsspecLineByVector[] = +{ + { VectorImp::stype(), I18N_NOOP( "Construct a line by this vector" ), + I18N_NOOP( "Select a vector in the direction of the new line..." ), true }, + { PointImp::stype(), constructlineabstat, + I18N_NOOP( "Select a point for the new line to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( LineByVectorType ) + +LineByVectorType::LineByVectorType() + : ArgsParserObjectType( "LineByVector", argsspecLineByVector, 2 ) +{ +} + +LineByVectorType::~LineByVectorType() +{ +} + +const LineByVectorType* LineByVectorType::instance() +{ + static const LineByVectorType s; + return &s; +} + +ObjectImp* LineByVectorType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + const VectorImp& a = *static_cast<const VectorImp*>( args[0] ); + const PointImp& b = *static_cast<const PointImp*>( args[1] ); + + return new LineImp( b.coordinate(), b.coordinate() + a.dir() ); +} + +const ObjectImpType* LineByVectorType::resultId() const +{ + return LineImp::stype(); +} + +static const ArgsParser::spec argsspecHalflineByVector[] = +{ + { VectorImp::stype(), I18N_NOOP( "Construct a half-line by this vector" ), + I18N_NOOP( "Select a vector in the direction of the new half-line..." ), true }, + { PointImp::stype(), constructhalflinestartingstat, + I18N_NOOP( "Select the start point of the new half-line..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( HalflineByVectorType ) + +HalflineByVectorType::HalflineByVectorType() + : ArgsParserObjectType( "HalflineByVector", argsspecHalflineByVector, 2 ) +{ +} + +HalflineByVectorType::~HalflineByVectorType() +{ +} + +const HalflineByVectorType* HalflineByVectorType::instance() +{ + static const HalflineByVectorType s; + return &s; +} + +ObjectImp* HalflineByVectorType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + const VectorImp& a = *static_cast<const VectorImp*>( args[0] ); + const PointImp& b = *static_cast<const PointImp*>( args[1] ); + + return new RayImp( b.coordinate(), b.coordinate() + a.dir() ); +} + +const ObjectImpType* HalflineByVectorType::resultId() const +{ + return RayImp::stype(); +} diff --git a/kig/objects/line_type.h b/kig/objects/line_type.h new file mode 100644 index 00000000..a3ded322 --- /dev/null +++ b/kig/objects/line_type.h @@ -0,0 +1,110 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_SEGMENT_H +#define KIG_OBJECTS_SEGMENT_H + +#include "base_type.h" + +class LineData; + +class SegmentABType + : public ObjectABType +{ + SegmentABType(); + ~SegmentABType(); +public: + static const SegmentABType* instance(); + + ObjectImp* calc( const Coordinate& a, const Coordinate& b ) const; + const ObjectImpType* resultId() const; + + QStringList specialActions() const; + /** + * execute the \p i 'th action from the specialActions above.. + */ + void executeAction( int i, ObjectHolder& o, ObjectTypeCalcer& c, + KigPart& d, KigWidget& w, NormalMode& m ) const; +}; + +class LineABType + : public ObjectABType +{ + LineABType(); + ~LineABType(); +public: + static const LineABType* instance(); + ObjectImp* calc( const Coordinate& a, const Coordinate& b ) const; + const ObjectImpType* resultId() const; +}; + +class RayABType + : public ObjectABType +{ + RayABType(); + ~RayABType(); +public: + static const RayABType* instance(); + ObjectImp* calc( const Coordinate& a, const Coordinate& b ) const; + const ObjectImpType* resultId() const; +}; + +class LinePerpendLPType + : public ObjectLPType +{ + LinePerpendLPType(); + ~LinePerpendLPType(); +public: + static LinePerpendLPType* instance(); + ObjectImp* calc( const LineData& a, const Coordinate& b ) const; + const ObjectImpType* resultId() const; +}; + +class LineParallelLPType + : public ObjectLPType +{ + LineParallelLPType(); + ~LineParallelLPType(); +public: + static LineParallelLPType* instance(); + ObjectImp* calc( const LineData& a, const Coordinate& b ) const; + const ObjectImpType* resultId() const; +}; + +class LineByVectorType + : public ArgsParserObjectType +{ + LineByVectorType(); + ~LineByVectorType(); +public: + static const LineByVectorType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class HalflineByVectorType + : public ArgsParserObjectType +{ + HalflineByVectorType(); + ~HalflineByVectorType(); +public: + static const HalflineByVectorType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/locus_imp.cc b/kig/objects/locus_imp.cc new file mode 100644 index 00000000..edbdc88b --- /dev/null +++ b/kig/objects/locus_imp.cc @@ -0,0 +1,397 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "locus_imp.h" + +#include "bogus_imp.h" +#include "point_imp.h" +#include "../misc/object_hierarchy.h" +#include "../misc/kigpainter.h" +#include "../misc/coordinate.h" +#include "../misc/common.h" + +#include "../kig/kig_view.h" + +#include <klocale.h> + +#include <cmath> + +using namespace std; + +static double cachedparam = 0.0; + +LocusImp::~LocusImp() +{ + delete mcurve; +} + +ObjectImp* LocusImp::transform( const Transformation& t ) const +{ + return new LocusImp( mcurve->copy(), mhier.transformFinalObject( t ) ); +} + +void LocusImp::draw( KigPainter& p ) const +{ + p.drawCurve( this ); +} + +bool LocusImp::contains( const Coordinate& p, int width, const KigWidget& w ) const +{ + return internalContainsPoint( p, w.screenInfo().normalMiss( width ), w.document() ); +} + +bool LocusImp::inRect( const Rect&, int, const KigWidget& ) const +{ + // TODO ? + return false; +} + +const Coordinate LocusImp::getPoint( double param, const KigDocument& doc ) const +{ + Coordinate arg = mcurve->getPoint( param, doc ); + if ( ! arg.valid() ) return arg; + PointImp argimp( arg ); + Args args; + args.push_back( &argimp ); + vector<ObjectImp*> calcret = mhier.calc( args, doc ); + assert( calcret.size() == 1 ); + ObjectImp* imp = calcret.front(); + Coordinate ret; + if ( imp->inherits( PointImp::stype() ) ) + { + cachedparam = param; + ret = static_cast<PointImp*>( imp )->coordinate(); + } + else + ret = Coordinate::invalidCoord(); + + delete imp; + return ret; +} + +LocusImp::LocusImp( CurveImp* curve, const ObjectHierarchy& hier ) + : mcurve( curve ), mhier( hier ) +{ +} + +const uint LocusImp::numberOfProperties() const +{ + return Parent::numberOfProperties(); +} + +const QCStringList LocusImp::propertiesInternalNames() const +{ + return Parent::propertiesInternalNames(); +} + +const QCStringList LocusImp::properties() const +{ + return Parent::properties(); +} + +const ObjectImpType* LocusImp::impRequirementForProperty( uint which ) const +{ + return Parent::impRequirementForProperty( which ); +} + +const char* LocusImp::iconForProperty( uint which ) const +{ + return Parent::iconForProperty( which ); +} + +ObjectImp* LocusImp::property( uint which, const KigDocument& w ) const +{ + return Parent::property( which, w ); +} + +LocusImp* LocusImp::copy() const +{ + return new LocusImp( mcurve->copy(), mhier ); +} + +const CurveImp* LocusImp::curve() const +{ + return mcurve; +} + +const ObjectHierarchy& LocusImp::hierarchy() const +{ + return mhier; +} + +/** + * This function returns the distance between the point with parameter + * param and point p. param is allowed to not be between 0 and 1, in + * which case we consider only the decimal part. + */ +double LocusImp::getDist(double param, const Coordinate& p, const KigDocument& doc) const +{ + param = fmod( param, 1 ); + if( param < 0 ) param += 1.; + Coordinate p1 = getPoint( param, doc ); + // i don't think the p1.valid() switch is really necessary, but I + // prefer to not take any chances :) + return p1.valid() ? ( p1 - p ).length() : +double_inf; +} + +/** + * This function searches starting from x1 for the first interval in + * which the function of the distance from the point at coordinate x + * starts to increase. The range found is returned in the parameters + * x1 and x2: [x1,x2]. + */ +void LocusImp::getInterval( double& x1, double& x2, + double incr,const Coordinate& p, + const KigDocument& doc) const +{ + double mm = getDist( x1, p, doc); + double mm1 = getDist( x2, p, doc); + if( mm <= mm1 ) return; + else + { + double x3 = x2 + incr; + double mm2 = getDist (x3, p, doc); + while( mm > mm1 & mm1 > mm2 ) + { + x1 = x2; + x2 = x3; + x3 = x2 + incr; + mm = mm1; + mm1 = mm2; + mm2 = getDist (x3, p, doc); + } + x2=x3; + } +} + +double LocusImp::getParam( const Coordinate& p, const KigDocument& doc ) const +{ + // this function ( and related functions like getInterval etc. ) is + // written by Franco Pasquarelli <pasqui@dmf.bs.unicatt.it>. + // I ( domi ) have adapted and documented it a bit. + + if ( cachedparam >= 0. && cachedparam <= 1. && + getPoint ( cachedparam, doc ) == p ) return cachedparam; + + // consider the function that returns the distance for a point at + // parameter x to the locus for a given parameter x. What we do + // here is look for the global minimum of this function. We do that + // by dividing the range ( [0,1] ) into N parts, and start looking + // for a local minimum from there on. If we find one, we keep it if + // it is the lowest of all the ones we've already found.. + + const int N = 50; + const double incr = 1. / (double) N; + + // xm is the best parameter we've found so far, fxm is the distance + // to the locus from that point. We start with some + // pseudo-values. + // (mp) note that if the distance is actually increasing in the + // whole interval [0,1] this value will be returned in the end. + double xm = 0.; + double fxm = getDist( xm, p, doc ); + + int j = 0; + double mm = fxm; + + while( j < N ) + { + // [x1,x2] is the range we're currently considering.. + double x1 = j * incr; + double x2 = x1 + incr; + + // check the range x1,x2 for the first local maximum.. + double mm1 = getDist( x2, p, doc); + double mm2; + j++; + if( mm < mm1 ) + mm = mm1; + + else + { + if ( mm > mm1 ) + { + double x3 = x2 + incr; + mm2 = getDist (x3, p, doc); + j++; + while( mm1 > mm2 & j <= N ) + { + x1 = x2; + x2 = x3; + x3 = x2 + incr; + mm = mm1; + mm1 = mm2; + mm2 = getDist (x3, p, doc); + j++; + } + x2 = x3; + } + else + mm2 = mm1; + + if ( mm1 <= mm2 ) + { + mm = mm2; + + double xm1 = getParamofmin( x1, x2, p, doc); + double fxm1 = getDist( xm1, p, doc ); + if( fxm1 < fxm ) + { + // we found a new minimum, save it.. + xm=xm1; + fxm=fxm1; + } + } + } + } + return xm; +} + +/** + * This function calculates the parameter of the point that realizes the + * minimum in [a,b] of the distance between the points of the locus and + * the point of coordinate p, using the golden ration method. + */ +double LocusImp::getParamofmin( double a, double b, + const Coordinate& p, + const KigDocument& doc ) const +{ + double epsilons = 1.e-08; + double epsilonl = 2.e-02; + + //assert( a < b && a >= 0. && b <= 1.0); + assert( a < b && a >= 0.); + + double r2 = ( sqrt( 5. ) - 1 ) / 2.; // golden ratio + double r1 = 1. - r2; + + double t2 = a + r2 * ( b - a ); + double t1 = a + r1 * ( b - a ); + Coordinate p1 = getPoint( fmod( t1, 1. ), doc); + double f1 = (p1 - p).length(); + Coordinate p2 = getPoint( fmod( t2, 1. ), doc); + double f2 = (p2 - p).length(); + + double fmin, tmin; + if (f1 < f2) + { + b = t2; + fmin = f1; + tmin = t1; + } + else + { + a = t1; + fmin = f2; + tmin = t2; + } + + while ( ( b - a ) > epsilons && + ( (p1 - p2).length() > 0.4 * fmin + || (b - a) > epsilonl) && + fmin > 1.e-8 ) + { + if ( f1 < f2 ) + { + t2 = t1; + t1 = a + r1*(b - a); + f2 = f1; + p1 = getPoint( fmod( t1, 1. ), doc); + f1 = (p1 - p).length(); + } + else + { + t1 = t2; + t2 = a + r2*(b - a); + f1 = f2; + p2 = getPoint( fmod( t2, 1. ), doc); + f2 = (p2 - p).length(); + } + if ( f1 < f2 ) + { + b = t2; + fmin = f1; + tmin = t1; + } + else + { + a = t1; + fmin = f2; + tmin = t2; + } + } + + return(tmin); +} + +void LocusImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +bool LocusImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( LocusImp::stype() ) && + static_cast<const LocusImp&>( rhs ).curve()->equals( *curve() ) && + static_cast<const LocusImp&>( rhs ).hierarchy() == hierarchy(); +} + +const ObjectImpType* LocusImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "locus", + I18N_NOOP( "locus" ), + I18N_NOOP( "Select this locus" ), + I18N_NOOP( "Select locus %1" ), + I18N_NOOP( "Remove a Locus" ), + I18N_NOOP( "Add a Locus" ), + I18N_NOOP( "Move a Locus" ), + I18N_NOOP( "Attach to this locus" ), + I18N_NOOP( "Show a Locus" ), + I18N_NOOP( "Hide a Locus" ) + ); + return &t; +} + +const ObjectImpType* LocusImp::type() const +{ + return LocusImp::stype(); +} + +bool LocusImp::containsPoint( const Coordinate& p, const KigDocument& doc ) const +{ + return internalContainsPoint( p, test_threshold, doc ); +} + +bool LocusImp::internalContainsPoint( const Coordinate& p, double threshold, const KigDocument& doc ) const +{ + double param = getParam( p, doc ); + double dist = getDist( param, p, doc ); + return fabs( dist ) <= threshold; +} + +bool LocusImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + return Parent::isPropertyDefinedOnOrThroughThisImp( which ); +} + +Rect LocusImp::surroundingRect() const +{ + // it's probably possible to calculate this, if it exists, but we + // don't for now. + return Rect::invalidRect(); +} diff --git a/kig/objects/locus_imp.h b/kig/objects/locus_imp.h new file mode 100644 index 00000000..568e0e7c --- /dev/null +++ b/kig/objects/locus_imp.h @@ -0,0 +1,96 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_LOCUS_IMP_H +#define KIG_OBJECTS_LOCUS_IMP_H + +#include "curve_imp.h" +#include "../misc/object_hierarchy.h" + +/** + * LocusImp is an imp that consists of a copy of the curveimp that the + * moving point moves over, and an ObjectHierarchy that can calc ( + * given a point, and optionally some more parent objects the position + * of the moving point. The hierarchy should take the moving point as + * its *first* argument and all others after that. The others are + * used to make it possible for Locus to be updated when some of the + * other objects that appear in the path from the moving point to the + * dependent point change. + * + * This may seem rather complicated, but I think it is absolutely + * necessary to support locuses using Kig's object system. It would + * be very bad for LocusImp to have to keep references to its parents + * as Objects ( since only the objects know how they are related to + * their parents ). This is how we used to do it, but the current + * method is far superior. First and foremost because the separation + * between ObjectImp and Object is something that Kig depends on very + * much, and because every ObjectImp should contain all the data it + * needs itself. ObjectImp's are entirely independent objects. + * That's also why we don't keep a pointer to the old CurveImp, but a + * copy of it.. + * + * i hope this is a bit clear, if not, feel free to ask for + * explanation of what you don't understand.. + */ +class LocusImp + : public CurveImp +{ + CurveImp* mcurve; + const ObjectHierarchy mhier; + + double getDist(double param, const Coordinate& p, const KigDocument& doc) const; + void getInterval(double& x1,double& x2,double incr,const Coordinate& p, const KigDocument& doc) const; + double getParamofmin(double a, double b, const Coordinate& p, const KigDocument& doc) const; +public: + typedef CurveImp Parent; + static const ObjectImpType* stype(); + + LocusImp( CurveImp*, const ObjectHierarchy& ); + ~LocusImp(); + LocusImp* copy() const; + + ObjectImp* transform( const Transformation& ) const; + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& ) const; + Rect surroundingRect() const; + bool inRect( const Rect& r, int width, const KigWidget& ) const; + double getParam( const Coordinate& point, const KigDocument& ) const; + const Coordinate getPoint( double param, const KigDocument& ) const; + + // TODO ? + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& w ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + const CurveImp* curve() const; + const ObjectHierarchy& hierarchy() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool equals( const ObjectImp& rhs ) const; + + bool containsPoint( const Coordinate& p, const KigDocument& d ) const; + bool internalContainsPoint( const Coordinate& p, double threshold, const KigDocument& doc ) const; +}; + +#endif diff --git a/kig/objects/object_calcer.cc b/kig/objects/object_calcer.cc new file mode 100644 index 00000000..40545ed1 --- /dev/null +++ b/kig/objects/object_calcer.cc @@ -0,0 +1,323 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "object_calcer.h" + +#include "object_holder.h" +#include "object_imp.h" +#include "object_type.h" +#include "../misc/coordinate.h" +#include "common.h" + +#include <algorithm> +#include <set> + +void ObjectTypeCalcer::calc( const KigDocument& doc ) +{ + Args a; + a.reserve( mparents.size() ); + std::transform( mparents.begin(), mparents.end(), + std::back_inserter( a ), std::mem_fun( &ObjectCalcer::imp ) ); + ObjectImp* n = mtype->calc( a, doc ); + delete mimp; + mimp = n; +} + +ObjectTypeCalcer::ObjectTypeCalcer( const ObjectType* type, + const std::vector<ObjectCalcer*>& parents, bool sort ) + : mparents( ( sort )?type->sortArgs( parents ):parents ), mtype( type ), mimp( 0 ) +{ + std::for_each( mparents.begin(), mparents.end(), + std::bind2nd( std::mem_fun( &ObjectCalcer::addChild ), this ) ); +} + +ObjectCalcer::~ObjectCalcer() +{ +} + +ObjectConstCalcer::ObjectConstCalcer( ObjectImp* imp ) + : mimp( imp ) +{ +} + +ObjectConstCalcer::~ObjectConstCalcer() +{ + delete mimp; +} + +const ObjectImp* ObjectConstCalcer::imp() const +{ + return mimp; +} + +void ObjectConstCalcer::calc( const KigDocument& ) +{ +} + +std::vector<ObjectCalcer*> ObjectConstCalcer::parents() const +{ + // we have no parents.. + return std::vector<ObjectCalcer*>(); +} + +void ObjectCalcer::ref() +{ + ++refcount; +} + +void ObjectCalcer::deref() +{ + if ( --refcount <= 0 ) delete this; +} + +void intrusive_ptr_add_ref( ObjectCalcer* p ) +{ + p->ref(); +} + +void intrusive_ptr_release( ObjectCalcer* p ) +{ + p->deref(); +} + +const ObjectImp* ObjectTypeCalcer::imp() const +{ + return mimp; +} + +std::vector<ObjectCalcer*> ObjectTypeCalcer::parents() const +{ + return mparents; +} + +void ObjectCalcer::addChild( ObjectCalcer* c ) +{ + mchildren.push_back( c ); + ref(); +} + +void ObjectCalcer::delChild( ObjectCalcer* c ) +{ + std::vector<ObjectCalcer*>::iterator i = std::find( mchildren.begin(), mchildren.end(), c ); + assert( i != mchildren.end() ); + + mchildren.erase( i ); + deref(); +} + +ObjectTypeCalcer::~ObjectTypeCalcer() +{ + std::for_each( mparents.begin(), mparents.end(), + std::bind2nd( std::mem_fun( &ObjectCalcer::delChild ), this ) ); + delete mimp; +} + +const ObjectType* ObjectTypeCalcer::type() const +{ + return mtype; +} + +ObjectPropertyCalcer::ObjectPropertyCalcer( ObjectCalcer* parent, int propid ) + : mimp( 0 ), mparent( parent ), mpropid( propid ) +{ + // Some weird C++ thing prevents me from calling protected members + // of ObjectCalcer on mparent.. This is an ugly workaround.. + ( mparent->*&ObjectCalcer::addChild )( this ); + //mparent->addChild( this ); +} + +ObjectPropertyCalcer::~ObjectPropertyCalcer() +{ + // Some weird C++ thing prevents me from calling protected members + // of ObjectCalcer on mparent.. This is an ugly workaround.. + ( mparent->*&ObjectCalcer::delChild )( this ); + //mparent->delChild( this ); + delete mimp; +} + +const ObjectImp* ObjectPropertyCalcer::imp() const +{ + return mimp; +} + +std::vector<ObjectCalcer*> ObjectPropertyCalcer::parents() const +{ + std::vector<ObjectCalcer*> ret; + ret.push_back( mparent ); + return ret; +} + +void ObjectPropertyCalcer::calc( const KigDocument& doc ) +{ + ObjectImp* n = mparent->imp()->property( mpropid, doc ); + delete mimp; + mimp = n; +} + +ObjectImp* ObjectConstCalcer::switchImp( ObjectImp* newimp ) +{ + ObjectImp* ret = mimp; + mimp = newimp; + return ret; +} + +std::vector<ObjectCalcer*> ObjectCalcer::children() const +{ + return mchildren; +} + +const ObjectImpType* ObjectPropertyCalcer::impRequirement( + ObjectCalcer*, const std::vector<ObjectCalcer*>& ) const +{ + return mparent->imp()->impRequirementForProperty( mpropid ); +} + +const ObjectImpType* ObjectConstCalcer::impRequirement( + ObjectCalcer*, const std::vector<ObjectCalcer*>& ) const +{ + assert( false ); + return ObjectImp::stype(); +} + +const ObjectImpType* ObjectTypeCalcer::impRequirement( + ObjectCalcer* o, const std::vector<ObjectCalcer*>& os ) const +{ + Args args; + args.reserve( mparents.size() ); + std::transform( + os.begin(), os.end(), + std::back_inserter( args ), + std::mem_fun( &ObjectCalcer::imp ) ); + assert( std::find( args.begin(), args.end(), o->imp() ) != args.end() ); + return mtype->impRequirement( o->imp(), args ); +} + +int ObjectPropertyCalcer::propId() const +{ + return mpropid; +} + +void ObjectConstCalcer::setImp( ObjectImp* newimp ) +{ + delete switchImp( newimp ); +} + +void ObjectTypeCalcer::setParents( const std::vector<ObjectCalcer*> np ) +{ + std::for_each( np.begin(), np.end(), + std::bind2nd( std::mem_fun( &ObjectCalcer::addChild ), this ) ); + std::for_each( mparents.begin(), mparents.end(), + std::bind2nd( std::mem_fun( &ObjectCalcer::delChild ), this ) ); + mparents = np; +} + +void ObjectTypeCalcer::setType( const ObjectType* t ) +{ + mtype = t; +} + +bool ObjectCalcer::canMove() const +{ + return false; +} + +bool ObjectCalcer::isFreelyTranslatable() const +{ + return false; +} + +Coordinate ObjectCalcer::moveReferencePoint() const +{ + assert( false ); + return Coordinate::invalidCoord(); +} + +void ObjectCalcer::move( const Coordinate&, const KigDocument& ) +{ + assert( false ); +} + +bool ObjectTypeCalcer::canMove() const +{ + return mtype->canMove( *this ); +} + +bool ObjectTypeCalcer::isFreelyTranslatable() const +{ + return mtype->isFreelyTranslatable( *this ); +} + +Coordinate ObjectTypeCalcer::moveReferencePoint() const +{ + return mtype->moveReferencePoint( *this ); +} + +void ObjectTypeCalcer::move( const Coordinate& to, const KigDocument& doc ) +{ + // we need to check if type can in fact move, because this check is + // not done for us in all circumstances ( e.g. LineABType::move uses + // move on its parents to move them ), and the ObjectType's depend + // on move only being called if canMove() returns true.. + if ( mtype->canMove( *this ) ) + mtype->move( *this, to, doc ); +} + +ObjectCalcer* ObjectPropertyCalcer::parent() const +{ + return mparent; +} + +ObjectCalcer::ObjectCalcer() + : refcount( 0 ) +{ +} + +std::vector<ObjectCalcer*> ObjectCalcer::movableParents() const +{ + return std::vector<ObjectCalcer*>(); +} + +std::vector<ObjectCalcer*> ObjectTypeCalcer::movableParents() const +{ + return mtype->movableParents( *this ); +} + +bool ObjectConstCalcer::isDefinedOnOrThrough( const ObjectCalcer* ) const +{ + return false; +} + +bool ObjectPropertyCalcer::isDefinedOnOrThrough( const ObjectCalcer* o ) const +{ + return o == mparent && + mparent->imp()->isPropertyDefinedOnOrThroughThisImp( propId() ); +} + +bool ObjectTypeCalcer::isDefinedOnOrThrough( const ObjectCalcer* o ) const +{ + Args args; + args.reserve( mparents.size() ); + std::transform( + mparents.begin(), mparents.end(), + std::back_inserter( args ), + std::mem_fun( &ObjectCalcer::imp ) ); + if ( std::find( args.begin(), args.end(), o->imp() ) == args.end() ) + return false; + + return mtype->isDefinedOnOrThrough( o->imp(), args ); +} + diff --git a/kig/objects/object_calcer.h b/kig/objects/object_calcer.h new file mode 100644 index 00000000..6df94fe8 --- /dev/null +++ b/kig/objects/object_calcer.h @@ -0,0 +1,301 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_OBJECT_CALCER_H +#define KIG_OBJECTS_OBJECT_CALCER_H + +#include "common.h" +#include "../misc/boost_intrusive_pointer.hpp" + +class ObjectCalcer; + +void intrusive_ptr_add_ref( ObjectCalcer* p ); +void intrusive_ptr_release( ObjectCalcer* p ); + +/** + * An ObjectCalcer is an object that represents an algorithm for + * calculating an ObjectImp from other ObjectImp's. It is also a node + * in the dependency graph of a certain document. E.g. a LineImp can + * be calculated from the two PointImp's it has to go through; every + * time either of them moves, this calculation is redone. In this + * case, there would be an ObjectCalcer that keeps a reference to its + * two parents ( the ObjectCalcer's representing the points ), and + * that will calculate its ObjectImp value every time it is asked to + * do so ( i.e. every time one of its parents moves.. ). + * + * Each ObjectHolder keeps its ObjectImp itself, and recalculates it + * from its parents in its calc() method ( if necessary ). + * + * Because of the complex relations that ObjectCalcer's hold to other + * ObjectCalcer's and to other classes, they have been made + * reference-counted. This means that they keep a count internally of + * how much times a pointer to them is held. If this count reaches 0, + * this means that nobody needs them anymore, and they delete + * themselves. E.g. an ObjectCalcer always keeps a reference to its + * parents, to ensure that those aren't deleted before it is deleted. + * + * At runtime, there will be an entire graph of ObjectCalcer that + * depend on their parents.. At the bottom, there are Calcer's that + * the user is aware of, and that are held by ObjectHolder's. At the + * top, there are Calcer's without parents that serve only to hold + * some data. Those are most likely ObjectConstCalcer's. There are + * some algorithms to work with the dependency graph in various ways + * in ../misc/calcpath.h + * + * ObjectCalcer's also decide how an object should be allowed to + * move. If the user selects a point, and tries to move it, then its + * ObjectCalcer will be asked whether it can move, and if so, will be + * asked to move. See below with the canMove(), move() and + * moveReferencePoint() methods.. + */ +class ObjectCalcer +{ +protected: + /** + * ObjectCalcer's are reference counted.. They all take a reference + * to their parents, and some other classes like ObjectHolder take a + * reference to some ObjectCalcer's that they don't want to see + * deleted.. + */ + friend void intrusive_ptr_add_ref( ObjectCalcer* p ); + friend void intrusive_ptr_release( ObjectCalcer* p ); + int refcount; + void ref(); + void deref(); + + // we keep track of our children, so algorithms can easily walk over + // the dependency graph.. + + std::vector<ObjectCalcer*> mchildren; + + ObjectCalcer(); +public: + /** + * a calcer should call this to register itself as a child of this + * calcer. This automatically takes a reference. + */ + void addChild( ObjectCalcer* c ); + /** + * a calcer should call this in its destructor, to inform its parent + * that it is no longer a child of this calcer. This will release + * the reference taken in addChild.. + */ + void delChild( ObjectCalcer* c ); + + // use this pointer type to keep a reference to an ObjectCalcer... + typedef myboost::intrusive_ptr<ObjectCalcer> shared_ptr; + + /** + * Returns the child ObjectCalcer's of this ObjectCalcer. + */ + std::vector<ObjectCalcer*> children() const; + + virtual ~ObjectCalcer(); + /** + * Returns the parent ObjectCalcer's of this ObjectCalcer. + */ + virtual std::vector<ObjectCalcer*> parents() const = 0; + /** + * Returns the ObjectImp of this ObjectCalcer. + */ + virtual const ObjectImp* imp() const = 0; + /** + * Makes the ObjectCalcer recalculate its ObjectImp from its + * parents. + */ + virtual void calc( const KigDocument& ) = 0; + + /** + * An ObjectCalcer expects its parents to have an ObjectImp of a + * certain type. This method returns the ObjectImpType that \p o + * should have. \p os is a list of all the parents in order, and + * \p o is part of it. This method will return the ObjectImpType + * that the parent should *at least* be. For example, a Translated + * object can translate any sort of object, so it will return + * ObjectImp::stype() here ( the topmost ObjectImpType, that all + * other ObjectImpType's inherit ). + */ + virtual const ObjectImpType* impRequirement( + ObjectCalcer* o, const std::vector<ObjectCalcer*>& os ) const = 0; + + /** + * Returns whether this ObjectCalcer supports moving. + */ + virtual bool canMove() const; + /** + * Returns whether this ObjectCalcer can be translated at any position + * in the coordinate plane. Note that a ConstrainedPointType can be + * moved, but cannot be moved everywhere. + */ + virtual bool isFreelyTranslatable() const; + /** + * Moving an object most of the time signifies invoking changes in + * some of its parents. This method returns the set of parents that + * will be changed in the move() method. The object itself should + * not be included. + */ + virtual std::vector<ObjectCalcer*> movableParents() const; + /** + * In order to support simultaneously moving objects that are in + * different locations, we need for each object a location that it + * is assumed to be at, at the moment the moving starts. This + * method returns such a point. + */ + virtual Coordinate moveReferencePoint() const; + /** + * This is the method that does the real moving work. It will be + * invoked to tell the object to move to the new location to. After + * this, the calc() method will be calced, so you only need to do + * the real changes in this method, updating the ObjectImp should be + * done in the calc() method, not here. + */ + virtual void move( const Coordinate& to, const KigDocument& doc ); + + /** + * If this ObjectCalcer represents a curve, return true if the given + * point is by construction on this curve. If this ObjectCalcer + * represents a point, return true if this point is by construction + * on the given curve. + */ + virtual bool isDefinedOnOrThrough( const ObjectCalcer* o ) const = 0; +}; + +/** + * This is an ObjectCalcer that uses one of the various ObjectType's + * to calculate its ObjectImp. It basically forwards all of its + * functionality to the ObjectType that it holds a pointer to. + */ +class ObjectTypeCalcer + : public ObjectCalcer +{ + std::vector<ObjectCalcer*> mparents; + const ObjectType* mtype; + ObjectImp* mimp; +public: + typedef myboost::intrusive_ptr<ObjectTypeCalcer> shared_ptr; + /** + * Construct a new ObjectTypeCalcer with a given type and parents. + */ +// ObjectTypeCalcer( const ObjectType* type, const std::vector<ObjectCalcer*>& parents ); + /** + * the sort boolean tells whether the sortArgs method should be invoked or not; + * if not present + */ + ObjectTypeCalcer( const ObjectType* type, const std::vector<ObjectCalcer*>& parents, bool sort=true ); + ~ObjectTypeCalcer(); + + const ObjectImp* imp() const; + std::vector<ObjectCalcer*> parents() const; + void calc( const KigDocument& doc ); + + /** + * Set the parents of this ObjectTypeCalcer to np. This object will + * release the reference it had to its old parents, and take a new + * one on the new parents. + */ + void setParents( const std::vector<ObjectCalcer*> np ); + void setType( const ObjectType* t ); + + const ObjectType* type() const; + + const ObjectImpType* impRequirement( + ObjectCalcer* o, const std::vector<ObjectCalcer*>& os ) const; + bool isDefinedOnOrThrough( const ObjectCalcer* o ) const; + bool canMove() const; + bool isFreelyTranslatable() const; + std::vector<ObjectCalcer*> movableParents() const; + Coordinate moveReferencePoint() const; + void move( const Coordinate& to, const KigDocument& doc ); +}; + +/** + * This is an ObjectCalcer that keeps an ObjectImp, and never + * calculates a new one. It is a trivial, but very useful + * ObjectCalcer. It is used often in Kig, for holding data to be used + * by other ObjectCalcer's. + */ +class ObjectConstCalcer + : public ObjectCalcer +{ + ObjectImp* mimp; +public: + typedef myboost::intrusive_ptr<ObjectConstCalcer> shared_ptr; + + /** + * Construct a new ObjectConstCalcer with the given imp as the + * stored ObjectImp. + * + * This class takes ownership of the imp you pass it, it should have + * been constructed using new, and this class is responsible for + * deleting it. + */ + ObjectConstCalcer( ObjectImp* imp ); + ~ObjectConstCalcer(); + + const ObjectImp* imp() const; + void calc( const KigDocument& doc ); + std::vector<ObjectCalcer*> parents() const; + + /** + * Set the ObjectImp of this ObjectConstCalcer to the given + * newimp. The old one will be deleted. + */ + void setImp( ObjectImp* newimp ); + /** + * Set the ObjectImp of this ObjectConstCalcer to the given + * newimp. The old one will not be deleted, but returned. + */ + ObjectImp* switchImp( ObjectImp* newimp ); + + const ObjectImpType* impRequirement( + ObjectCalcer* o, const std::vector<ObjectCalcer*>& os ) const; + bool isDefinedOnOrThrough( const ObjectCalcer* o ) const; +}; + +/** + * This is an ObjectCalcer that has a single parent, and gets a + * certain property from it in its calc() method. + * + * \see ObjectImp::property + */ +class ObjectPropertyCalcer + : public ObjectCalcer +{ + ObjectImp* mimp; + ObjectCalcer* mparent; + int mpropid; +public: + /** + * Construct a new ObjectPropertyCalcer, that will get the property + * from parent with number propid. + */ + ObjectPropertyCalcer( ObjectCalcer* parent, int propid ); + ~ObjectPropertyCalcer(); + + const ObjectImp* imp() const; + std::vector<ObjectCalcer*> parents() const; + void calc( const KigDocument& doc ); + + int propId() const; + ObjectCalcer* parent() const; + + const ObjectImpType* impRequirement( + ObjectCalcer* o, const std::vector<ObjectCalcer*>& os ) const; + bool isDefinedOnOrThrough( const ObjectCalcer* o ) const; +}; + +#endif diff --git a/kig/objects/object_drawer.cc b/kig/objects/object_drawer.cc new file mode 100644 index 00000000..0989d4f2 --- /dev/null +++ b/kig/objects/object_drawer.cc @@ -0,0 +1,204 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "object_drawer.h" + +#include "object_imp.h" +#include "../misc/kigpainter.h" + +#include <qpen.h> +#include <qnamespace.h> +#include <cassert> + +#include <kdebug.h> + +void ObjectDrawer::draw( const ObjectImp& imp, KigPainter& p, bool sel ) const +{ + bool nv = p.getNightVision( ); + if ( mshown || nv ) + { + p.setBrushStyle( Qt::NoBrush ); + p.setBrushColor( sel ? Qt::red : ( mshown?mcolor:Qt::gray ) ); + p.setPen( QPen ( sel ? Qt::red : ( mshown?mcolor:Qt::gray ), 1) ); + p.setWidth( mwidth ); + p.setStyle( mstyle ); + p.setPointStyle( mpointstyle ); + imp.draw( p ); + } +} + +bool ObjectDrawer::contains( const ObjectImp& imp, const Coordinate& pt, const KigWidget& w, bool nv ) const +{ + bool shownornv = mshown || nv; + return shownornv && imp.contains( pt, mwidth, w ); +} + +bool ObjectDrawer::shown( ) const +{ + return mshown; +} + +QColor ObjectDrawer::color() const +{ + return mcolor; +} + +ObjectDrawer* ObjectDrawer::getCopyShown( bool s ) const +{ + ObjectDrawer* ret = new ObjectDrawer; + ret->mcolor = mcolor; + ret->mshown = s; + ret->mwidth = mwidth; + ret->mstyle = mstyle; + ret->mpointstyle = mpointstyle; + return ret; +} + +ObjectDrawer* ObjectDrawer::getCopyColor( const QColor& c ) const +{ + ObjectDrawer* ret = new ObjectDrawer; + ret->mcolor = c; + ret->mshown = mshown; + ret->mwidth = mwidth; + ret->mstyle = mstyle; + ret->mpointstyle = mpointstyle; + return ret; +} + +ObjectDrawer* ObjectDrawer::getCopyWidth( int w ) const +{ + ObjectDrawer* ret = new ObjectDrawer; + ret->mcolor = mcolor; + ret->mshown = mshown; + ret->mwidth = w; + ret->mstyle = mstyle; + ret->mpointstyle = mpointstyle; + return ret; +} + +ObjectDrawer* ObjectDrawer::getCopyStyle( Qt::PenStyle s ) const +{ + ObjectDrawer* ret = new ObjectDrawer; + ret->mcolor = mcolor; + ret->mshown = mshown; + ret->mwidth = mwidth; + ret->mstyle = s; + ret->mpointstyle = mpointstyle; + return ret; +} + +ObjectDrawer* ObjectDrawer::getCopyPointStyle( int p ) const +{ + ObjectDrawer* ret = new ObjectDrawer; + ret->mcolor = mcolor; + ret->mshown = mshown; + ret->mwidth = mwidth; + ret->mstyle = mstyle; + ret->mpointstyle = p; + return ret; +} + +int ObjectDrawer::width() const +{ + return mwidth; +} + +Qt::PenStyle ObjectDrawer::style() const +{ + return mstyle; +} + +int ObjectDrawer::pointStyle() const +{ + return mpointstyle; +} + +ObjectDrawer::ObjectDrawer( const QColor& color, int width, bool shown, Qt::PenStyle style, int pointStyle ) + : mcolor( color ), mshown( shown ), mwidth( width ), mstyle( style ), mpointstyle( pointStyle ) +{ +} + +ObjectDrawer::ObjectDrawer() + : mcolor( Qt::blue ), mshown( true ), mwidth( -1 ), mstyle( Qt::SolidLine ), mpointstyle( 0 ) +{ +} + +bool ObjectDrawer::inRect( const ObjectImp& imp, const Rect& r, const KigWidget& w ) const +{ + return mshown && imp.inRect( r, mwidth, w ); +} + +int ObjectDrawer::pointStyleFromString( const QString& style ) +{ + if ( style == "Round" ) + return 0; + else if ( style == "RoundEmpty" ) + return 1; + else if ( style == "Rectangular" ) + return 2; + else if ( style == "RectangularEmpty" ) + return 3; + else if ( style == "Cross" ) + return 4; + return 0; +} + +QString ObjectDrawer::pointStyleToString() const +{ + if ( mpointstyle == 0 ) + return "Round"; + else if ( mpointstyle == 1 ) + return "RoundEmpty"; + else if ( mpointstyle == 2 ) + return "Rectangular"; + else if ( mpointstyle == 3 ) + return "RectangularEmpty"; + else if ( mpointstyle == 4 ) + return "Cross"; + assert( false ); + return QString::null; +} + +Qt::PenStyle ObjectDrawer::styleFromString( const QString& style ) +{ + if ( style == "SolidLine" ) + return Qt::SolidLine; + else if ( style == "DashLine" ) + return Qt::DashLine; + else if ( style == "DotLine" ) + return Qt::DotLine; + else if ( style == "DashDotLine" ) + return Qt::DashDotLine; + else if ( style == "DashDotDotLine" ) + return Qt::DashDotDotLine; + else return Qt::SolidLine; +} + +QString ObjectDrawer::styleToString() const +{ + if ( mstyle == Qt::SolidLine ) + return "SolidLine"; + else if ( mstyle == Qt::DashLine ) + return "DashLine"; + else if ( mstyle == Qt::DotLine ) + return "DotLine"; + else if ( mstyle == Qt::DashDotLine ) + return "DashDotLine"; + else if ( mstyle == Qt::DashDotDotLine ) + return "DashDotDotLine"; + return "SolidLine"; +} diff --git a/kig/objects/object_drawer.h b/kig/objects/object_drawer.h new file mode 100644 index 00000000..2781acdc --- /dev/null +++ b/kig/objects/object_drawer.h @@ -0,0 +1,146 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_OBJECT_DRAWER_H +#define KIG_OBJECTS_OBJECT_DRAWER_H + +#include <qcolor.h> +#include <qnamespace.h> + +class ObjectImp; +class KigPainter; +class Coordinate; +class KigWidget; +class Rect; + +/** + * A class holding some information about how a certain object is + * drawn on the window. + * + * An ObjectDrawer is used by an ObjectHolder to keep information + * about how to draw an ObjectImp on the window. It is really nothing + * more than a struct with some convenience methods. It does not have + * any virtual methods, or have any complex semantics. It keeps + * information like the thickness of an object, its color, and whether + * or not it is hidden. + * + * \note The default width of an object depends on its type. E.g. A + * point is by default drawn at width 5, a line at width 1. + * Therefore, there is a special width -1, which means "the default + * width for this object". + */ +class ObjectDrawer +{ + QColor mcolor; + bool mshown; + int mwidth; + Qt::PenStyle mstyle; + int mpointstyle; +public: + /** + * Construct a new ObjectDrawer with a default color ( Qt::blue ), + * width ( -1 ), shown state ( true ), PenStyle ( Qt::SolidLine ), + * and pointstyle ( 0 ) + */ + ObjectDrawer(); + ObjectDrawer( const QColor& color, int width = -1, bool shown = true, Qt::PenStyle = Qt::SolidLine, int pointStyle = 0 ); + /** + * Draw the object \p imp on kigpainter \p p . If \p selected is true, it is + * drawn in red, otherwise in its normal color. + */ + void draw( const ObjectImp& imp, KigPainter& p, bool selected ) const; + /** + * returns whether the object \p imp contains coordinate \p p . This is + * dependent on whether it is shown ( when it will never contain + * anything ), and on its width.. + */ + bool contains( const ObjectImp& imp, const Coordinate& pt, const KigWidget& w, bool nv = false ) const; + /** + * returns whether the object \p imp is in the rectangle \p r . This is + * dependent on whether it is shown and on its width.. + */ + bool inRect( const ObjectImp& imp, const Rect& r, const KigWidget& w ) const; + + /** + * returns whether the object this ObjectDrawer is responsible for + * will be drawn or not.. + */ + bool shown() const; + /** + * returns the color that the object will be drawn in + */ + QColor color() const; + /** + * return the width of the object + */ + int width() const; + /** + * return PenStyle for all objects except points + */ + Qt::PenStyle style() const; + /** + * return pointStyle for points + */ + int pointStyle() const; + /** + * return pointStyle trasnformed in a string + */ + QString pointStyleToString() const; + /** + * return style trasnformed in a string + */ + QString styleToString() const; + /** + * returns a new ObjectDrawer that is identical to this one.. except + * that the shown state is set to \p s .. + */ + ObjectDrawer* getCopyShown( bool s ) const; + /** + * returns a new ObjectDrawer that is identical to this one.. except + * that the color is set to \p c .. + */ + ObjectDrawer* getCopyColor( const QColor& c ) const; + /** + * returns a new ObjectDrawer that is identical to this one.. except + * that the width is set to \p w .. + */ + ObjectDrawer* getCopyWidth( int w ) const; + /** + * returns a new ObjectDrawer that is identical to this one.. except + * that the PenStyle state is set to \p s .. + */ + ObjectDrawer* getCopyStyle( Qt::PenStyle s ) const; + /** + * returns a new ObjectDrawer that is identical to this one.. except + * that the pointStyle state is set to \p p .. + */ + ObjectDrawer* getCopyPointStyle( int p ) const; + /** + * Note that this returns a valid point style in every case, even if + * the given \p style string is unknown. In that case it returns a + * default value. + */ + static int pointStyleFromString( const QString& style ); + /** + * Note that this returns a valid style in every case, even if the + * given \p style string is unknown. In that case it returns a default + * value. + */ + static Qt::PenStyle styleFromString( const QString& style ); +}; + +#endif diff --git a/kig/objects/object_factory.cc b/kig/objects/object_factory.cc new file mode 100644 index 00000000..a54e01f0 --- /dev/null +++ b/kig/objects/object_factory.cc @@ -0,0 +1,369 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "object_factory.h" + +#include "bogus_imp.h" +#include "curve_imp.h" +#include "intersection_types.h" +#include "line_imp.h" +#include "object_drawer.h" +#include "object_holder.h" +#include "other_type.h" +#include "point_imp.h" +#include "point_type.h" +#include "text_type.h" + +#include "../kig/kig_document.h" +#include "../kig/kig_view.h" +#include "../misc/calcpaths.h" +#include "../misc/coordinate.h" +#include "../misc/object_hierarchy.h" + +#include <algorithm> +#include <functional> + +ObjectHolder* ObjectFactory::fixedPoint( const Coordinate& c ) const +{ + ObjectHolder* o = new ObjectHolder( fixedPointCalcer( c ) ); + return o; +} + +ObjectTypeCalcer* ObjectFactory::fixedPointCalcer( const Coordinate& c ) const +{ + std::vector<ObjectCalcer*> args; + args.push_back( new ObjectConstCalcer( new DoubleImp( c.x ) ) ); + args.push_back( new ObjectConstCalcer( new DoubleImp( c.y ) ) ); + ObjectTypeCalcer* oc = new ObjectTypeCalcer( FixedPointType::instance(), args ); + return oc; +} + +ObjectTypeCalcer* ObjectFactory::cursorPointCalcer( const Coordinate& c ) const +{ + std::vector<ObjectCalcer*> args; + args.push_back( new ObjectConstCalcer( new DoubleImp( c.x ) ) ); + args.push_back( new ObjectConstCalcer( new DoubleImp( c.y ) ) ); + ObjectTypeCalcer* oc = new ObjectTypeCalcer( CursorPointType::instance(), args ); + return oc; +} + +const ObjectFactory* ObjectFactory::instance() +{ + static ObjectFactory f; + return &f; +} + +ObjectTypeCalcer* ObjectFactory::sensiblePointCalcer( + const Coordinate& c, const KigDocument& d, const KigWidget& w ) const +{ + std::vector<ObjectHolder*> os = d.whatAmIOn( c, w ); + if ( os.size() == 2 ) + { + // we can calc intersection point *olny* between two objects... + std::vector<ObjectCalcer*> args; + args.push_back( os[0]->calcer() ); + args.push_back( os[1]->calcer() ); + // the simpliest case: two lines... + if ( ( os[0]->imp()->inherits( AbstractLineImp::stype() ) ) && + ( os[1]->imp()->inherits( AbstractLineImp::stype() ) ) ) + return new ObjectTypeCalcer( LineLineIntersectionType::instance(), args ); + // other cases will follow... + } + for ( std::vector<ObjectHolder*>::iterator i = os.begin(); i != os.end(); ++i ) + if ( (*i)->imp()->inherits( CurveImp::stype() ) ) + return constrainedPointCalcer( (*i)->calcer(), c, d ); + return fixedPointCalcer( c ); +} + +ObjectHolder* ObjectFactory::sensiblePoint( + const Coordinate& c, const KigDocument& d, const KigWidget& w ) const +{ + return new ObjectHolder( sensiblePointCalcer( c, d, w ) ); +} + +ObjectTypeCalcer* ObjectFactory::relativePointCalcer( + ObjectCalcer* o, const Coordinate& loc ) const +{ + Coordinate reference = + static_cast<const ObjectImp*>( o->imp() )->attachPoint(); + assert( reference.valid() ); + + double x = 0.0; + double y = 0.0; + if ( loc.valid() ) + { + x = loc.x - reference.x; + y = loc.y - reference.y; + } + std::vector<ObjectCalcer*> parents; + parents.push_back( new ObjectConstCalcer( new DoubleImp( x ) ) ); + parents.push_back( new ObjectConstCalcer( new DoubleImp( y ) ) ); + parents.push_back( o ); + return new ObjectTypeCalcer( RelativePointType::instance(), parents ); +} + +ObjectTypeCalcer* ObjectFactory::constrainedPointCalcer( + ObjectCalcer* curve, double param ) const +{ + assert( curve->imp()->inherits( CurveImp::stype() ) ); + std::vector<ObjectCalcer*> parents; + parents.push_back( new ObjectConstCalcer( new DoubleImp( param ) ) ); + parents.push_back( curve ); + return new ObjectTypeCalcer( ConstrainedPointType::instance(), parents ); +} + +ObjectHolder* ObjectFactory::constrainedPoint( + ObjectCalcer* curve, double param ) const +{ + return new ObjectHolder( constrainedPointCalcer( curve, param ) ); +} + +ObjectTypeCalcer* ObjectFactory::constrainedPointCalcer( + ObjectCalcer* curve, const Coordinate& c, const KigDocument& d ) const +{ + assert( curve->imp()->inherits( CurveImp::stype() ) ); + double param = static_cast<const CurveImp*>( curve->imp() )->getParam( c, d ); + return constrainedPointCalcer( curve, param ); +} + +ObjectHolder* ObjectFactory::constrainedPoint( + ObjectCalcer* curve, const Coordinate& c, const KigDocument& d ) const +{ + return new ObjectHolder( constrainedPointCalcer( curve, c, d ) ); +} + +ObjectTypeCalcer* ObjectFactory::locusCalcer( + ObjectCalcer* a, ObjectCalcer* b ) const +{ + assert( dynamic_cast<const ObjectTypeCalcer*>( a ) ); + ObjectTypeCalcer* constrained = static_cast<ObjectTypeCalcer*>( a ); + assert( constrained->type()->inherits( ObjectType::ID_ConstrainedPointType ) ); + assert( constrained->parents().size() == 2 ); + ObjectCalcer* curve = a->parents().back(); + + const ObjectCalcer* moving = b; + + std::vector<ObjectCalcer*> hierparents; + hierparents.push_back( constrained ); + std::vector<ObjectCalcer*> sideOfTree = sideOfTreePath( hierparents, moving ); + std::copy( sideOfTree.begin(), sideOfTree.end(), std::back_inserter( hierparents ) ); + + ObjectHierarchy hier( hierparents, moving ); + + std::vector<ObjectCalcer*> realparents( 2 + sideOfTree.size(), 0 ); + realparents[0] = new ObjectConstCalcer( new HierarchyImp( hier ) ); + realparents[1] = curve; + copy( sideOfTree.begin(), sideOfTree.end(), realparents.begin() + 2 ); + + return new ObjectTypeCalcer( LocusType::instance(), realparents ); +} + +ObjectHolder* ObjectFactory::locus( ObjectCalcer* a, ObjectCalcer* b ) const +{ + return new ObjectHolder( locusCalcer( a, b ) ); +} + +ObjectHolder* ObjectFactory::label( + const QString& s, const Coordinate& loc, + bool needframe, const std::vector<ObjectCalcer*>& parents, + const KigDocument& doc ) const +{ + return new ObjectHolder( labelCalcer( s, loc, needframe, parents, doc ) ); +} + +ObjectTypeCalcer* ObjectFactory::labelCalcer( + const QString& s, const Coordinate& loc, + bool needframe, const std::vector<ObjectCalcer*>& parents, + const KigDocument& doc ) const +{ + return attachedLabelCalcer( s, 0, loc, needframe, parents, doc ); +} + +ObjectTypeCalcer* ObjectFactory::attachedLabelCalcer( + const QString& s, ObjectCalcer* p, + const Coordinate& loc, bool needframe, + const std::vector<ObjectCalcer*>& nparents, + const KigDocument& doc ) const +{ + std::vector<ObjectCalcer*> parents; + parents.reserve( nparents.size() + 3 ); + parents.push_back( new ObjectConstCalcer( new IntImp( needframe ? 1 : 0 ) ) ); + + parents.push_back( getAttachPoint( p, loc, doc ) ); + + parents.push_back( new ObjectConstCalcer( new StringImp( s ) ) ); + std::copy( nparents.begin(), nparents.end(), std::back_inserter( parents ) ); + ObjectTypeCalcer* ret = new ObjectTypeCalcer( TextType::instance(), parents ); + ret->calc( doc ); + return ret; +} + +ObjectCalcer* ObjectFactory::getAttachPoint( + ObjectCalcer* p, + const Coordinate& loc, + const KigDocument& doc ) const +{ +/* + * mp: (changes to add relative-attachment). Now an object is tested + * as follows: + * - if attachPoint() returns a valid coordinate, then we use the new method + * - if it is a point: 'old-style' treatment (we can change this shortly) + * - if it is a curve: 'old-style' treatment (we could use the new approach, + * which can be better/worse depending on personal taste, I think) + * + * the first condition that matches determines the behaviour. + * the new method works similarly to the curve case, but we generate a new + * RelativePointType instead of a ConstrainedPointType; this will in turn make use + * of the new attachPoint() method for objects. + * + * changed the preference order 2005/01/21 (now attachPoint has preference over points) + * + * NOTE: changes in the tests performed should be matched also in + * modes/popup.cc (addNameLabel) and in label.cc (TextLabelModeBase::mouseMoved) + */ + + if ( p && p->imp()->attachPoint().valid() ) + { + ObjectCalcer* o = relativePointCalcer( p, loc ); + o->calc( doc ); + return o; + } + else if ( p && p->imp()->inherits( PointImp::stype() ) ) + { + return p; + } + else if ( p && p->imp()->inherits( CurveImp::stype() ) ) + { + double param = 0.5; + if ( loc.valid() ) + param = static_cast<const CurveImp*>( p->imp() )->getParam( loc, doc ); + + ObjectCalcer* o = constrainedPointCalcer( p, param ); + o->calc( doc ); + return o; + } + else + { + if ( loc.valid() ) + return new ObjectConstCalcer( new PointImp( loc ) ); + else + return new ObjectConstCalcer( new PointImp( Coordinate( 0, 0 ) ) ); + } +} + +ObjectHolder* ObjectFactory::attachedLabel( + const QString& s, ObjectCalcer* locationparent, + const Coordinate& loc, bool needframe, + const std::vector<ObjectCalcer*>& parents, + const KigDocument& doc ) const +{ + return new ObjectHolder( attachedLabelCalcer( s, locationparent, loc, needframe, parents, doc ) ); +} + +ObjectPropertyCalcer* ObjectFactory::propertyObjectCalcer( + ObjectCalcer* o, const char* p ) const +{ + int wp = o->imp()->propertiesInternalNames().findIndex( p ); + if ( wp == -1 ) return 0; + return new ObjectPropertyCalcer( o, wp ); +} + +ObjectHolder* ObjectFactory::propertyObject( + ObjectCalcer* o, const char* p ) const +{ + return new ObjectHolder( propertyObjectCalcer( o, p ) ); +} + +void ObjectFactory::redefinePoint( + ObjectTypeCalcer* point, const Coordinate& c, + KigDocument& doc, const KigWidget& w ) const +{ + std::vector<ObjectHolder*> hos = doc.whatAmIOn( c, w ); + std::vector<ObjectCalcer*> os; + ObjectCalcer* (ObjectHolder::*calcmeth)() = &ObjectHolder::calcer; + std::transform( hos.begin(), hos.end(), std::back_inserter( os ), + std::mem_fun( calcmeth ) ); + ObjectCalcer* v = 0; + + // we don't want one of our children as a parent... + std::set<ObjectCalcer*> children = getAllChildren( point ); + for ( std::vector<ObjectCalcer*>::iterator i = os.begin(); + i != os.end(); ++i ) + if ( (*i)->imp()->inherits( CurveImp::stype() ) && + children.find( *i ) == children.end() ) + { + v = *i; + break; + }; + + if ( v ) + { + // we want a constrained point... + const CurveImp* curveimp = static_cast<const CurveImp*>( v->imp() ); + double newparam = curveimp->getParam( c, doc ); + + if ( point->type()->inherits( ObjectType::ID_ConstrainedPointType ) ) + { + // point already was constrained -> simply update the param + // DataObject and make sure point is on the right curve... + ObjectCalcer* dataobj = 0; + std::vector<ObjectCalcer*> parents = point->parents(); + assert( parents.size() == 2 ); + assert ( parents[0]->imp()->inherits( DoubleImp::stype() ) ); + dataobj = parents[0]; + + parents.clear(); + parents.push_back( dataobj ); + parents.push_back( v ); + point->setParents( parents ); + + assert( dynamic_cast<ObjectConstCalcer*>( dataobj ) ); + static_cast<ObjectConstCalcer*>( dataobj )->setImp( + new DoubleImp( newparam ) ); + } + else + { + // point used to be fixed -> add a new DataObject etc. + std::vector<ObjectCalcer*> args; + args.push_back( new ObjectConstCalcer( new DoubleImp( newparam ) ) ); + args.push_back( v ); + point->setType( ConstrainedPointType::instance() ); + point->setParents( args ); + } + } + else + { + // a fixed point... + if ( point->type()->inherits( ObjectType::ID_ConstrainedPointType ) ) + { + // point used to be constrained.. + std::vector<ObjectCalcer*> a; + a.push_back( new ObjectConstCalcer( new DoubleImp( c.x ) ) ); + a.push_back( new ObjectConstCalcer( new DoubleImp( c.y ) ) ); + + point->setType( FixedPointType::instance() ); + point->setParents( a ); + } + else + { + // point used to be fixed -> simply update the DataObject's + // we can use the point's move function for that.. + point->move( c, doc ); + }; + } +} + diff --git a/kig/objects/object_factory.h b/kig/objects/object_factory.h new file mode 100644 index 00000000..0ce6ce62 --- /dev/null +++ b/kig/objects/object_factory.h @@ -0,0 +1,145 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_OBJECT_FACTORY_H +#define KIG_OBJECTS_OBJECT_FACTORY_H + +#include "common.h" + +class ObjectFactory +{ +public: + + static const ObjectFactory* instance(); + + /** + * this returns a fixed point. Note that the returned object is + * not added to the document.. + */ + ObjectHolder* fixedPoint( const Coordinate& c ) const; + ObjectTypeCalcer* fixedPointCalcer( const Coordinate& c ) const; + + /** + * this returns a CursorPointType; this is used during special + * constructions (e.g. regular polygons) where the constructor + * wants to use the cursor position without actually generating + * an object depending on a new point there. + */ + ObjectTypeCalcer* cursorPointCalcer( const Coordinate& c ) const; + + /** + * this returns a relative point (to an object). Note that the returned object + * is not added to the document.. + */ + ObjectTypeCalcer* relativePointCalcer( ObjectCalcer* o, const Coordinate& loc ) const; + + /** + * this returns a constrained point. Note that the returned object + * is not added to the document.. + */ + ObjectHolder* constrainedPoint( ObjectCalcer* curve, double param ) const; + ObjectTypeCalcer* constrainedPointCalcer( ObjectCalcer* curve, double param ) const; + /** + * \overload, changes nothing to the semantics, only calcs the param + * value for you.. + */ + ObjectTypeCalcer* constrainedPointCalcer( + ObjectCalcer* curve, const Coordinate& c, const KigDocument& ) const; + ObjectHolder* constrainedPoint( + ObjectCalcer* curve, const Coordinate& c, const KigDocument& ) const; + + /** + * this returns a "sensible point". + * By a "sensible point", I mean a point that would be about what + * the user expects when he asks for a point at point \p c . This is a + * constrained point if \p c is on a curve, and otherwise a fixed + * point. I might add the possibility for an intersection point + * sometime.. Note that the returned object is not added to + * the document.. + */ + ObjectTypeCalcer* sensiblePointCalcer( + const Coordinate& c, const KigDocument& d, const KigWidget& w ) const; + ObjectHolder* sensiblePoint( + const Coordinate& c, const KigDocument& d, const KigWidget& w ) const; + + /** + * set point to what sensiblePoint would have returned.. + */ + void redefinePoint( ObjectTypeCalcer* point, const Coordinate& c, + KigDocument& d, const KigWidget& w ) const; + + /** + * return a locus, defined by the two points ( one constrained, and + * one following ) \p a and \p b . \p a should be the constrained point, + * and thus, it has to be of type ObjectTypeCalcer where a->type() is of + * type ConstrainedPointType. The semantics of LocusType are a bit + * weird ( but I believe correct :) ), so this function takes care + * of the complication there.. + */ + ObjectTypeCalcer* locusCalcer( ObjectCalcer* a, ObjectCalcer* b ) const; + ObjectHolder* locus( ObjectCalcer* a, ObjectCalcer* b ) const; + + /** + * returns a label with text \p s at point \p c .. It ( and its parents ) + * is calced already... + */ + ObjectHolder* label( + const QString& s, const Coordinate& loc, + bool needframe, const std::vector<ObjectCalcer*>& parents, + const KigDocument& doc ) const; + ObjectTypeCalcer* labelCalcer( + const QString& s, const Coordinate& loc, + bool needframe, const std::vector<ObjectCalcer*>& parents, + const KigDocument& doc ) const; + + /** + * this one does the same as the above, only that the new label is + * attached to locationparent if it is non-null.. + */ + ObjectTypeCalcer* attachedLabelCalcer( + const QString& s, ObjectCalcer* locationparent, + const Coordinate& loc, bool needframe, + const std::vector<ObjectCalcer*>& parents, + const KigDocument& doc ) const; + /** + * this has been added because it comes handy when redefining + * a text label, we move here all the code for getting an + * attach point from the method above + */ + ObjectCalcer* getAttachPoint( + ObjectCalcer* locationparent, + const Coordinate& loc, + const KigDocument& doc ) const; + ObjectHolder* attachedLabel( + const QString& s, ObjectCalcer* locationparent, + const Coordinate& loc, bool needframe, + const std::vector<ObjectCalcer*>& parents, + const KigDocument& doc ) const; + + /** + * returns a property object for the property \p p of object \p o . + * + * \note + * \p o should have already been calc'd, or this will fail and + * return 0.. The returned object also needs to be calced after + * this.. + */ + ObjectPropertyCalcer* propertyObjectCalcer( ObjectCalcer* o, const char* p ) const; + ObjectHolder* propertyObject( ObjectCalcer* o, const char* p ) const; +}; + +#endif diff --git a/kig/objects/object_holder.cc b/kig/objects/object_holder.cc new file mode 100644 index 00000000..5a2c0765 --- /dev/null +++ b/kig/objects/object_holder.cc @@ -0,0 +1,164 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "object_holder.h" + +#include "bogus_imp.h" +#include "object_calcer.h" +#include "object_drawer.h" + +#include "../misc/coordinate.h" + +ObjectHolder::ObjectHolder( ObjectCalcer* calcer ) + : mcalcer( calcer ), mdrawer( new ObjectDrawer ), mnamecalcer( 0 ) +{ +} + +ObjectHolder::ObjectHolder( ObjectCalcer* calcer, ObjectDrawer* drawer, + ObjectConstCalcer* namecalcer ) + : mcalcer( calcer ), mdrawer( drawer ), mnamecalcer( namecalcer ) +{ + assert( !namecalcer || namecalcer->imp()->inherits( StringImp::stype() ) ); +} + +ObjectHolder::ObjectHolder( ObjectCalcer* calcer, ObjectDrawer* drawer ) + : mcalcer( calcer ), mdrawer( drawer ), mnamecalcer( 0 ) +{ +} + +ObjectHolder::~ObjectHolder() +{ + delete mdrawer; +} + +const ObjectImp* ObjectHolder::imp() const +{ + return mcalcer->imp(); +} + +const ObjectCalcer* ObjectHolder::calcer() const +{ + return mcalcer.get(); +} + +const ObjectDrawer* ObjectHolder::drawer() const +{ + return mdrawer; +} + +const ObjectConstCalcer* ObjectHolder::nameCalcer() const +{ + return mnamecalcer.get(); +} + +void ObjectHolder::setDrawer( ObjectDrawer* d ) +{ + delete switchDrawer( d ); +} + +void ObjectHolder::calc( const KigDocument& d ) +{ + mcalcer->calc( d ); +} + +void ObjectHolder::draw( KigPainter& p, bool selected ) const +{ + mdrawer->draw( *imp(), p, selected ); +} + +bool ObjectHolder::contains( const Coordinate& pt, const KigWidget& w, bool nv ) const +{ + return mdrawer->contains( *imp(), pt, w, nv ); +} + +bool ObjectHolder::inRect( const Rect& r, const KigWidget& w ) const +{ + return mdrawer->inRect( *imp(), r, w ); +} + +ObjectCalcer* ObjectHolder::calcer() +{ + return mcalcer.get(); +} + +ObjectDrawer* ObjectHolder::drawer() +{ + return mdrawer; +} + +ObjectConstCalcer* ObjectHolder::nameCalcer() +{ + return mnamecalcer.get(); +} + +const Coordinate ObjectHolder::moveReferencePoint() const +{ + return mcalcer->moveReferencePoint(); +} + +void ObjectHolder::move( const Coordinate& to, const KigDocument& doc ) +{ + mcalcer->move( to, doc ); +} + +bool ObjectHolder::canMove() const +{ + return mcalcer->canMove(); +} + +bool ObjectHolder::isFreelyTranslatable() const +{ + return mcalcer->isFreelyTranslatable(); +} + +ObjectDrawer* ObjectHolder::switchDrawer( ObjectDrawer* d ) +{ + ObjectDrawer* tmp = mdrawer; + mdrawer = d; + return tmp; +} + +bool ObjectHolder::shown( ) const +{ + return mdrawer->shown( ); +} + +const QString ObjectHolder::name() const +{ + if ( mnamecalcer ) + { + assert( mnamecalcer->imp()->inherits( StringImp::stype() ) ); + return static_cast<const StringImp*>( mnamecalcer->imp() )->data(); + } + else + return QString::null; +} + +void ObjectHolder::setNameCalcer( ObjectConstCalcer* namecalcer ) +{ + assert( !mnamecalcer ); + mnamecalcer = namecalcer; +} + +QString ObjectHolder::selectStatement() const +{ + const QString n = name(); + if ( n.isEmpty() ) + return i18n( imp()->type()->selectStatement() ); + else + return i18n( imp()->type()->selectNameStatement() ).arg( n ); +} diff --git a/kig/objects/object_holder.h b/kig/objects/object_holder.h new file mode 100644 index 00000000..9b30453d --- /dev/null +++ b/kig/objects/object_holder.h @@ -0,0 +1,145 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_OBJECT_HOLDER_H +#define KIG_OBJECTS_OBJECT_HOLDER_H + +#include "object_calcer.h" + +#include <qstring.h> + +/** + * An ObjectHolder represents an object as it is known to the + * document. It keeps a pointer to an ObjectCalcer, where it gets its + * data ( the ObjectImp that the ObjectCalcer holds ) from. It also + * holds information about how to draw this ObjectImp on the window, + * by keeping a pointer to an ObjectDrawer ( see below ). In its draw + * method, it gets the ObjectImp from the ObjectCalcer, and passes it + * to the ObjectDrawer, asking it to draw the ObjectImp on the window. + * + * The document ( check the KigDocument class ) holds a list of these + * ObjectHolder's. This is its only link with the ObjectCalcer + * dependency graph. + * + * An ObjectHolder keeps a reference to its * ObjectCalcer. + */ +class ObjectHolder +{ + ObjectCalcer::shared_ptr mcalcer; + ObjectDrawer* mdrawer; + ObjectConstCalcer::shared_ptr mnamecalcer; + +public: + /** + * Construct a new ObjectHolder with a given ObjectCalcer and + * ObjectDrawer, with a given name calcer. + */ + ObjectHolder( ObjectCalcer* calcer, ObjectDrawer* drawer, ObjectConstCalcer* namecalcer ); + /** + * Construct a new ObjectHolder with a given ObjectCalcer and + * ObjectDrawer. + */ + ObjectHolder( ObjectCalcer* calcer, ObjectDrawer* drawer ); + /** + * equivalent to the previous constructor, but with a default + * ObjectDrawer and no name. + */ + ObjectHolder( ObjectCalcer* calcer ); + ~ObjectHolder(); + + const ObjectImp* imp() const; + const ObjectCalcer* calcer() const; + ObjectCalcer* calcer(); + const ObjectDrawer* drawer() const; + ObjectDrawer* drawer(); + // the following two return zero if no name is set. + const ObjectConstCalcer* nameCalcer() const; + ObjectConstCalcer* nameCalcer(); + /** + * Setting the namecalcer is only allowed if previously none was + * set. This way, we avoid keeping a useless namecalcer around if + * no name is set. + */ + void setNameCalcer( ObjectConstCalcer* namecalcer ); + + /** + * returns QString::null if no name is set. + */ + const QString name() const; + /** + * Set the ObjectDrawer of this ObjectHolder to \p d , the old + * ObjectDrawer is deleted. + */ + void setDrawer( ObjectDrawer* d ); + /** + * Set the ObjectDrawer of this ObjectHolder to \p d , the old + * ObjectDrawer is not deleted, but returned. + */ + ObjectDrawer* switchDrawer( ObjectDrawer* d ); + + /** + * Make our ObjectCalcer recalculate its ObjectImp. + */ + void calc( const KigDocument& ); + /** + * Draw this object on the given KigPainter. If \p selected is true, + * then it will be drawn in red, instead of its normal color. + */ + void draw( KigPainter& p, bool selected ) const; + /** + * Returns whether this object contains the point \p p . + */ + bool contains( const Coordinate& p, const KigWidget& w, bool nv = false ) const; + /** + * Returns whether this object is in the rectangle \p r . + */ + bool inRect( const Rect& r, const KigWidget& w ) const; + /** + * Returns whether this object is shown. + */ + bool shown() const; + + /** + * This call is simply forwarded to the ObjectCalcer. Check the + * documentation of ObjectCalcer::moveReferencePoint() for more info. + */ + const Coordinate moveReferencePoint() const; + /** + * This call is simply forwarded to the ObjectCalcer. Check the + * documentation of ObjectCalcer::move() for more info. + */ + void move( const Coordinate& to, const KigDocument& ); + /** + * This call is simply forwarded to the ObjectCalcer. Check the + * documentation of ObjectCalcer::canMove() for more info. + */ + bool canMove() const; + /** + * This call is simply forwarded to the ObjectCalcer. Check the + * documentation of ObjectCalcer::isFreelyTranslatable() for more info. + */ + bool isFreelyTranslatable() const; + + /** + * Return a statement saying something like "select this segment" or + * "select segment ab" depending on whether this ObjectHolder has a + * name. + */ + QString selectStatement() const; +}; + +#endif diff --git a/kig/objects/object_imp.cc b/kig/objects/object_imp.cc new file mode 100644 index 00000000..6cb6650f --- /dev/null +++ b/kig/objects/object_imp.cc @@ -0,0 +1,308 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "object_imp.h" + +#include "bogus_imp.h" + +#include "../misc/coordinate.h" + +#include <klocale.h> +#include <map> + +class ObjectImpType::StaticPrivate +{ +public: + std::map<QCString, const ObjectImpType*> namemap; +}; + +ObjectImp::ObjectImp() +{ +} + +ObjectImp::~ObjectImp() +{ +} + +bool ObjectImp::valid() const +{ + return ! type()->inherits( InvalidImp::stype() ); +} + +void ObjectImp::fillInNextEscape( QString&, const KigDocument& ) const +{ + assert( false ); +} + +const QCStringList ObjectImp::properties() const +{ + QCStringList ret; + ret << I18N_NOOP( "Object Type" ); + return ret; +} + +const uint ObjectImp::numberOfProperties() const +{ + return 1; +} + +const QCStringList ObjectImp::propertiesInternalNames() const +{ + QCStringList ret; + ret << "base-object-type"; + return ret; +} + +ObjectImp* ObjectImp::property( uint i, const KigDocument& ) const +{ + if ( i == 0 ) return new StringImp( type()->translatedName() ); + return new InvalidImp; +} + +const ObjectImpType* ObjectImp::impRequirementForProperty( uint ) const +{ + return ObjectImp::stype(); +} + +void ObjectImpVisitor::visit( const ObjectImp* imp ) +{ + imp->visit( this ); +} + +void ObjectImpVisitor::visit( const IntImp* ) +{ +} + +void ObjectImpVisitor::visit( const DoubleImp* ) +{ +} + +void ObjectImpVisitor::visit( const StringImp* ) +{ +} + +void ObjectImpVisitor::visit( const InvalidImp* ) +{ +} + +void ObjectImpVisitor::visit( const HierarchyImp* ) +{ +} + +void ObjectImpVisitor::visit( const LineImp* ) +{ +} + +void ObjectImpVisitor::visit( const PointImp* ) +{ +} + +void ObjectImpVisitor::visit( const TextImp* ) +{ +} + +void ObjectImpVisitor::visit( const AngleImp* ) +{ +} + +void ObjectImpVisitor::visit( const VectorImp* ) +{ +} + +void ObjectImpVisitor::visit( const LocusImp* ) +{ +} + +void ObjectImpVisitor::visit( const CircleImp* ) +{ +} + +void ObjectImpVisitor::visit( const ConicImp* ) +{ +} + +void ObjectImpVisitor::visit( const CubicImp* ) +{ +} + +void ObjectImpVisitor::visit( const SegmentImp* ) +{ +} + +void ObjectImpVisitor::visit( const RayImp* ) +{ +} + +void ObjectImpVisitor::visit( const ArcImp* ) +{ +} + +void ObjectImpVisitor::visit( const PolygonImp* ) +{ +} + +ObjectImpVisitor::~ObjectImpVisitor() +{ + +} + +void ObjectImpVisitor::visit( const TransformationImp* ) +{ +} + +void ObjectImpVisitor::visit( const TestResultImp* ) +{ +} + +const char* ObjectImp::iconForProperty( uint ) const +{ + return "kig_text"; +} + +bool ObjectImp::canFillInNextEscape() const +{ + return false; +} + +ObjectImpType::ObjectImpType( const ObjectImpType* parent, + const char* internalname, + const char* translatedname, + const char* selectstatement, + const char* selectnamestatement, + const char* removeastatement, + const char* addastatement, + const char* moveastatement, + const char* attachtothisstatement, + const char* showastatement, + const char* hideastatement ) + : mparent( parent ), minternalname( internalname ), + mtranslatedname( translatedname ), mselectstatement( selectstatement ), + mselectnamestatement( selectnamestatement ), + mremoveastatement( removeastatement ), maddastatement( addastatement ), + mmoveastatement( moveastatement ), + mattachtothisstatement( attachtothisstatement ), + mshowastatement( showastatement ), + mhideastatement( hideastatement ) +{ + sd()->namemap[minternalname] = this; +} + +ObjectImpType::~ObjectImpType() +{ +} + +bool ObjectImpType::inherits( const ObjectImpType* t ) const +{ + return t == this || (mparent && mparent->inherits( t ) ); +} + +const char* ObjectImpType::internalName() const +{ + return minternalname; +} + +QString ObjectImpType::translatedName() const +{ + return i18n( mtranslatedname ); +} + +const char* ObjectImpType::selectStatement() const +{ + return mselectstatement; +} + +const char* ObjectImpType::selectNameStatement() const +{ + return mselectnamestatement; +} + +QString ObjectImpType::removeAStatement() const +{ + return i18n( mremoveastatement ); +} + +QString ObjectImpType::addAStatement() const +{ + return i18n( maddastatement ); +} + +QString ObjectImpType::moveAStatement() const +{ + return i18n( mmoveastatement ); +} + +const ObjectImpType* ObjectImpType::typeFromInternalName( const char* string ) +{ + QCString s( string ); + std::map<QCString, const ObjectImpType*>::iterator i = sd()->namemap.find( s ); + if ( i == sd()->namemap.end() ) + return 0; + else return i->second; +} + +bool ObjectImp::inherits( const ObjectImpType* t ) const +{ + return type()->inherits( t ); +} + +const ObjectImpType* ObjectImp::stype() +{ + static const ObjectImpType t( + 0, "any", + I18N_NOOP( "Object" ), + I18N_NOOP( "Select this object" ), + I18N_NOOP( "Select object %1" ), + I18N_NOOP( "Remove an object" ), + I18N_NOOP( "Add an object" ), + I18N_NOOP( "Move an object" ), + I18N_NOOP( "Attach to this object" ), + I18N_NOOP( "Show an object" ), + I18N_NOOP( "Hide an object" ) ); + return &t; +} + +ObjectImpType::StaticPrivate* ObjectImpType::sd() +{ + static StaticPrivate d; + return &d; +} + +bool ObjectImp::isCache() const +{ + return false; +} + +QString ObjectImpType::attachToThisStatement() const +{ + return i18n( mattachtothisstatement ); +} + +QString ObjectImpType::showAStatement() const +{ + return i18n( mshowastatement ); +} + +QString ObjectImpType::hideAStatement() const +{ + return i18n( mhideastatement ); +} + +bool ObjectImp::isPropertyDefinedOnOrThroughThisImp( uint ) const +{ + return false; +} + diff --git a/kig/objects/object_imp.h b/kig/objects/object_imp.h new file mode 100644 index 00000000..2c032f99 --- /dev/null +++ b/kig/objects/object_imp.h @@ -0,0 +1,360 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_OBJECT_IMP_H +#define KIG_OBJECTS_OBJECT_IMP_H + +#include "common.h" + +class IntImp; +class DoubleImp; +class StringImp; +class InvalidImp; +class HierarchyImp; +class TransformationImp; +class TestResultImp; +class CurveImp; +class LineImp; +class PointImp; +class TextImp; +class AngleImp; +class VectorImp; +class LocusImp; +class CircleImp; +class ConicImp; +class CubicImp; +class SegmentImp; +class RayImp; +class ArcImp; +class PolygonImp; + +/** + * \internal This is some OO magic commonly referred to as "double + * dispatch". If you need to do some action on an ObjectImp, and you + * need to do something different dependent on the type of o, then + * make a Visitor class that inherits this interface, and implements + * the appropriate functions properly, and call "o->visit( my_visitor + * );". + */ +class ObjectImpVisitor +{ +public: + virtual ~ObjectImpVisitor(); + void visit( const ObjectImp* imp ); + virtual void visit( const IntImp* imp ); + virtual void visit( const DoubleImp* imp ); + virtual void visit( const StringImp* imp ); + virtual void visit( const InvalidImp* imp ); + virtual void visit( const HierarchyImp* imp ); + virtual void visit( const TransformationImp* imp ); + virtual void visit( const TestResultImp* imp ); + virtual void visit( const LineImp* imp ); + virtual void visit( const PointImp* imp ); + virtual void visit( const TextImp* imp ); + virtual void visit( const AngleImp* imp ); + virtual void visit( const VectorImp* imp ); + virtual void visit( const LocusImp* imp ); + virtual void visit( const CircleImp* imp ); + virtual void visit( const ConicImp* imp ); + virtual void visit( const CubicImp* imp ); + virtual void visit( const SegmentImp* imp ); + virtual void visit( const RayImp* imp ); + virtual void visit( const ArcImp* imp ); + virtual void visit( const PolygonImp* imp ); +}; + +typedef unsigned int uint; + +/** + * Instances of this class represent a certain ObjectImp type. Every + * ObjectImp type has a static ObjectImpType member, that it returns a + * reference to in its type() function.. Think of it as a nice enum, + * that you can also get some data from.. + */ +class ObjectImpType +{ + const ObjectImpType* mparent; + const char* minternalname; + const char* mtranslatedname; + const char* mselectstatement; + const char* mselectnamestatement; + const char* mremoveastatement; + const char* maddastatement; + const char* mmoveastatement; + const char* mattachtothisstatement; + const char* mshowastatement; + const char* mhideastatement; + class StaticPrivate; + static StaticPrivate* sd(); +public: + /** + * Returns the type with name n. + * + * \internal Do *not* call this from functions that can be called at + * static initializer time ! It depends on information that is only + * available after that stage and will crash if used too early.. + */ + static const ObjectImpType* typeFromInternalName( const char* n ); + + /** + * \internal Construct an ObjectImpType, with a lot of data about + * your ObjectImp type. + * + * translatedname is a translatable string like "segment" + * selectstatement is a translatable string like "Select this segment" + * selectnamestatement is a translatable string like "Select segment %1" + * removeastatement is a translatable string like "Remove a Segment" + * addastatement is a translatable string like "Add a Segment" + * moveastatement is a translatable string like "Move a Segment" + * attachtothisstatement is a translatable string like "Attach to + * this segment" + * showastatement is a translatable string like "Show a Segment" + * hideastatement is a translatable string like "Hide a Segment" + * + * All translatable strings should have + * I18N_NOOP around them ! @param parent is the ObjectImpType of + * your parent ObjectImp type. Never give 0 as parent, except for + * the top ObjectImp ObjectImpType.. + */ + ObjectImpType( + const ObjectImpType* parent, const char* internalname, + const char* translatedname, + const char* selectstatement, + const char* selectnamestatement, + const char* removeastatement, + const char* addastatement, + const char* moveastatement, + const char* attachtothisstatement, + const char* showastatement, + const char* hideastatement ); + ~ObjectImpType(); + + /** + * Does the ObjectImp type represented by this instance inherit the + * ObjectImp type represented by t ? + */ + bool inherits( const ObjectImpType* t ) const; + + /** + * Returns an internal name for this ObjectImp type. This name is + * guaranteed unique, and mostly corresponds with the class name of + * the corresponding ObjectImp.. + */ + const char* internalName() const; + /** + * The name of this type, translated to the currently used language. + */ + QString translatedName() const; + /** + * Returns a translatable string of the form "Select this %1". + * E.g. "Select this segment". Note that users of this function + * should use i18n on the returned string before using it. + */ + const char* selectStatement() const; + + /** + * Returns a translatable string of the form "Select point %1". %1 + * will be filled in by whomever calls this function with the name + * of the object in question. This function should be used as + * follows: i18n( x->selectNameStatement() ).arg( xname ). + */ + const char* selectNameStatement() const; + + /** + * Returns a translated string of the form "Remove a xxx". + * E.g. "Remove a Segment". + */ + QString removeAStatement() const; + /** + * Returns a translated string of the form "Add a xxx". + * E.g. "Add a Segment". + */ + QString addAStatement() const; + /** + * Returns a translated string of the form "Move a xxx". + * E.g. "Move a Segment". + */ + QString moveAStatement() const; + /** + * Returns a translated string of the form "Attach to this xxx". + * E.g. "Attach to this segment". + * \internal This is used by the text label construction mode + */ + QString attachToThisStatement() const; + + /** + * Returns a translated string of the form "Show a xxx". + * E.g. "Show a Segment". + */ + QString showAStatement() const; + + /** + * Returns a translated string of the form "Hide a xxx". + * E.g. "Hide a Segment". + */ + QString hideAStatement() const; +}; + +/** + * The ObjectImp class represents the behaviour of an object after it + * is calculated. This means how to draw() it, whether it claims to + * contain a certain point etc. It is also the class where the + * ObjectType's get their information from.. + */ +class ObjectImp +{ +protected: + ObjectImp(); +public: + /** + * The ObjectImpType representing the base ObjectImp class. All + * other ObjectImp's inherit from this type. + */ + static const ObjectImpType* stype(); + + virtual ~ObjectImp(); + + /** + * Returns true if this ObjectImp inherits the ObjectImp type + * represented by t. + * E.g. you can check whether an ObjectImp is a LineImp by doing: + * \if creating-python-scripting-doc + * \code + * if object.inherits( LineImp.stype() ): + * \endcode + * \else + * \code + * if( object.inherits( LineImp::stype() ) + * \endcode + * \endif + */ + bool inherits( const ObjectImpType* t ) const; + + /** + * Returns a reference point where to attach labels; when this + * returns an invalidCoord then the attachment is either not + * done at all, or done in a specific way (like for curves, + * or for points) The treatment of points could also take + * advantage of this attachment mechanism. + * + * If this method returns a valid Coordinate, then this is + * interpreted as a pivot point for the label, which can still + * be moved relative to that point, but follows the object when + * the object changes. + * In practice a new RelativePointType is created (position of + * the string), this type in turn depends on the object (to get + * its attachPoint) and two DoubleImp that are interpreted as + * relative displacement (x and y) + */ + virtual Coordinate attachPoint( ) const = 0; + + /** + * Return this ObjectImp, transformed by the transformation t. + */ + virtual ObjectImp* transform( const Transformation& t ) const = 0; + + virtual void draw( KigPainter& p ) const = 0; + virtual bool contains( const Coordinate& p, int width, + const KigWidget& si ) const = 0; + virtual bool inRect( const Rect& r, int width, + const KigWidget& si ) const = 0; + virtual Rect surroundingRect() const = 0; + + /** + * Returns true if this is a valid ObjectImp. + * If you want to return an invalid ObjectImp, you should return an + * InvalidImp instance. + */ + bool valid() const; + + virtual const uint numberOfProperties() const; + // the names of the properties as perceived by the user.. put + // I18N_NOOP's around them here.. + virtual const QCStringList properties() const; + // the names of the properties as known only by kig internally. No + // need for I18N_NOOP. Preferably choose some lowercase name with + // only letters and dashes, no spaces.. + virtual const QCStringList propertiesInternalNames() const; + virtual ObjectImp* property( uint which, const KigDocument& d ) const; + // Sometimes we need to know which type an imp needs to be at least + // in order to have the imp with number which. Macro's need it + // foremost. This function answers that question.. + virtual const ObjectImpType* impRequirementForProperty( uint which ) const; + // Return whether the property with number which is by construction + // always a point on this curve ( if this is a curve ), or always a + // curve through this point ( if this is a curve ). + virtual bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + // What icon should be shown when talking about this property ? + virtual const char* iconForProperty( uint which ) const; + + /** + * Returns the lowermost ObjectImpType that this object is an + * instantiation of. + * E.g. if you want to get a string containing the internal name of + * the type of an object, you can do: + * \if creating-python-scripting-doc + * \code + * tn = object.type().internalName() + * \endcode + * \else + * \code + * std::string typename = object.type()->internalName(); + * \endcode + * \endif + */ + virtual const ObjectImpType* type() const = 0; + virtual void visit( ObjectImpVisitor* vtor ) const = 0; + + /** + * Returns a copy of this ObjectImp. + * The copy is an exact copy. Changes to the copy don't affect the + * original. + */ + virtual ObjectImp* copy() const = 0; + + // s is a string with at least one escape ( "%N" where N is a + // number ) somewhere. This function replaces the first escape it + // sees with the "value" of this imp ( using the QString::arg + // functions ). This is e.g. used by TextType to turn its variable + // args into strings.. + // if you implement this, then you should return true in + // canFillInEscape() ( standard implementation returns false ), and + // override fillInNextEscape() ( standard implementation does an + // assert( false ) ).. + virtual bool canFillInNextEscape() const; + virtual void fillInNextEscape( QString& s, const KigDocument& ) const; + + /** + * Returns true if this ObjectImp is equal to rhs. + * This function checks whether rhs is of the same ObjectImp type, + * and whether it contains the same data as this ObjectImp. + * \internal It is used e.g. by the KigCommand stuff to see what the + * user has changed during a move.. + */ + virtual bool equals( const ObjectImp& rhs ) const = 0; + + /** + * \internal Return true if this imp is just a cache imp. This + * means that it will never be considered to be stored in a file or + * in an ObjectHierarchy. This is useful for objects which cannot + * (easily and usefully) be (de)serialized, like e.g. + * PythonCompiledScriptImp.. For normal objects, the default + * implementation returns false, which is fine. + */ + virtual bool isCache() const; +}; +#endif diff --git a/kig/objects/object_imp_factory.cc b/kig/objects/object_imp_factory.cc new file mode 100644 index 00000000..bfeb1358 --- /dev/null +++ b/kig/objects/object_imp_factory.cc @@ -0,0 +1,510 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "object_imp_factory.h" + +#include "object_imp.h" +#include "bogus_imp.h" +#include "circle_imp.h" +#include "conic_imp.h" +#include "cubic_imp.h" +#include "line_imp.h" +#include "locus_imp.h" +#include "other_imp.h" +#include "point_imp.h" +#include "text_imp.h" + +#include "../misc/coordinate.h" + +#include <qdom.h> + +#include <klocale.h> + +const ObjectImpFactory* ObjectImpFactory::instance() +{ + static const ObjectImpFactory t; + return &t; +} + +ObjectImpFactory::ObjectImpFactory() +{ +} + +ObjectImpFactory::~ObjectImpFactory() +{ +} + +static void addXYElements( const Coordinate& c, QDomElement& parent, QDomDocument& doc ) +{ + QDomElement xe = doc.createElement( "x" ); + xe.appendChild( + doc.createTextNode( + QString::number( c.x ) ) ); + parent.appendChild( xe ); + QDomElement ye = doc.createElement( "y" ); + ye.appendChild( + doc.createTextNode( + QString::number( c.y ) ) ); + parent.appendChild( ye ); +} + +static void addDoubleElement( const char* name, double d, QDomElement& parent, QDomDocument& doc ) +{ + QDomElement e = doc.createElement( name ); + e.appendChild( doc.createTextNode( QString::number( d ) ) ); + parent.appendChild( e ); +} + +static void addCoordinateElement( const char* name, const Coordinate& d, QDomElement& p, QDomDocument& doc ) +{ + QDomElement e = doc.createElement( name ); + addXYElements( d, e, doc ); + p.appendChild( e ); +} + +QString ObjectImpFactory::serialize( const ObjectImp& d, QDomElement& parent, + QDomDocument& doc ) const +{ + if( d.inherits( IntImp::stype() ) ) + { + parent.appendChild( + doc.createTextNode( + QString::number( static_cast<const IntImp&>( d ).data() ) ) ); + return QString::fromLatin1( "int" ); + } + else if ( d.inherits( DoubleImp::stype() ) ) + { + parent.appendChild( + doc.createTextNode( + QString::number( static_cast<const DoubleImp&>( d ).data() ) ) ); + return QString::fromLatin1( "double" ); + } + else if( d.inherits( StringImp::stype() ) ) + { + parent.appendChild( + doc.createTextNode( + static_cast<const StringImp&>( d ).data() ) ); + return QString::fromLatin1( "string" ); + } + else if ( d.inherits( TestResultImp::stype() ) ) + { + parent.appendChild( + doc.createTextNode( + static_cast<const TestResultImp&>( d ).data() ) ); + return QString::fromLatin1( "testresult" ); + } + else if( d.inherits( HierarchyImp::stype() ) ) + { + static_cast<const HierarchyImp&>( d ).data().serialize( parent, doc ); + return QString::fromLatin1( "hierarchy" ); + } + else if ( d.inherits( TransformationImp::stype() ) ) + { + const Transformation& trans = static_cast<const TransformationImp&>( d ).data(); + + QDomElement matrixe = doc.createElement( "matrix" ); + for ( int i = 0; i < 3; ++i ) + { + for ( int j = 0; j < 3; ++j ) + { + QDomElement elel = doc.createElement( "element" ); + elel.setAttribute( "row", QString::number( i ) ); + elel.setAttribute( "column", QString::number( j ) ); + elel.appendChild( doc.createTextNode( QString::number( trans.data( i, j ) ) ) ); + matrixe.appendChild( elel ); + }; + } + parent.appendChild( matrixe ); + + QDomElement homothetye = doc.createElement( "homothetic" ); + const char* ishomothety = trans.isHomothetic() ? "true" : "false"; + homothetye.appendChild( doc.createTextNode( ishomothety ) ); + parent.appendChild( homothetye ); + + return QString::fromLatin1( "transformation" ); + } + else if( d.inherits( AbstractLineImp::stype() ) ) + { + LineData l = static_cast<const AbstractLineImp&>( d ).data(); + addCoordinateElement( "a", l.a, parent, doc ); + addCoordinateElement( "b", l.b, parent, doc ); + if( d.inherits( SegmentImp::stype() ) ) + return QString::fromLatin1( "segment" ); + else if( d.inherits( RayImp::stype() ) ) + return QString::fromLatin1( "ray" ); + else return QString::fromLatin1( "line" ); + } + else if( d.inherits( PointImp::stype() ) ) + { + addXYElements( static_cast<const PointImp&>( d ).coordinate(), + parent, doc ); + return QString::fromLatin1( "point" ); + } + else if( d.inherits( TextImp::stype() ) ) + { + QString text = static_cast<const TextImp&>( d ).text(); + parent.appendChild( + doc.createTextNode( text ) ); + return QString::fromLatin1( "text" ); + } + else if( d.inherits( AngleImp::stype() ) ) + { + addDoubleElement( "size", static_cast<const AngleImp&>( d ).size(), parent, doc ); + return QString::fromLatin1( "angle" ); + } + else if ( d.inherits( ArcImp::stype() ) ) + { + const ArcImp& a = static_cast<const ArcImp&>( d ); + addCoordinateElement( "center", a.center(), parent, doc ); + addDoubleElement( "radius", a.radius(), parent, doc ); + addDoubleElement( "startangle", a.startAngle(), parent, doc ); + addDoubleElement( "angle", a.angle(), parent, doc ); + return QString::fromLatin1( "arc" ); + } + else if( d.inherits( VectorImp::stype() ) ) + { + Coordinate dir = static_cast<const VectorImp&>( d ).dir(); + addXYElements( dir, parent, doc ); + return QString::fromLatin1( "vector" ); + } + else if( d.inherits( LocusImp::stype() ) ) + { + const LocusImp& locus = static_cast<const LocusImp&>( d ); + + // serialize the curve.. + QDomElement curve = doc.createElement( "curve" ); + const CurveImp& curveimp = *locus.curve(); + QString type = serialize( curveimp, curve, doc ); + curve.setAttribute( "type", type ); + parent.appendChild( curve ); + + // serialize the hierarchy.. + QDomElement hier = doc.createElement( "calculation" ); + locus.hierarchy().serialize( hier, doc ); + parent.appendChild( hier ); + + return QString::fromLatin1( "locus" ); + } + else if( d.inherits( CircleImp::stype() ) ) + { + const CircleImp& c = static_cast<const CircleImp&>( d ); + addCoordinateElement( "center", c.center(), parent, doc ); + addDoubleElement( "radius", c.radius(), parent, doc ); + return QString::fromLatin1( "circle" ); + } + else if( d.inherits( ConicImp::stype() ) ) + { + const ConicPolarData data = static_cast<const ConicImp&>( d ).polarData(); + addCoordinateElement( "focus1", data.focus1, parent, doc ); + addDoubleElement( "pdimen", data.pdimen, parent, doc ); + addDoubleElement( "ecostheta0", data.ecostheta0, parent, doc ); + addDoubleElement( "esintheta0", data.esintheta0, parent, doc ); + return QString::fromLatin1( "conic" ); + } + else if( d.inherits( CubicImp::stype() ) ) + { + const CubicCartesianData data = static_cast<const CubicImp&>( d ).data(); + QDomElement coeffs = doc.createElement( "coefficients" ); + addDoubleElement( "a000", data.coeffs[0], coeffs, doc ); + addDoubleElement( "a001", data.coeffs[1], coeffs, doc ); + addDoubleElement( "a002", data.coeffs[2], coeffs, doc ); + addDoubleElement( "a011", data.coeffs[3], coeffs, doc ); + addDoubleElement( "a012", data.coeffs[4], coeffs, doc ); + addDoubleElement( "a022", data.coeffs[5], coeffs, doc ); + addDoubleElement( "a111", data.coeffs[6], coeffs, doc ); + addDoubleElement( "a112", data.coeffs[7], coeffs, doc ); + addDoubleElement( "a122", data.coeffs[8], coeffs, doc ); + addDoubleElement( "a222", data.coeffs[9], coeffs, doc ); + parent.appendChild( coeffs ); + return QString::fromLatin1( "cubic" ); + } + assert( false ); + return QString::null; +} + +static Coordinate readXYElements( const QDomElement& e, bool& ok ) +{ + double x, y; + ok = true; + QDomElement xe = e.firstChild().toElement(); + if ( xe.isNull() || xe.tagName() != "x" ) + { + ok = false; + return Coordinate(); + } + else x = xe.text().toDouble( &ok ); + + QDomElement ye = xe.nextSibling().toElement(); + if ( ye.isNull() || ye.tagName() != "y" ) + { + ok = false; + return Coordinate(); + } + else y = ye.text().toDouble( &ok ); + + return Coordinate( x, y ); +} + +static Coordinate readCoordinateElement( QDomNode n, bool& ok, + const char* tagname ) +{ + QDomElement e = n.toElement(); + if ( e.isNull() || e.tagName() != tagname ) + { + ok = false; + Coordinate ret; + return ret; + } + return readXYElements( e, ok ); +} + +static double readDoubleElement( QDomNode n, bool& ok, + const char* tagname ) +{ + QDomElement e = n.toElement(); + if ( e.isNull() || e.tagName() != tagname ) + { + ok = false; + return 0.; + }; + return e.text().toDouble( &ok ); +} + +ObjectImp* ObjectImpFactory::deserialize( const QString& type, + const QDomElement& parent, + QString& error ) const +{ +#define KIG_GENERIC_PARSE_ERROR \ + { \ + error = i18n( "An error was encountered at line %1 in file %2." ) \ + .arg( __LINE__ ).arg( __FILE__ ); \ + return 0; \ + } + + bool ok = true; + if ( type == "int" ) + { + int ret = parent.text().toInt( &ok ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + return new IntImp( ret ); + } + else if ( type == "double" ) + { + double ret = parent.text().toDouble( &ok ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + return new DoubleImp( ret ); + } + else if ( type == "string" ) + { + return new StringImp( parent.text() ); + } + else if ( type == "testresult" ) + { + return new TestResultImp( parent.text() ); + } + else if ( type == "hierarchy" ) + { + ObjectHierarchy* hier = ObjectHierarchy::buildSafeObjectHierarchy( parent, error ); + if ( ! hier ) return 0; + HierarchyImp* imp = new HierarchyImp( *hier ); + delete hier; + return imp; + } + else if ( type == "transformation" ) + { + double data[3][3]; + bool homothetic = false; + for ( QDomElement childe = parent.firstChild().toElement(); + ! childe.isNull(); childe = childe.nextSibling().toElement() ) + { + if ( childe.tagName() == "matrix" ) + { + for ( QDomElement elel = childe.firstChild().toElement(); + ! elel.isNull(); elel = elel.nextSibling().toElement() ) + { + if ( elel.tagName() != "element" ) KIG_GENERIC_PARSE_ERROR; + bool ok = true; + int row = elel.attribute( "row" ).toInt( &ok ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + int column = elel.attribute( "column" ).toInt( &ok ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + data[row][column] = elel.text().toDouble( &ok ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + }; + } + else if ( childe.tagName() == "homothetic" ) + { + homothetic = childe.text() == "true"; + } + else continue; + }; + Transformation trans( data, homothetic ); + return new TransformationImp( trans ); + } + else if ( type == "point" ) + { + Coordinate ret = readXYElements( parent, ok ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + return new PointImp( ret ); + } + else if ( type == "line" || type == "segment" || type == "ray" ) + { + QDomNode n = parent.firstChild(); + Coordinate a = readCoordinateElement( n, ok, "a" ); + if ( !ok ) KIG_GENERIC_PARSE_ERROR; + n = n.nextSibling(); + Coordinate b = readCoordinateElement( n, ok, "b" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + if ( type == "line" ) return new LineImp( a, b ); + else if ( type == "segment" ) return new SegmentImp( a, b ); + else return new RayImp( a, b ); + } + else if( type == "angle" ) + { + double size = readDoubleElement( parent.firstChild(), ok, "size" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + return new AngleImp( Coordinate(), 0, size ); + } + else if ( type == "arc" ) + { + QDomNode n = parent.firstChild(); + Coordinate center = readCoordinateElement( n, ok, "center" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + n = n.nextSibling(); + double radius = readDoubleElement( n, ok, "radius" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + n = n.nextSibling(); + double startangle = readDoubleElement( n, ok, "startangle" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + n = n.nextSibling(); + double angle = readDoubleElement( n, ok, "angle" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + return new ArcImp( center, radius, startangle, angle ); + } + else if( type == "vector" ) + { + Coordinate dir = readXYElements( parent, ok ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + return new VectorImp( Coordinate(), dir ); + } + else if( type == "locus" ) + { + QDomElement curvee = parent.firstChild().toElement(); + if ( curvee.isNull() || curvee.tagName() != "curve" ) KIG_GENERIC_PARSE_ERROR; + QString type = curvee.attribute( "type" ); + ObjectImp* oi = deserialize( type, curvee, error ); + if ( ! oi || ! oi->inherits( CurveImp::stype() ) ) KIG_GENERIC_PARSE_ERROR; + //CurveImp* curvei = static_cast<CurveImp*>( oi ); + + QDomElement hiere = curvee.nextSibling().toElement(); + if ( hiere.isNull() || hiere.tagName() != "calculation" ) KIG_GENERIC_PARSE_ERROR; + assert( false ); // TODO +// return new LocusImp( curvei, hier ); + } + else if( type == "circle" ) + { + QDomNode n = parent.firstChild(); + Coordinate center = readCoordinateElement( n, ok, "center" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double radius = readDoubleElement( n, ok, "radius" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + return new CircleImp( center, radius ); + } + else if( type == "conic" ) + { + QDomNode n = parent.firstChild(); + Coordinate focus1 = readCoordinateElement( n, ok, "focus1" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double pdimen = readDoubleElement( n, ok, "pdimen" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double ecostheta0 = readDoubleElement( n, ok, "ecostheta0" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double esintheta0 = readDoubleElement( n, ok, "esintheta0" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + return new ConicImpPolar( + ConicPolarData( focus1, pdimen, ecostheta0, esintheta0 ) ); + } + else if( type == "cubic" ) + { + QDomElement coeffse = parent.firstChild().toElement(); + if ( coeffse.isNull() || coeffse.tagName() != "coefficients" ) + KIG_GENERIC_PARSE_ERROR; + + QDomNode n = coeffse.firstChild(); + double a000 = readDoubleElement( n, ok, "a000" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double a001 = readDoubleElement( n, ok, "a001" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double a002 = readDoubleElement( n, ok, "a002" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double a011 = readDoubleElement( n, ok, "a011" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double a012 = readDoubleElement( n, ok, "a012" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double a022 = readDoubleElement( n, ok, "a022" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double a111 = readDoubleElement( n, ok, "a111" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double a112 = readDoubleElement( n, ok, "a112" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double a122 = readDoubleElement( n, ok, "a112" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double a222 = readDoubleElement( n, ok, "a222" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + return new CubicImp( CubicCartesianData( a000, a001, a002, + a011, a012, a022, + a111, a112, a122, + a222 ) ); + } + + error = i18n( "This Kig file uses an object of type \"%1\", " + "which this Kig version does not support." + "Perhaps you have compiled Kig without support " + "for this object type," + "or perhaps you are using an older Kig version." ).arg( type ); + return 0; +} + diff --git a/kig/objects/object_imp_factory.h b/kig/objects/object_imp_factory.h new file mode 100644 index 00000000..1ab82bb4 --- /dev/null +++ b/kig/objects/object_imp_factory.h @@ -0,0 +1,40 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_OBJECT_IMP_FACTORY_H +#define KIG_OBJECTS_OBJECT_IMP_FACTORY_H + +#include "common.h" + +class ObjectImpFactory +{ + ObjectImpFactory(); + ~ObjectImpFactory(); +public: + static const ObjectImpFactory* instance(); + /** + * loads data from \p parent , and returns a new ObjectImp from the type + * string \p type . + */ + ObjectImp* deserialize( const QString& type, const QDomElement& parent, QString& error ) const; + /** + * adds data to \p parent , and returns a type string.. + */ + QString serialize( const ObjectImp& d, QDomElement& parent, QDomDocument& doc ) const; +}; + +#endif diff --git a/kig/objects/object_type.cc b/kig/objects/object_type.cc new file mode 100644 index 00000000..9ac2845b --- /dev/null +++ b/kig/objects/object_type.cc @@ -0,0 +1,125 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "object_type.h" + +#include "bogus_imp.h" +#include "object_type_factory.h" + +#include "../misc/coordinate.h" + +#include <qstringlist.h> + +#include <klocale.h> + +const char* ObjectType::fullName() const +{ + return mfulltypename; +} + +ObjectType::~ObjectType() +{ +} + +ObjectType::ObjectType( const char fulltypename[] ) + : mfulltypename( fulltypename ) +{ + ObjectTypeFactory::instance()->add( this ); +} + +bool ObjectType::canMove( const ObjectTypeCalcer& ) const +{ + return false; +} + +bool ObjectType::isFreelyTranslatable( const ObjectTypeCalcer& ) const +{ + return false; +} + +void ObjectType::move( ObjectTypeCalcer&, const Coordinate&, const KigDocument& ) const +{ + // we can't do an assert here, because sometimes ( like in + // ObjectABType::move, ObjectType::move is called without checking + // the object's canMove(). +// assert( false ); +} + +bool ObjectType::inherits( int ) const +{ + return false; +} + +ArgsParserObjectType::ArgsParserObjectType( const char fulltypename[], + const struct ArgsParser::spec argsspec[], + int n ) + : ObjectType( fulltypename ), margsparser( argsspec, n ) +{ +} + +const ObjectImpType* ArgsParserObjectType::impRequirement( const ObjectImp* o, const Args& parents ) const +{ + return margsparser.impRequirement( o, parents ); +} + +const ArgsParser& ArgsParserObjectType::argsParser() const +{ + return margsparser; +} + +bool ObjectType::isTransform() const +{ + return false; +} + +QStringList ObjectType::specialActions() const +{ + return QStringList(); +} + +void ObjectType::executeAction( int, ObjectHolder&, ObjectTypeCalcer&, KigPart&, KigWidget&, + NormalMode& ) const +{ + assert( false ); +} + +const Coordinate ObjectType::moveReferencePoint( const ObjectTypeCalcer& ) const +{ + assert( false ); + return Coordinate::invalidCoord(); +} + +std::vector<ObjectCalcer*> ArgsParserObjectType::sortArgs( const std::vector<ObjectCalcer*>& args ) const +{ + return margsparser.parse( args ); +} + +Args ArgsParserObjectType::sortArgs( const Args& args ) const +{ + return margsparser.parse( args ); +} + +std::vector<ObjectCalcer*> ObjectType::movableParents( const ObjectTypeCalcer& ) const +{ + return std::vector<ObjectCalcer*>(); +} + +bool ArgsParserObjectType::isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const +{ + return margsparser.isDefinedOnOrThrough( o, parents ); +} + diff --git a/kig/objects/object_type.h b/kig/objects/object_type.h new file mode 100644 index 00000000..f0ac49af --- /dev/null +++ b/kig/objects/object_type.h @@ -0,0 +1,130 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_OBJECT_TYPE_H +#define KIG_OBJECTS_OBJECT_TYPE_H + +#include "common.h" +#include "../misc/argsparser.h" + +class ObjectTypeCalcer; + +/** + * The ObjectType class is a thing that represents the "behaviour" for + * a certain type.. This basically means that it decides what + * \ref ObjectImp the object gets in the calc() function, how the + * object move()'s etc. + */ +class ObjectType +{ + const char* mfulltypename; +protected: + ObjectType( const char fulltypename[] ); +public: + virtual ~ObjectType(); + + enum { + ID_ConstrainedPointType, + ID_LocusType, + ID_FixedPointType + }; + + virtual bool inherits( int type ) const; + + virtual ObjectImp* calc( const Args& parents, const KigDocument& d ) const = 0; + + virtual bool canMove( const ObjectTypeCalcer& ourobj ) const; + virtual bool isFreelyTranslatable( const ObjectTypeCalcer& ourobj ) const; + virtual std::vector<ObjectCalcer*> movableParents( const ObjectTypeCalcer& ourobj ) const; + virtual const Coordinate moveReferencePoint( const ObjectTypeCalcer& ourobj ) const; + virtual void move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& d ) const; + + const char* fullName() const; + + /** + * Supposing that \p parents would be given as parents to + * this type's calc function, this function returns the ObjectImp id + * that \p o should at least have.. ( \p o should be part of \p parents ) + */ + virtual const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const = 0; + + /** + * Supposing that \p parents would be given as parents to this type's + * calc function, this function returns whether the returned + * ObjectImp will be, by construction, on \p o ( if \p o is a curve ), or + * through \p o ( if \p o is a point ). + */ + virtual bool isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const = 0; + + /** + * returns the ObjectImp id of the ObjectImp's produced by this + * ObjectType.. if the ObjectType can return different sorts of + * ObjectImp's, it should return the biggest common id, or + * ID_AnyImp.. + */ + virtual const ObjectImpType* resultId() const = 0; + + virtual std::vector<ObjectCalcer*> sortArgs( const std::vector<ObjectCalcer*>& args ) const = 0; + + virtual Args sortArgs( const Args& args ) const = 0; + + /** + * is this object type a transformation type. We want to know this + * cause transform types are shown separately in an object's RMB + * menu.. + */ + virtual bool isTransform() const; + + // ObjectType's can define some special actions, that are strictly + // specific to the type at hand. E.g. a text label allows to toggle + // the display of a frame around the text. Constrained and fixed + // points can be redefined etc. + + /** + * return i18n'd names for the special actions.. + */ + virtual QStringList specialActions() const; + /** + * execute the \p i 'th action from the specialActions above.. + */ + virtual void executeAction( int i, ObjectHolder& o, ObjectTypeCalcer& t, + KigPart& d, KigWidget& w, NormalMode& m ) const; +}; + +/** + * This is a convenience subclass of ObjectType that a type should + * inherit from if its parents can be specified in an ArgsParser.. + */ +class ArgsParserObjectType + : public ObjectType +{ +protected: + const ArgsParser margsparser; + ArgsParserObjectType( const char fulltypename[], + const struct ArgsParser::spec argsspec[], + int n ); +public: + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + bool isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const; + const ArgsParser& argsParser() const; + + std::vector<ObjectCalcer*> sortArgs( const std::vector<ObjectCalcer*>& args ) const; + Args sortArgs( const Args& args ) const; +}; + +#endif diff --git a/kig/objects/object_type_factory.cc b/kig/objects/object_type_factory.cc new file mode 100644 index 00000000..ee3024fd --- /dev/null +++ b/kig/objects/object_type_factory.cc @@ -0,0 +1,148 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "object_type_factory.h" + +#include <config.h> + +#include "object_type.h" +#include "circle_type.h" +#include "conic_types.h" +#include "cubic_type.h" +#include "intersection_types.h" +#include "line_type.h" +#include "text_type.h" +#include "other_type.h" +#include "transform_types.h" +#include "point_type.h" +#include "tests_type.h" + +#ifdef KIG_ENABLE_PYTHON_SCRIPTING +#include "../scripting/python_type.h" +#endif + +#include <qdom.h> +#include <string> + +ObjectTypeFactory::ObjectTypeFactory() + : malreadysetup( false ) +{ + setupBuiltinTypes(); +} + +ObjectTypeFactory::~ObjectTypeFactory() +{ +} + +ObjectTypeFactory* ObjectTypeFactory::instance() +{ + static ObjectTypeFactory fact; + return &fact; +} + +void ObjectTypeFactory::add( const ObjectType* type ) +{ + assert( mmap.find( std::string( type->fullName() ) ) == mmap.end() ); + mmap[std::string( type->fullName() )] = type; +} + +const ObjectType* ObjectTypeFactory::find( const char* name ) const +{ + maptype::const_iterator i = mmap.find( std::string( name ) ); + if ( i == mmap.end() ) return 0; + else return i->second; +} + +void ObjectTypeFactory::setupBuiltinTypes() +{ +// assert( ! malreadysetup ); +// malreadysetup = true; + +// // circle_type.h +// add( CircleBCPType::instance() ); +// add( CircleBPRType::instance() ); +// add( CircleBTPType::instance() ); + +// // conic_types.h +// add( ConicB5PType::instance() ); +// add( ConicBAAPType::instance() ); +// add( EllipseBFFPType::instance() ); +// add( HyperbolaBFFPType::instance() ); +// add( ConicBDFPType::instance() ); +// add( ParabolaBTPType::instance() ); +// add( EquilateralHyperbolaB4PType::instance() ); +// add( ConicPolarPointType::instance() ); +// add( ConicPolarLineType::instance() ); +// add( ConicDirectrixType::instance() ); +// add( ParabolaBDPType::instance() ); +// add( ConicAsymptoteType::instance() ); +// add( ConicRadicalType::instance() ); + +// // cubic_type.h +// add( CubicB9PType::instance() ); +// add( CubicNodeB6PType::instance() ); +// add( CubicCuspB4PType::instance() ); + +// // intersection_types.h +// add( ConicLineIntersectionType::instance() ); +// add( ConicLineOtherIntersectionType::instance() ); +// add( LineLineIntersectionType::instance() ); +// add( LineCubicIntersectionType::instance() ); +// add( CircleCircleIntersectionType::instance() ); + +// // line_type.h +// add( SegmentABType::instance() ); +// add( LineABType::instance() ); +// add( RayABType::instance() ); +// add( LinePerpendLPType::instance() ); +// add( LineParallelLPType::instance() ); + +// // other_type.h +// add( AngleType::instance() ); +// add( VectorType::instance() ); +// add( LocusType::instance() ); +// add( ArcBTPType::instance() ); +// add( CopyObjectType::instance() ); + +// // point_type.h +// add( FixedPointType::instance() ); +// add( ConstrainedPointType::instance() ); +// add( MidPointType::instance() ); +// add( MeasureTransportType::instance() ); + +// // text_type.h +// add( TextType::instance() ); + +// // tests_type.h +// add( AreParallelType::instance() ); + +// // transform_types.h +// add( TranslatedType::instance() ); +// add( PointReflectionType::instance() ); +// add( LineReflectionType::instance() ); +// add( RotationType::instance() ); +// add( ScalingOverCenterType::instance() ); +// add( ScalingOverLineType::instance() ); +// add( ProjectiveRotationType::instance() ); +// add( CastShadowType::instance() ); + +// #ifdef KIG_ENABLE_PYTHON_SCRIPTING +// // python types +// add( PythonCompileType::instance() ); +// add( PythonExecuteType::instance() ); +// #endif +} diff --git a/kig/objects/object_type_factory.h b/kig/objects/object_type_factory.h new file mode 100644 index 00000000..c1371d67 --- /dev/null +++ b/kig/objects/object_type_factory.h @@ -0,0 +1,40 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef OBJECT_TYPE_FACTORY_H +#define OBJECT_TYPE_FACTORY_H + +#include "common.h" + +#include <map> +#include <string> + +class ObjectTypeFactory +{ + typedef std::map<std::string, const ObjectType*> maptype; + maptype mmap; + bool malreadysetup; + ObjectTypeFactory(); + ~ObjectTypeFactory(); + void setupBuiltinTypes(); +public: + static ObjectTypeFactory* instance(); + void add( const ObjectType* type ); + const ObjectType* find( const char* name ) const; +}; + +#endif diff --git a/kig/objects/other_imp.cc b/kig/objects/other_imp.cc new file mode 100644 index 00000000..137a3e93 --- /dev/null +++ b/kig/objects/other_imp.cc @@ -0,0 +1,710 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "other_imp.h" + +#include "bogus_imp.h" +#include "point_imp.h" +#include "line_imp.h" + +#include "../misc/screeninfo.h" +#include "../misc/common.h" +#include "../misc/kigtransform.h" +#include "../misc/kigpainter.h" +#include "../misc/goniometry.h" +#include "../kig/kig_view.h" + +#include <klocale.h> + +#include <cmath> +#include <utility> +using namespace std; + +AngleImp::~AngleImp() +{ +} + +ObjectImp* AngleImp::transform( const Transformation& ) const +{ + // TODO ? + return new InvalidImp; +} + +void AngleImp::draw( KigPainter& p ) const +{ + p.drawAngle( mpoint, mstartangle, mangle ); +} + +AngleImp::AngleImp( const Coordinate& pt, double start_angle_in_radials, + double angle_in_radials ) + : mpoint( pt ), mstartangle( start_angle_in_radials ), + mangle( angle_in_radials ) +{ +} + +bool AngleImp::contains( const Coordinate& p, int width, const KigWidget& w ) const +{ + double radius = 50*w.screenInfo().pixelWidth(); + + if ( fabs( (p-mpoint).length() - radius ) > w.screenInfo().normalMiss( width ) ) + return false; + + // and next we check if the angle is appropriate... + Coordinate vect = p - mpoint; + double angle = atan2( vect.y, vect.x ); + while ( angle < mstartangle ) angle += 2*M_PI; + return angle <= mstartangle + mangle; +} + +bool AngleImp::inRect( const Rect& r, int width, const KigWidget& w ) const +{ + // TODO ? + return r.contains( mpoint, w.screenInfo().normalMiss( width ) ); +} + +Coordinate AngleImp::attachPoint() const +{ + return mpoint; +} + +const uint AngleImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 3; +} + +const QCStringList AngleImp::propertiesInternalNames() const +{ + QCStringList l = Parent::propertiesInternalNames(); + l << "angle-radian"; + l << "angle-degrees"; + l << "angle-bisector"; + assert( l.size() == AngleImp::numberOfProperties() ); + return l; +} + +const QCStringList AngleImp::properties() const +{ + QCStringList l = Parent::properties(); + l << I18N_NOOP( "Angle in Radians" ); + l << I18N_NOOP( "Angle in Degrees" ); + l << I18N_NOOP( "Angle Bisector" ); + assert( l.size() == AngleImp::numberOfProperties() ); + return l; +} + +const ObjectImpType* AngleImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else return AngleImp::stype(); +} + +const char* AngleImp::iconForProperty( uint which ) const +{ + int numprop = 0; + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + if ( which == Parent::numberOfProperties() + numprop++ ) + return "angle_size"; // size in radians + else if ( which == Parent::numberOfProperties() + numprop++ ) + return "angle_size"; // size in degrees + else if ( which == Parent::numberOfProperties() + numprop++ ) + return "angle_bisector"; // angle bisector.. + else assert( false ); + return ""; +} + +ObjectImp* AngleImp::property( uint which, const KigDocument& w ) const +{ + int numprop = 0; + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, w ); + if ( which == Parent::numberOfProperties() + numprop++ ) + return new DoubleImp( size() ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return new DoubleImp( Goniometry::convert( size(), Goniometry::Rad, Goniometry::Deg ) ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + { + const double angle = mstartangle + mangle / 2; + Coordinate p2 = mpoint + Coordinate( cos( angle ), sin( angle ) ) * 10; + return new RayImp( mpoint, p2 ); + } + else assert( false ); + return new InvalidImp; +} + +const double AngleImp::size() const +{ + return mangle; +} + +ObjectImp* AngleImp::copy() const +{ + return new AngleImp( mpoint, mstartangle, mangle ); +} + +VectorImp::VectorImp( const Coordinate& a, const Coordinate& b ) + : mdata( a, b ) +{ +} + +VectorImp::~VectorImp() +{ +} + +ObjectImp* VectorImp::transform( const Transformation& t ) const +{ + Coordinate ta = t.apply( mdata.a ); + Coordinate tb = t.apply( mdata.b ); + if ( ta.valid() && tb.valid() ) return new VectorImp( ta, tb ); + else return new InvalidImp; +} + +void VectorImp::draw( KigPainter& p ) const +{ + p.drawVector( mdata.a, mdata.b ); +} + +bool VectorImp::contains( const Coordinate& o, int width, const KigWidget& w ) const +{ + return internalContainsPoint( o, w.screenInfo().normalMiss( width ) ); +} + +bool VectorImp::inRect( const Rect& r, int width, const KigWidget& w ) const +{ + return lineInRect( r, mdata.a, mdata.b, width, this, w ); +} + +const uint VectorImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 5; +} + +const QCStringList VectorImp::propertiesInternalNames() const +{ + QCStringList ret = Parent::propertiesInternalNames(); + ret << "length"; + ret << "vect-mid-point"; + ret << "length-x"; + ret << "length-y"; + ret << "vector-opposite"; + assert( ret.size() == VectorImp::numberOfProperties() ); + return ret; +} + +const QCStringList VectorImp::properties() const +{ + QCStringList ret = Parent::properties(); + ret << I18N_NOOP( "Length" ); + ret << I18N_NOOP( "Midpoint" ); + ret << I18N_NOOP( "X length" ); + ret << I18N_NOOP( "Y length" ); + ret << I18N_NOOP( "Opposite Vector" ); + assert( ret.size() == VectorImp::numberOfProperties() ); + return ret; +} + +const ObjectImpType* VectorImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else return VectorImp::stype(); +} + +const char* VectorImp::iconForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + else if ( which == Parent::numberOfProperties() ) + return "distance"; // length + else if ( which == Parent::numberOfProperties() + 1 ) + return "bisection"; // mid point + else if ( which == Parent::numberOfProperties() + 2 ) + return "distance"; // length-x + else if ( which == Parent::numberOfProperties() + 3 ) + return "distance"; // length-y + else if ( which == Parent::numberOfProperties() + 4 ) + return "opposite-vector"; // opposite vector + else assert( false ); + return ""; +} + +ObjectImp* VectorImp::property( uint which, const KigDocument& w ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, w ); + else if ( which == Parent::numberOfProperties() ) + return new DoubleImp( length() ); + else if ( which == Parent::numberOfProperties() + 1 ) + return new PointImp( ( mdata.a + mdata.b ) / 2 ); + else if ( which == Parent::numberOfProperties() + 2 ) + return new DoubleImp( fabs( mdata.a.x - mdata.b.x ) ); + else if ( which == Parent::numberOfProperties() + 3 ) + return new DoubleImp( fabs( mdata.a.y - mdata.b.y ) ); + else if ( which == Parent::numberOfProperties() + 4 ) // opposite + return new VectorImp( mdata.a, 2*mdata.a-mdata.b ); + else assert( false ); + return new InvalidImp; +} + +VectorImp* VectorImp::copy() const +{ + return new VectorImp( mdata.a, mdata.b ); +} + +const Coordinate VectorImp::dir() const +{ + return mdata.dir(); +} + +void AngleImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +void VectorImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +const double VectorImp::length() const +{ + return ( mdata.a - mdata.b ).length(); +} + +ArcImp::ArcImp( const Coordinate& center, const double radius, + const double startangle, const double angle ) + : CurveImp(), mcenter( center ), mradius( radius ), + msa( startangle ), ma( angle ) +{ + if ( ma < 0 ) + { + // we want a positive angle.. + msa = msa + ma; + ma = -ma; + }; +} + +ArcImp::~ArcImp() +{ +} + +ArcImp* ArcImp::copy() const +{ + return new ArcImp( mcenter, mradius, msa, ma ); +} + +ObjectImp* ArcImp::transform( const Transformation& t ) const +{ + // + // we don't have conic arcs! So it is invalid to transform an arc + // with a nonhomothetic transformation + // + if ( ! t.isHomothetic() ) return new InvalidImp(); + + Coordinate nc = t.apply( mcenter ); + double nr = t.apply( mradius ); + // transform msa... + double nmsa = msa; + if ( t.getAffineDeterminant() > 0 ) + { + nmsa = msa - t.getRotationAngle(); + } else + { + Coordinate ar = t.apply2by2only( Coordinate( cos(msa), sin(msa) ) ); + nmsa = atan2( ar.y, ar.x ); + nmsa -= ma; + } + while ( nmsa < -M_PI ) nmsa += 2*M_PI; + while ( nmsa > M_PI ) nmsa -= 2*M_PI; + if ( nc.valid() ) return new ArcImp( nc, nr, nmsa, ma ); + else return new InvalidImp; +} + +void ArcImp::draw( KigPainter& p ) const +{ + p.drawArc( mcenter, mradius, msa, ma ); +} + +bool ArcImp::contains( const Coordinate& p, int width, const KigWidget& w ) const +{ + return internalContainsPoint( p, w.screenInfo().normalMiss( width ) ); +} + +bool ArcImp::inRect( const Rect&, int, const KigWidget& ) const +{ + // TODO + return false; +} + +bool ArcImp::valid() const +{ + return true; +} + +const uint ArcImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 9; +} + +const QCStringList ArcImp::properties() const +{ + QCStringList ret = Parent::properties(); + ret << I18N_NOOP( "Center" ); + ret << I18N_NOOP( "Radius" ); + ret << I18N_NOOP( "Angle" ); + ret << I18N_NOOP( "Angle in Degrees" ); + ret << I18N_NOOP( "Angle in Radians" ); + ret << I18N_NOOP( "Sector Surface" ); + ret << I18N_NOOP( "Arc Length" ); + ret << I18N_NOOP( "First End Point" ); + ret << I18N_NOOP( "Second End Point" ); + assert( ret.size() == ArcImp::numberOfProperties() ); + return ret; +} + +const QCStringList ArcImp::propertiesInternalNames() const +{ + QCStringList ret = Parent::propertiesInternalNames(); + ret << "center"; + ret << "radius"; + ret << "angle"; + ret << "angle-degrees"; + ret << "angle-radians"; + ret << "sector-surface"; + ret << "arc-length"; + ret << "end-point-A"; + ret << "end-point-B"; + return ret; +} + +const char* ArcImp::iconForProperty( uint which ) const +{ + int numprop = 0; + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return "arc_center"; // center + else if ( which == Parent::numberOfProperties() + numprop++ ) + return ""; + else if ( which == Parent::numberOfProperties() + numprop++ ) + return "angle"; + else if ( which == Parent::numberOfProperties() + numprop++ ) + return "angle_size"; + else if ( which == Parent::numberOfProperties() + numprop++ ) + return "angle_size"; + else if ( which == Parent::numberOfProperties() + numprop++ ) + return ""; + else if ( which == Parent::numberOfProperties() + numprop++ ) + return ""; + else if ( which == Parent::numberOfProperties() + numprop++ ) + return ""; + else if ( which == Parent::numberOfProperties() + numprop++ ) + return ""; + else assert( false ); + return ""; +} + +ObjectImp* ArcImp::property( uint which, const KigDocument& d ) const +{ + int numprop = 0; + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, d ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return new PointImp( mcenter ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return new DoubleImp( mradius ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return new AngleImp( mcenter, msa, ma ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return new IntImp( static_cast<int>( Goniometry::convert( ma, Goniometry::Rad, Goniometry::Deg ) ) ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return new DoubleImp( ma ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return new DoubleImp( sectorSurface() ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return new DoubleImp( mradius * ma ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return new PointImp( firstEndPoint() ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return new PointImp( secondEndPoint() ); + else assert( false ); + return new InvalidImp; +} + +const double ArcImp::sectorSurface() const +{ + return mradius * mradius * ma / 2; +} + +const ObjectImpType* ArcImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else + return ArcImp::stype(); +} + +void ArcImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +double ArcImp::getParam( const Coordinate& c, const KigDocument& ) const +{ + Coordinate d = (c - mcenter).normalize(); + double angle = atan2( d.y, d.x ); + angle -= msa; +// mp: problems with large arcs + while ( angle > ma/2 + M_PI ) angle -= 2*M_PI; + while ( angle < ma/2 - M_PI ) angle += 2*M_PI; +// + angle = max( 0., min( angle, ma ) ); + angle /= ma; + return angle; +} + +const Coordinate ArcImp::getPoint( double p, const KigDocument& ) const +{ + double angle = msa + p * ma; + Coordinate d = Coordinate( cos( angle ), sin( angle ) ) * mradius; + return mcenter + d; +} + +const Coordinate ArcImp::center() const +{ + return mcenter; +} + +double ArcImp::radius() const +{ + return mradius; +} + +double ArcImp::startAngle() const +{ + return msa; +} + +double ArcImp::angle() const +{ + return ma; +} + +Coordinate ArcImp::firstEndPoint() const +{ + double angle = msa; + return mcenter + Coordinate( cos( angle ), sin( angle ) ) * mradius; +} + +Coordinate ArcImp::secondEndPoint() const +{ + double angle = msa + ma; + return mcenter + Coordinate( cos( angle ), sin( angle ) ) * mradius; +} + +const Coordinate VectorImp::a() const +{ + return mdata.a; +} + +const Coordinate VectorImp::b() const +{ + return mdata.b; +} + +bool ArcImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( ArcImp::stype() ) && + static_cast<const ArcImp&>( rhs ).radius() == radius() && + static_cast<const ArcImp&>( rhs ).startAngle() == startAngle() && + static_cast<const ArcImp&>( rhs ).angle() == angle(); +} + +bool AngleImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( AngleImp::stype() ) && + static_cast<const AngleImp&>( rhs ).point() == point() && + static_cast<const AngleImp&>( rhs ).startAngle() == startAngle() && + static_cast<const AngleImp&>( rhs ).angle() == angle(); +} + +bool VectorImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( VectorImp::stype() ) && + static_cast<const VectorImp&>( rhs ).a() == a() && + static_cast<const VectorImp&>( rhs ).b() == b(); +} + +const ObjectImpType* AngleImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "angle", + I18N_NOOP( "angle" ), + I18N_NOOP( "Select this angle" ), + I18N_NOOP( "Select angle %1" ), + I18N_NOOP( "Remove an Angle" ), + I18N_NOOP( "Add an Angle" ), + I18N_NOOP( "Move an Angle" ), + I18N_NOOP( "Attach to this angle" ), + I18N_NOOP( "Show an Angle" ), + I18N_NOOP( "Hide an Angle" ) + ); + return &t; +} +const ObjectImpType* VectorImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "vector", + I18N_NOOP( "vector" ), + I18N_NOOP( "Select this vector" ), + I18N_NOOP( "Select vector %1" ), + I18N_NOOP( "Remove a Vector" ), + I18N_NOOP( "Add a Vector" ), + I18N_NOOP( "Move a Vector" ), + I18N_NOOP( "Attach to this vector" ), + I18N_NOOP( "Show a Vector" ), + I18N_NOOP( "Hide a Vector" ) + ); + return &t; +} +const ObjectImpType* ArcImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "arc", + I18N_NOOP( "arc" ), + I18N_NOOP( "Select this arc" ), + I18N_NOOP( "Select arc %1" ), + I18N_NOOP( "Remove an Arc" ), + I18N_NOOP( "Add an Arc" ), + I18N_NOOP( "Move an Arc" ), + I18N_NOOP( "Attach to this arc" ), + I18N_NOOP( "Show an Arc" ), + I18N_NOOP( "Hide an Arc" ) + ); + return &t; +} + +const ObjectImpType* AngleImp::type() const +{ + return AngleImp::stype(); +} + +const ObjectImpType* VectorImp::type() const +{ + return VectorImp::stype(); +} + +const ObjectImpType* ArcImp::type() const +{ + return ArcImp::stype(); +} + +bool ArcImp::containsPoint( const Coordinate& p, const KigDocument& ) const +{ + return internalContainsPoint( p, test_threshold ); +} + +bool ArcImp::internalContainsPoint( const Coordinate& p, double threshold ) const +{ + return isOnArc( p, mcenter, mradius, msa, ma, threshold ); +} + +bool AngleImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::isPropertyDefinedOnOrThroughThisImp( which ); + return false; +} + +bool VectorImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + return Parent::isPropertyDefinedOnOrThroughThisImp( which ); +} + +bool ArcImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::isPropertyDefinedOnOrThroughThisImp( which ); + else if ( which == Parent::numberOfProperties() ) + return true; + else + return false; +} + +Rect AngleImp::surroundingRect() const +{ + return Rect( mpoint, 0, 0 ); +} + +Rect VectorImp::surroundingRect() const +{ + return Rect( mdata.a, mdata.b ); +} + +Rect ArcImp::surroundingRect() const +{ + // the returned rect should contain the center point(?), the two end + // points, and all extreme x and y positions in between. + //Rect ret( mcenter, 0, 0 ); + double a = msa; + //ret.setContains( mcenter + mradius*Coordinate( cos( a ), sin( a ) ) ); + Rect ret ( mcenter + mradius*Coordinate( cos( a ), sin( a ) ), 0, 0 ); + a = msa + ma; + ret.setContains( mcenter + mradius*Coordinate( cos( a ), sin( a ) ) ); + for ( a = -2*M_PI; a <= 2*M_PI; a+=M_PI/2 ) + { + Coordinate d = mcenter + mradius*Coordinate( cos( a ), sin( a ) ); + if ( msa <= a && a <= msa + ma ) + ret.setContains( d ); + } + return ret; +} + +const Coordinate VectorImp::getPoint( double param, const KigDocument& ) const +{ + return mdata.a + mdata.dir() * param; +} + +double VectorImp::getParam( const Coordinate& p, const KigDocument& ) const +{ + Coordinate pt = calcPointOnPerpend( mdata, p ); + pt = calcIntersectionPoint( mdata, LineData( p, pt ) ); + // if pt is over the end of the vector we set it to one of the end + // points of the vector... + if ( ( pt - mdata.a ).length() > dir().length() ) + pt = mdata.b; + else if ( ( pt - mdata.b ).length() > dir().length() ) + pt = mdata.a; + if ( mdata.b == mdata.a ) return 0; + return ( ( pt - mdata.a ).length() ) / ( dir().length() ); +} + +bool VectorImp::containsPoint( const Coordinate& p, const KigDocument& ) const +{ + return internalContainsPoint( p, test_threshold ); +} + +bool VectorImp::internalContainsPoint( const Coordinate& p, double threshold ) const +{ + return isOnSegment( p, mdata.a, mdata.b, threshold ); +} + +LineData VectorImp::data() const +{ + return mdata; +} diff --git a/kig/objects/other_imp.h b/kig/objects/other_imp.h new file mode 100644 index 00000000..8e716fa6 --- /dev/null +++ b/kig/objects/other_imp.h @@ -0,0 +1,243 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_OTHER_IMP_H +#define KIG_OBJECTS_OTHER_IMP_H + +#include "curve_imp.h" +#include "../misc/common.h" +#include "../misc/coordinate.h" + +/** + * An ObjectImp representing an angle. + */ +class AngleImp + : public ObjectImp +{ + const Coordinate mpoint; + const double mstartangle; + const double mangle; +public: + typedef ObjectImp Parent; + /** + * Returns the ObjectImpType representing the AngleImp type.. + */ + static const ObjectImpType* stype(); + + /** + * Construct an Angle with a given center, start angle and + * dimension (both in radians). + */ + AngleImp( const Coordinate& pt, double start_angle_in_radials, + double angle_in_radials ); + ~AngleImp(); + + ObjectImp* transform( const Transformation& ) const; + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& ) const; + bool inRect( const Rect& r, int width, const KigWidget& ) const; + Rect surroundingRect() const; + + Coordinate attachPoint() const; + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& w ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + ObjectImp* copy() const; + + /** + * Return the size in radians of this angle. + */ + const double size() const; + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + /** + * Return the center of this angle. + */ + const Coordinate point() const { return mpoint; } + /** + * Return the start angle in radians of this angle. + */ + const double startAngle() const { return mstartangle; } + /** + * Return the dimension in radians of this angle. + */ + const double angle() const { return mangle; } + + bool equals( const ObjectImp& rhs ) const; +}; + +/** + * An ObjectImp representing a vector. + */ +class VectorImp + : public CurveImp +{ + LineData mdata; +public: + typedef CurveImp Parent; + /** + * Returns the ObjectImpType representing the VectorImp type.. + */ + static const ObjectImpType* stype(); + + /** + * Construct a Vector with a given start point and end point. + */ + VectorImp( const Coordinate& a, const Coordinate& b ); + ~VectorImp(); + + ObjectImp* transform( const Transformation& ) const; + + const Coordinate getPoint( double param, const KigDocument& ) const; + double getParam( const Coordinate&, const KigDocument& ) const; + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& ) const; + bool inRect( const Rect& r, int width, const KigWidget& ) const; + Rect surroundingRect() const; + + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& w ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + VectorImp* copy() const; + + /** + * Return the direction of this vector. + */ + const Coordinate dir() const; + /** + * Return the start point of this vector. + */ + const Coordinate a() const; + /** + * Return the end point of this vector. + */ + const Coordinate b() const; + /** + * Return the length of this vector. + */ + const double length() const; + /** + * Get the LineData for this vector. + */ + LineData data() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool equals( const ObjectImp& rhs ) const; + + bool containsPoint( const Coordinate& p, const KigDocument& doc ) const; + bool internalContainsPoint( const Coordinate& p, double threshold ) const; +}; + +/** + * An ObjectImp representing an arc. + */ +class ArcImp + : public CurveImp +{ + Coordinate mcenter; + double mradius; + double msa; + double ma; +public: + typedef CurveImp Parent; + /** + * Returns the ObjectImpType representing the ArcImp type.. + */ + static const ObjectImpType* stype(); + + /** + * Construct an Arc with a given center, radius, start angle and + * dimension (both in radians). + */ + ArcImp( const Coordinate& center, const double radius, + const double startangle, const double angle ); + ~ArcImp(); + ArcImp* copy() const; + + ObjectImp* transform( const Transformation& t ) const; + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& w ) const; + bool inRect( const Rect& r, int width, const KigWidget& si ) const; + Rect surroundingRect() const; + bool valid() const; + + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& d ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + double getParam( const Coordinate& c, const KigDocument& d ) const; + const Coordinate getPoint( double p, const KigDocument& d ) const; + + /** + * Return the center of this arc. + */ + const Coordinate center() const; + /** + * Return the radius of this arc. + */ + double radius() const; + /** + * Return the start angle in radians of this arc. + */ + double startAngle() const; + /** + * Return the dimension in radians of this arc. + */ + double angle() const; + /** + * Return the start point of this arc. + */ + Coordinate firstEndPoint() const; + /** + * Return the end point of this arc. + */ + Coordinate secondEndPoint() const; + /** + * Return the size of the sector surface of this arc. + */ + const double sectorSurface() const; + + bool equals( const ObjectImp& rhs ) const; + + bool containsPoint( const Coordinate& p, const KigDocument& doc ) const; + bool internalContainsPoint( const Coordinate& p, double threshold ) const; +}; + +#endif diff --git a/kig/objects/other_type.cc b/kig/objects/other_type.cc new file mode 100644 index 00000000..27787986 --- /dev/null +++ b/kig/objects/other_type.cc @@ -0,0 +1,189 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "other_type.h" + +#include "bogus_imp.h" +#include "point_imp.h" +#include "locus_imp.h" + +#include "../misc/common.h" +#include "../misc/calcpaths.h" +#include "../misc/goniometry.h" +#include "../kig/kig_part.h" +#include "../kig/kig_view.h" +#include "../kig/kig_commands.h" + +#include <functional> +#include <algorithm> +#include <cmath> + +using std::find; + +static const struct ArgsParser::spec argsspecLocus[] = +{ + { HierarchyImp::stype(), "hierarchy", "SHOULD NOT BE SEEN", false }, + { CurveImp::stype(), "curve", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( LocusType ) + +LocusType::LocusType() + : ArgsParserObjectType( "Locus", argsspecLocus, 2 ) +{ +} + +LocusType::~LocusType() +{ +} + +ObjectImp* LocusType::calc( const Args& args, const KigDocument& ) const +{ + using namespace std; + + assert( args.size() >= 2 ); + const Args firsttwo( args.begin(), args.begin() + 2 ); + Args fixedargs( args.begin() + 2, args.end() ); + + if ( ! margsparser.checkArgs( firsttwo ) ) return new InvalidImp; + for ( Args::iterator i = fixedargs.begin(); i != fixedargs.end(); ++i ) + if ( ! (*i)->valid() ) + return new InvalidImp; + + const ObjectHierarchy& hier = + static_cast<const HierarchyImp*>( args[0] )->data(); + const CurveImp* curveimp = static_cast<const CurveImp*>( args[1] ); + + return new LocusImp( curveimp->copy(), hier.withFixedArgs( fixedargs ) ); +} + +bool LocusType::inherits( int type ) const +{ + return type == ID_LocusType ? true : Parent::inherits( type ); +} + +const ObjectImpType* LocusType::resultId() const +{ + return LocusImp::stype(); +} + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CopyObjectType ) + +CopyObjectType::CopyObjectType() + : ObjectType( "Copy" ) +{ +} + +CopyObjectType::~CopyObjectType() +{ +} + +CopyObjectType* CopyObjectType::instance() +{ + static CopyObjectType t; + return &t; +} + +bool CopyObjectType::inherits( int ) const +{ + return false; +} + +ObjectImp* CopyObjectType::calc( const Args& parents, const KigDocument& ) const +{ + assert( parents.size() == 1 ); + return parents[0]->copy(); +} + +const ObjectImpType* CopyObjectType::impRequirement( const ObjectImp*, const Args& ) const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* CopyObjectType::resultId() const +{ + // we don't know what we return.. + return ObjectImp::stype(); +} + +const ObjectImpType* LocusType::impRequirement( const ObjectImp* o, const Args& parents ) const +{ + assert( parents.size() >= 2 ); + Args firsttwo( parents.begin(), parents.begin() + 2 ); + if ( o == parents[0] || o == parents[1] ) + return margsparser.impRequirement( o, firsttwo ); + else + { + const HierarchyImp* h = dynamic_cast<const HierarchyImp*>( parents[0] ); + if ( h ) + { + PointImp* p = new PointImp( Coordinate() ); + Args hargs( parents.begin()+ 2, parents.end() ); + hargs.push_back( p ); + ArgsParser hparser = h->data().argParser(); + const ObjectImpType* ret = hparser.impRequirement( o, hargs ); + delete p; + return ret; + } + else + return ObjectImp::stype(); + }; +} + +const LocusType* LocusType::instance() +{ + static const LocusType t; + return &t; +} + +std::vector<ObjectCalcer*> CopyObjectType::sortArgs( const std::vector<ObjectCalcer*>& os ) const +{ + assert( os.size() == 1 ); + return os; +} + +std::vector<ObjectCalcer*> LocusType::sortArgs( const std::vector<ObjectCalcer*>& args ) const +{ + assert( args.size() >= 2 ); + std::vector<ObjectCalcer*> firsttwo( args.begin(), args.begin() + 2 ); + firsttwo = margsparser.parse( firsttwo ); + std::copy( args.begin() + 2, args.end(), std::back_inserter( firsttwo ) ); + return firsttwo; +} + +Args LocusType::sortArgs( const Args& args ) const +{ + assert( args.size() >= 2 ); + Args firsttwo( args.begin(), args.begin() + 2 ); + firsttwo = margsparser.parse( firsttwo ); + std::copy( args.begin() + 2, args.end(), std::back_inserter( firsttwo ) ); + return firsttwo; +} + +Args CopyObjectType::sortArgs( const Args& args ) const +{ + assert( args.size() == 1 ); + return args; +} + +bool CopyObjectType::isDefinedOnOrThrough( const ObjectImp*, const Args& ) const +{ + // TODO: vragen aan parent ? + // TODO: translate the above TODO ? + return false; +} + diff --git a/kig/objects/other_type.h b/kig/objects/other_type.h new file mode 100644 index 00000000..6bbcead1 --- /dev/null +++ b/kig/objects/other_type.h @@ -0,0 +1,61 @@ +// Copyright (C) 2003-2004 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_MISC_OTHER_TYPE_H +#define KIG_MISC_OTHER_TYPE_H + +#include "base_type.h" +#include "../misc/object_hierarchy.h" + +class LocusType + : public ArgsParserObjectType +{ + typedef ArgsParserObjectType Parent; + LocusType(); + ~LocusType(); +public: + static const LocusType* instance(); + + ObjectImp* calc( const Args& args, const KigDocument& ) const; + + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + + bool inherits( int type ) const; + const ObjectImpType* resultId() const; + + std::vector<ObjectCalcer*> sortArgs( const std::vector<ObjectCalcer*>& args ) const; + Args sortArgs( const Args& args ) const; +}; + +class CopyObjectType + : public ObjectType +{ +protected: + CopyObjectType(); + ~CopyObjectType(); +public: + static CopyObjectType* instance(); + bool inherits( int type ) const; + ObjectImp* calc( const Args& parents, const KigDocument& d ) const; + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + bool isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const; + const ObjectImpType* resultId() const; + std::vector<ObjectCalcer*> sortArgs( const std::vector<ObjectCalcer*>& os ) const; + Args sortArgs( const Args& args ) const; +}; + +#endif diff --git a/kig/objects/point_imp.cc b/kig/objects/point_imp.cc new file mode 100644 index 00000000..02d4d360 --- /dev/null +++ b/kig/objects/point_imp.cc @@ -0,0 +1,223 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "point_imp.h" + +#include "bogus_imp.h" +#include "../misc/kigtransform.h" +#include "../misc/kigpainter.h" +#include "../misc/coordinate_system.h" +#include "../kig/kig_document.h" +#include "../kig/kig_view.h" + +#include <klocale.h> + +PointImp::PointImp( const Coordinate& c ) + : mc( c ) +{ +} + +Coordinate PointImp::attachPoint() const +{ + return mc; +// return Coordinate::invalidCoord(); +} + +void PointImp::draw( KigPainter& p ) const +{ + p.drawFatPoint( mc ); +} + +bool PointImp::contains( const Coordinate& p, int width, const KigWidget& w ) const +{ + int twidth = width == -1 ? 5 : width; + return (p - mc).length() - twidth*w.screenInfo().pixelWidth() < 0; +} + +bool PointImp::inRect( const Rect& r, int width, const KigWidget& w ) const +{ + double am = w.screenInfo().normalMiss( width ); + return r.contains( mc, am ); +} + +const uint PointImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 3; +} + +const QCStringList PointImp::propertiesInternalNames() const +{ + QCStringList l = Parent::propertiesInternalNames(); + l << "coordinate"; + l << "coordinate-x"; + l << "coordinate-y"; + assert( l.size() == PointImp::numberOfProperties() ); + return l; +} + +const QCStringList PointImp::properties() const +{ + QCStringList l = Parent::properties(); + l << I18N_NOOP( "Coordinate" ); + l << I18N_NOOP( "X coordinate" ); + l << I18N_NOOP( "Y coordinate" ); + assert( l.size() == PointImp::numberOfProperties() ); + return l; +} + +const ObjectImpType* PointImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else return PointImp::stype(); +} + +const char* PointImp::iconForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + if ( which == Parent::numberOfProperties() ) + return "pointxy"; // coordinate + if ( which == Parent::numberOfProperties() + 1 ) + return "pointxy"; // coordinate-x + if ( which == Parent::numberOfProperties() + 2 ) + return "pointxy"; // coordinate-y + else assert( false ); + return ""; +} + +ObjectImp* PointImp::property( uint which, const KigDocument& d ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, d ); + if ( which == Parent::numberOfProperties() ) + return new PointImp( mc ); + if ( which == Parent::numberOfProperties() + 1 ) + return new DoubleImp( mc.x ); + if ( which == Parent::numberOfProperties() + 2 ) + return new DoubleImp( mc.y ); +// else assert( false ); + return new InvalidImp; +} + +PointImp::~PointImp() +{ +} + +PointImp* PointImp::copy() const +{ + return new PointImp( mc ); +} + +ObjectImp* PointImp::transform( const Transformation& t ) const +{ + Coordinate nc = t.apply( mc ); + if ( nc.valid() ) return new PointImp( nc ); + else return new InvalidImp(); +} + +void PointImp::setCoordinate( const Coordinate& c ) +{ + mc = c; +} + +void PointImp::fillInNextEscape( QString& s, const KigDocument& doc ) const +{ + s = s.arg( doc.coordinateSystem().fromScreen( mc, doc ) ); +} + +void PointImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +bool PointImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( PointImp::stype() ) && + static_cast<const PointImp&>( rhs ).coordinate() == coordinate(); +} + +bool PointImp::canFillInNextEscape() const +{ + return true; +} + +const ObjectImpType* PointImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "point", + I18N_NOOP( "point" ), + I18N_NOOP( "Select this point" ), + I18N_NOOP( "Select point %1" ), + I18N_NOOP( "Remove a Point" ), + I18N_NOOP( "Add a Point" ), + I18N_NOOP( "Move a Point" ), + I18N_NOOP( "Attach to this point" ), + I18N_NOOP( "Show a Point" ), + I18N_NOOP( "Hide a Point" ) + ); + return &t; +} + +const ObjectImpType* PointImp::type() const +{ + return PointImp::stype(); +} + +bool PointImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + return Parent::isPropertyDefinedOnOrThroughThisImp( which ); +} + +Rect PointImp::surroundingRect() const +{ + return Rect( mc, 0., 0. ); +} + +/* + */ + +BogusPointImp::BogusPointImp( const Coordinate& c ) + : PointImp( c ) +{ +} + +BogusPointImp::~BogusPointImp() +{ +} + +const ObjectImpType* BogusPointImp::stype() +{ + static const ObjectImpType t( + 0, "boguspoint", + "SHOULDNOTBESEEN", + "SHOULDNOTBESEEN", + "SHOULDNOTBESEEN", + "SHOULDNOTBESEEN", + "SHOULDNOTBESEEN", + "SHOULDNOTBESEEN", + "SHOULDNOTBESEEN", + "SHOULDNOTBESEEN", + "SHOULDNOTBESEEN" + ); + return &t; +} + +const ObjectImpType* BogusPointImp::type() const +{ + return BogusPointImp::stype(); +} diff --git a/kig/objects/point_imp.h b/kig/objects/point_imp.h new file mode 100644 index 00000000..a5e8eb98 --- /dev/null +++ b/kig/objects/point_imp.h @@ -0,0 +1,91 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_POINT_IMP_H +#define KIG_OBJECTS_POINT_IMP_H + +#include "object_imp.h" +#include "../misc/coordinate.h" + +/** + * An ObjectImp representing a point.. + */ +class PointImp + : public ObjectImp +{ + Coordinate mc; +public: + typedef ObjectImp Parent; + /** + * Returns the ObjectImpType representing PointImp's. + */ + static const ObjectImpType* stype(); + + /** + * Construct a PointImp with coordinate c. + */ + PointImp( const Coordinate& c ); + ~PointImp(); + + Rect surroundingRect() const; + Coordinate attachPoint() const; + + /** + * Get the coordinate of this PointImp. + */ + const Coordinate& coordinate() const { return mc; } + /** + * Set the coordinate of this PointImp. + */ + void setCoordinate( const Coordinate& c ); + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& ) const; + bool inRect( const Rect& r, int width, const KigWidget& ) const; + + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& d ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + ObjectImp* transform( const Transformation& ) const; + + PointImp* copy() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + void fillInNextEscape( QString& s, const KigDocument& ) const; + bool canFillInNextEscape() const; + + bool equals( const ObjectImp& rhs ) const; +}; + +class BogusPointImp + : public PointImp +{ +public: + BogusPointImp( const Coordinate& c ); + ~BogusPointImp(); + static const ObjectImpType* stype(); + const ObjectImpType* type() const; +}; + +#endif diff --git a/kig/objects/point_type.cc b/kig/objects/point_type.cc new file mode 100644 index 00000000..04f8272c --- /dev/null +++ b/kig/objects/point_type.cc @@ -0,0 +1,665 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "point_type.h" + +#include "point_imp.h" +#include "curve_imp.h" +#include "line_imp.h" +#include "other_imp.h" +#include "bogus_imp.h" + +#include "../modes/moving.h" +#include "../misc/coordinate_system.h" +#include "../misc/common.h" +#include "../misc/calcpaths.h" +#include "../misc/kiginputdialog.h" +#include "../kig/kig_part.h" +#include "../kig/kig_document.h" +#include "../kig/kig_view.h" +#include "../kig/kig_commands.h" + +#include <klocale.h> + +static const ArgsParser::spec argsspecFixedPoint[] = +{ + { DoubleImp::stype(), "x", "SHOULD NOT BE SEEN", false }, + { DoubleImp::stype(), "y", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( FixedPointType ) + +FixedPointType::FixedPointType() + : ArgsParserObjectType( "FixedPoint", argsspecFixedPoint, 2 ) +{ +} + +FixedPointType::~FixedPointType() +{ +} + +ObjectImp* FixedPointType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + double a = static_cast<const DoubleImp*>( parents[0] )->data(); + double b = static_cast<const DoubleImp*>( parents[1] )->data(); + + return new PointImp( Coordinate( a, b ) ); +} + +static const ArgsParser::spec argsspecRelativePoint[] = +{ + { DoubleImp::stype(), "relative-x", "SHOULD NOT BE SEEN", false }, + { DoubleImp::stype(), "relative-y", "SHOULD NOT BE SEEN", false }, + { ObjectImp::stype(), "object", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( RelativePointType ) + +RelativePointType::RelativePointType() + : ArgsParserObjectType( "RelativePoint", argsspecRelativePoint, 3 ) +{ +} + +RelativePointType::~RelativePointType() +{ +} + +ObjectImp* RelativePointType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + if ( ! parents[2]->attachPoint().valid() ) return new InvalidImp; + + Coordinate reference = static_cast<const ObjectImp*>( parents[2] )->attachPoint(); + double a = static_cast<const DoubleImp*>( parents[0] )->data(); + double b = static_cast<const DoubleImp*>( parents[1] )->data(); + + return new PointImp( reference + Coordinate( a, b ) ); +} + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CursorPointType ) + +CursorPointType::CursorPointType() + : ObjectType( "CursorPoint" ) +{ +} + +CursorPointType::~CursorPointType() +{ +} + +const CursorPointType* CursorPointType::instance() +{ + static const CursorPointType t; + return &t; +} + +ObjectImp* CursorPointType::calc( const Args& parents, const KigDocument& ) const +{ + assert ( parents[0]->inherits( DoubleImp::stype() ) ); + assert ( parents[1]->inherits( DoubleImp::stype() ) ); + double a = static_cast<const DoubleImp*>( parents[0] )->data(); + double b = static_cast<const DoubleImp*>( parents[1] )->data(); + + return new BogusPointImp( Coordinate( a, b ) ); +} + +const ObjectImpType* CursorPointType::resultId() const +{ + return BogusPointImp::stype(); +} + +ObjectImp* ConstrainedPointType::calc( const Args& parents, const KigDocument& doc ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + double param = static_cast<const DoubleImp*>( parents[0] )->data(); + const Coordinate nc = static_cast<const CurveImp*>( parents[1] )->getPoint( param, doc ); + if ( nc.valid() ) return new PointImp( nc ); + else return new InvalidImp; +} + +const ArgsParser::spec argsspecConstrainedPoint[] = +{ + { DoubleImp::stype(), "parameter", "SHOULD NOT BE SEEN", false }, + { CurveImp::stype(), "Constrain the point to this curve", "SHOULD NOT BE SEEN", true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConstrainedPointType ) + +ConstrainedPointType::ConstrainedPointType() + : ArgsParserObjectType( "ConstrainedPoint", argsspecConstrainedPoint, 2 ) +{ +} + +ConstrainedPointType::~ConstrainedPointType() +{ +} + +void FixedPointType::move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& ) const +{ + // fetch the old coord..; + std::vector<ObjectCalcer*> pa = ourobj.parents(); + assert( margsparser.checkArgs( pa ) ); + assert( dynamic_cast<ObjectConstCalcer*>( pa.front() ) ); + assert( dynamic_cast<ObjectConstCalcer*>( pa.back() ) ); + + ObjectConstCalcer* ox = static_cast<ObjectConstCalcer*>( pa.front() ); + ObjectConstCalcer* oy = static_cast<ObjectConstCalcer*>( pa.back() ); + + ox->setImp( new DoubleImp( to.x ) ); + oy->setImp( new DoubleImp( to.y ) ); +} + +void RelativePointType::move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& ) const +{ + // fetch the attach point..; + // this routine is tightly paired with what moveReferencePoint returns! + // right now moveReferencePoint always returns the origin + std::vector<ObjectCalcer*> pa = ourobj.parents(); + assert( margsparser.checkArgs( pa ) ); + assert( dynamic_cast<ObjectConstCalcer*>( pa[0] ) ); + assert( dynamic_cast<ObjectConstCalcer*>( pa[1] ) ); + + ObjectConstCalcer* ox = static_cast<ObjectConstCalcer*>( pa[0] ); + ObjectConstCalcer* oy = static_cast<ObjectConstCalcer*>( pa[1] ); + ObjectCalcer* ob = static_cast<ObjectCalcer*>( pa[2] ); + + Coordinate attach = ob->imp()->attachPoint(); + ox->setImp( new DoubleImp( to.x - attach.x ) ); + oy->setImp( new DoubleImp( to.y - attach.y ) ); +} + +void CursorPointType::move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& ) const +{ + // fetch the old coord..; + + std::vector<ObjectCalcer*> pa = ourobj.parents(); + assert( pa.size() == 2 ); + assert( dynamic_cast<ObjectConstCalcer*>( pa.front() ) ); + assert( dynamic_cast<ObjectConstCalcer*>( pa.back() ) ); + + ObjectConstCalcer* ox = static_cast<ObjectConstCalcer*>( pa.front() ); + ObjectConstCalcer* oy = static_cast<ObjectConstCalcer*>( pa.back() ); + + ox->setImp( new DoubleImp( to.x ) ); + oy->setImp( new DoubleImp( to.y ) ); +} + +void ConstrainedPointType::move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& d ) const +{ + // fetch the CurveImp.. + std::vector<ObjectCalcer*> parents = ourobj.parents(); + assert( margsparser.checkArgs( parents ) ); + + assert( dynamic_cast<ObjectConstCalcer*>( parents[0] ) ); + ObjectConstCalcer* paramo = static_cast<ObjectConstCalcer*>( parents[0] ); + const CurveImp* ci = static_cast<const CurveImp*>( parents[1]->imp() ); + + // fetch the new param.. + const double np = ci->getParam( to, d ); + + paramo->setImp( new DoubleImp( np ) ); +} + +bool ConstrainedPointType::canMove( const ObjectTypeCalcer& ) const +{ + return true; +} + +bool ConstrainedPointType::isFreelyTranslatable( const ObjectTypeCalcer& ) const +{ + return false; +} + +bool FixedPointType::canMove( const ObjectTypeCalcer& ) const +{ + return true; +} + +bool FixedPointType::isFreelyTranslatable( const ObjectTypeCalcer& ) const +{ + return true; +} + +bool RelativePointType::canMove( const ObjectTypeCalcer& ) const +{ + return true; +} + +bool RelativePointType::isFreelyTranslatable( const ObjectTypeCalcer& ) const +{ + return true; +} + +bool CursorPointType::canMove( const ObjectTypeCalcer& ) const +{ + return true; +} + +static const ArgsParser::spec argsspecMidPoint[] = +{ + { PointImp::stype(), I18N_NOOP( "Construct the midpoint of this point and another point" ), + I18N_NOOP( "Select the first of the two points of which you want to construct the midpoint..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct the midpoint of this point and another point" ), + I18N_NOOP( "Select the other of the two points of which you want to construct the midpoint..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( MidPointType ) + +MidPointType::MidPointType() + : ObjectABType( "MidPoint", argsspecMidPoint, 2 ) +{ +} + +MidPointType::~MidPointType() +{ +} + +const MidPointType* MidPointType::instance() +{ + static const MidPointType t; + return &t; +} + +ObjectImp* MidPointType::calc( const Coordinate& a, const Coordinate& b ) const +{ + return new PointImp( ( a + b ) / 2 ); +} + +bool ConstrainedPointType::inherits( int type ) const +{ + return type == ID_ConstrainedPointType; +} + +const ConstrainedPointType* ConstrainedPointType::instance() +{ + static const ConstrainedPointType t; + return &t; +} + +bool FixedPointType::inherits( int type ) const +{ + return type == ID_FixedPointType; +} + +const FixedPointType* FixedPointType::instance() +{ + static const FixedPointType t; + return &t; +} + +const ObjectImpType* FixedPointType::resultId() const +{ + return PointImp::stype(); +} + +const RelativePointType* RelativePointType::instance() +{ + static const RelativePointType t; + return &t; +} + +const ObjectImpType* RelativePointType::resultId() const +{ + return PointImp::stype(); +} + +const ObjectImpType* ConstrainedPointType::resultId() const +{ + return PointImp::stype(); +} + +const ObjectImpType* CursorPointType::impRequirement( const ObjectImp* o, const Args& ) const +{ + if ( o->inherits( DoubleImp::stype() ) ) + return DoubleImp::stype(); + + if ( o->inherits( PointImp::stype() ) ) + return PointImp::stype(); + + return 0; +} + +bool CursorPointType::isDefinedOnOrThrough( const ObjectImp*, const Args& ) const +{ + return false; +} + +std::vector<ObjectCalcer*> CursorPointType::sortArgs( const std::vector<ObjectCalcer*>& args ) const +{ + return args; +} + +Args CursorPointType::sortArgs( const Args& args ) const +{ + return args; +} + +const ObjectImpType* MidPointType::resultId() const +{ + return PointImp::stype(); +} + +QStringList FixedPointType::specialActions() const +{ + QStringList ret; + ret << i18n( "Set &Coordinate..." ); + ret << i18n( "Redefine" ); + return ret; +} + +QStringList ConstrainedPointType::specialActions() const +{ + QStringList ret; + ret << i18n( "Set &Parameter..." ); + ret << i18n( "Redefine" ); + return ret; +} + +static void redefinePoint( ObjectHolder* o, KigPart& d, KigWidget& w ) +{ + PointRedefineMode pm( o, d, w ); + d.runMode( &pm ); +} + +void FixedPointType::executeAction( + int i, ObjectHolder& oh, ObjectTypeCalcer& o, + KigPart& d, KigWidget& w, NormalMode& ) const +{ + switch( i ) + { + case 0: + { + bool ok = true; + assert ( o.imp()->inherits( PointImp::stype() ) ); + Coordinate oldc = static_cast<const PointImp*>( o.imp() )->coordinate(); + KigInputDialog::getCoordinate( + i18n( "Set Coordinate" ), + i18n( "Enter the new coordinate." ) + QString::fromLatin1( "<br>" ) + + d.document().coordinateSystem().coordinateFormatNoticeMarkup(), + &w, &ok, d.document(), &oldc ); + if ( ! ok ) break; + + MonitorDataObjects mon( getAllParents( &o ) ); + o.move( oldc, d.document() ); + KigCommand* kc = new KigCommand( d, PointImp::stype()->moveAStatement() ); + mon.finish( kc ); + + d.history()->addCommand( kc ); + break; + }; + case 1: + redefinePoint( &oh, d, w ); + break; + default: + assert( false ); + }; +} + +void ConstrainedPointType::executeAction( + int i, ObjectHolder& oh, ObjectTypeCalcer& o, KigPart& d, KigWidget& w, + NormalMode& ) const +{ + switch( i ) + { + case 1: + redefinePoint( &oh, d, w ); + break; + case 0: + { + std::vector<ObjectCalcer*> parents = o.parents(); + assert( dynamic_cast<ObjectConstCalcer*>( parents[0] ) && + parents[0]->imp()->inherits( DoubleImp::stype() ) ); + + ObjectConstCalcer* po = static_cast<ObjectConstCalcer*>( parents[0] ); + double oldp = static_cast<const DoubleImp*>( po->imp() )->data(); + + bool ok = true; + double newp = getDoubleFromUser( + i18n( "Set Point Parameter" ), i18n( "Choose the new parameter: " ), + oldp, &w, &ok, 0, 1, 4 ); + if ( ! ok ) return; + + MonitorDataObjects mon( parents ); + po->setImp( new DoubleImp( newp ) ); + KigCommand* kc = new KigCommand( d, i18n( "Change Parameter of Constrained Point" ) ); + mon.finish( kc ); + d.history()->addCommand( kc ); + break; + }; + default: + assert( false ); + }; +} + +const Coordinate FixedPointType::moveReferencePoint( const ObjectTypeCalcer& ourobj ) const +{ + assert( ourobj.imp()->inherits( PointImp::stype() ) ); + return static_cast<const PointImp*>( ourobj.imp() )->coordinate(); +} + +const Coordinate RelativePointType::moveReferencePoint( const ObjectTypeCalcer& ourobj ) const +{ + assert( ourobj.imp()->inherits( PointImp::stype() ) ); +// return static_cast<const PointImp*>( ourobj.imp() )->coordinate(); + return Coordinate( 0., 0. ); +} + +const Coordinate ConstrainedPointType::moveReferencePoint( const ObjectTypeCalcer& ourobj ) const +{ + assert( ourobj.imp()->inherits( PointImp::stype() ) ); + return static_cast<const PointImp*>( ourobj.imp() )->coordinate(); +} + +std::vector<ObjectCalcer*> FixedPointType::movableParents( const ObjectTypeCalcer& ourobj ) const +{ + return ourobj.parents(); +} + +std::vector<ObjectCalcer*> RelativePointType::movableParents( const ObjectTypeCalcer& ourobj ) const +{ + std::vector<ObjectCalcer*> ret; + ret.push_back( ourobj.parents()[0] ); + ret.push_back( ourobj.parents()[1] ); + return ret; +} + +std::vector<ObjectCalcer*> ConstrainedPointType::movableParents( const ObjectTypeCalcer& ourobj ) const +{ + std::vector<ObjectCalcer*> ret; + ret.push_back( ourobj.parents()[0] ); + return ret; +} + +/* ----------------- Transport of measure ------------------------------ */ + +ObjectImp* MeasureTransportType::calc( const Args& parents, const KigDocument& doc ) const +{ + double measure; + + if ( parents.size() != 3 ) return new InvalidImp; + + if ( parents[0]->inherits (SegmentImp::stype()) ) + { + const SegmentImp* s = static_cast<const SegmentImp*>( parents[0] ); + measure = s->length(); + } else if ( parents[0]->inherits (ArcImp::stype()) ) + { + const ArcImp* s = static_cast<const ArcImp*>( parents[0] ); + measure = s->radius()*s->angle(); + } else return new InvalidImp; + + const Coordinate& p = static_cast<const PointImp*>( parents[2] )->coordinate(); + if ( parents[1]->inherits (LineImp::stype()) ) + { + const LineImp* c = static_cast<const LineImp*>( parents[1] ); + + if ( !c->containsPoint( p, doc ) ) + return new InvalidImp; + + const LineData line = c->data(); + const Coordinate dir = line.dir()/line.length(); + const Coordinate nc = p + measure*dir; + + if ( nc.valid() ) return new PointImp( nc ); + else return new InvalidImp; + } else if ( parents[1]->inherits (CircleImp::stype()) ) + { + const CircleImp* c = static_cast<const CircleImp*>( parents[1] ); + if ( !c->containsPoint( p, doc ) ) + return new InvalidImp; + + double param = c->getParam( p, doc ); + measure /= 2*c->radius()*M_PI; + param += measure; + while (param > 1) param -= 1; + + const Coordinate nc = c->getPoint( param, doc ); + if ( nc.valid() ) return new PointImp( nc ); + else return new InvalidImp; + } + + return new InvalidImp; +} + +// I18N_NOOP( "Select the segment/arc to transport on the circle/line..." ), false }, +// I18N_NOOP( "Select the circle/line on which to transport a measure..." ), true }, +// I18N_NOOP( "Select a point on the circle/line..." ), false } + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( MeasureTransportType ) + +MeasureTransportType::MeasureTransportType() + : ObjectType( "TransportOfMeasure" ) +{ +} + +MeasureTransportType::~MeasureTransportType() +{ +} + +const MeasureTransportType* MeasureTransportType::instance() +{ + static const MeasureTransportType t; + return &t; +} + +const ObjectImpType* MeasureTransportType::resultId() const +{ + return PointImp::stype(); +} + +const ObjectImpType* MeasureTransportType::impRequirement( const ObjectImp* obj, const Args& ) const +{ + if ( obj->inherits( PointImp::stype () ) ) + return PointImp::stype (); + + if ( obj->inherits( LineImp::stype () ) ) + return LineImp::stype (); + + if ( obj->inherits( CircleImp::stype () ) ) + return CircleImp::stype (); + + if ( obj->inherits( SegmentImp::stype () ) ) + return SegmentImp::stype (); + + if ( obj->inherits( ArcImp::stype () ) ) + return ArcImp::stype (); + + return 0; +} + +bool MeasureTransportType::isDefinedOnOrThrough( const ObjectImp* o, const Args& ) const +{ + if ( o->inherits( LineImp::stype() ) ) return true; + if ( o->inherits( CircleImp::stype() ) ) return true; + return false; +} + +std::vector<ObjectCalcer*> MeasureTransportType::sortArgs( const std::vector<ObjectCalcer*>& args ) const +{ + return args; /* should already be in correct order */ +} + +Args MeasureTransportType::sortArgs( const Args& args ) const +{ + return args; +} + +/* - transport of measure (old, for compatibility with prev. kig files) - */ + +ObjectImp* MeasureTransportTypeOld::calc( const Args& parents, const KigDocument& doc ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + const CircleImp* c = static_cast<const CircleImp*>( parents[0] ); + const Coordinate& p = static_cast<const PointImp*>( parents[1] )->coordinate(); + + if ( !c->containsPoint( p, doc ) ) + return new InvalidImp; + + const SegmentImp* s = static_cast<const SegmentImp*>( parents[2] ); + double param = c->getParam( p, doc ); + double measure = s->length(); + measure /= 2*c->radius()*M_PI; + param += measure; + while (param > 1) param -= 1; + + const Coordinate nc = c->getPoint( param, doc ); + if ( nc.valid() ) return new PointImp( nc ); + else return new InvalidImp; +} + +static const ArgsParser::spec argsspecMeasureTransportOld[] = +{ + { CircleImp::stype(), "Transport a measure on this circle", + I18N_NOOP( "Select the circle on which to transport a measure..." ), true }, + { PointImp::stype(), "Start transport from this point of the circle", + I18N_NOOP( "Select a point on the circle..." ), false }, + { SegmentImp::stype(), "Segment to transport", + I18N_NOOP( "Select the segment to transport on the circle..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( MeasureTransportTypeOld ) + +MeasureTransportTypeOld::MeasureTransportTypeOld() + : ArgsParserObjectType( "MeasureTransport", argsspecMeasureTransportOld, 3 ) +{ +} + +MeasureTransportTypeOld::~MeasureTransportTypeOld() +{ +} + +const MeasureTransportTypeOld* MeasureTransportTypeOld::instance() +{ + static const MeasureTransportTypeOld t; + return &t; +} + +const ObjectImpType* MeasureTransportTypeOld::resultId() const +{ + return PointImp::stype(); +} + +/* ----------------- end transport of measure ------------------------- */ + diff --git a/kig/objects/point_type.h b/kig/objects/point_type.h new file mode 100644 index 00000000..231ad6c5 --- /dev/null +++ b/kig/objects/point_type.h @@ -0,0 +1,159 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_POINT_TYPE_H +#define KIG_OBJECTS_POINT_TYPE_H + +#include "base_type.h" +#include "common.h" +#include "circle_imp.h" + +class FixedPointType + : public ArgsParserObjectType +{ + FixedPointType(); + ~FixedPointType(); + + static const ArgsParser::spec argsspec[1]; +public: + static const FixedPointType* instance(); + + bool inherits( int type ) const; + + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + bool canMove( const ObjectTypeCalcer& ourobj ) const; + bool isFreelyTranslatable( const ObjectTypeCalcer& ourobj ) const; + std::vector<ObjectCalcer*> movableParents( const ObjectTypeCalcer& ourobj ) const; + const Coordinate moveReferencePoint( const ObjectTypeCalcer& ourobj ) const; + void move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& ) const; + const ObjectImpType* resultId() const; + + QStringList specialActions() const; + void executeAction( int i, ObjectHolder& o, ObjectTypeCalcer& t, + KigPart& d, KigWidget& w, NormalMode& m ) const; +}; + +class RelativePointType + : public ArgsParserObjectType +{ + RelativePointType(); + ~RelativePointType(); + + static const ArgsParser::spec argsspec[1]; +public: + static const RelativePointType* instance(); + + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + bool canMove( const ObjectTypeCalcer& ourobj ) const; + bool isFreelyTranslatable( const ObjectTypeCalcer& ourobj ) const; + std::vector<ObjectCalcer*> movableParents( const ObjectTypeCalcer& ourobj ) const; + const Coordinate moveReferencePoint( const ObjectTypeCalcer& ourobj ) const; + void move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& ) const; + const ObjectImpType* resultId() const; + +// QStringList specialActions() const; +// void executeAction( int i, ObjectHolder& o, ObjectTypeCalcer& t, +// KigPart& d, KigWidget& w, NormalMode& m ) const; +}; + +class CursorPointType + : public ObjectType +{ + CursorPointType(); + ~CursorPointType(); + +public: + static const CursorPointType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + bool isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const; + std::vector<ObjectCalcer*> sortArgs( const std::vector<ObjectCalcer*>& args ) const; + Args sortArgs( const Args& args ) const; + bool canMove( const ObjectTypeCalcer& ourobj ) const; + void move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ConstrainedPointType + : public ArgsParserObjectType +{ + ConstrainedPointType(); + ~ConstrainedPointType(); +public: + static const ConstrainedPointType* instance(); + + bool inherits( int type ) const; + + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + + bool canMove( const ObjectTypeCalcer& ourobj ) const; + bool isFreelyTranslatable( const ObjectTypeCalcer& ourobj ) const; + std::vector<ObjectCalcer*> movableParents( const ObjectTypeCalcer& ourobj ) const; + const Coordinate moveReferencePoint( const ObjectTypeCalcer& ourobj ) const; + void move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& ) const; + const ObjectImpType* resultId() const; + + QStringList specialActions() const; + void executeAction( int i, ObjectHolder&, ObjectTypeCalcer& o, KigPart& d, KigWidget& w, + NormalMode& m ) const; +}; + +class MidPointType + : public ObjectABType +{ + MidPointType(); + ~MidPointType(); +public: + static const MidPointType* instance(); + ObjectImp* calc( const Coordinate& a, const Coordinate& b ) const; + const ObjectImpType* resultId() const; +}; + +class MeasureTransportType + : public ObjectType +{ + MeasureTransportType(); + ~MeasureTransportType(); +public: + static const MeasureTransportType* instance(); + + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + bool isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const; + std::vector<ObjectCalcer*> sortArgs( const std::vector<ObjectCalcer*>& args )const; + Args sortArgs( const Args& args ) const; +}; + +class MeasureTransportTypeOld + : public ArgsParserObjectType +{ + MeasureTransportTypeOld(); + ~MeasureTransportTypeOld(); +public: + static const MeasureTransportTypeOld* instance(); + + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/polygon_imp.cc b/kig/objects/polygon_imp.cc new file mode 100644 index 00000000..08215bfb --- /dev/null +++ b/kig/objects/polygon_imp.cc @@ -0,0 +1,550 @@ +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "polygon_imp.h" + +#include "bogus_imp.h" +#include "line_imp.h" +#include "point_imp.h" + +#include "../misc/common.h" +#include "../misc/coordinate.h" +#include "../misc/kigpainter.h" +#include "../misc/kigtransform.h" + +#include "../kig/kig_document.h" +#include "../kig/kig_view.h" + +#include <klocale.h> + +#include <cmath> + +PolygonImp::PolygonImp( uint npoints, const std::vector<Coordinate>& points, + const Coordinate& centerofmass ) + : mnpoints( npoints ), mpoints( points ), mcenterofmass( centerofmass ) +{ +// mpoints = points; +} + +PolygonImp::PolygonImp( const std::vector<Coordinate>& points ) +{ + uint npoints = points.size(); + Coordinate centerofmassn = Coordinate( 0, 0 ); + + for ( uint i = 0; i < npoints; ++i ) + { + centerofmassn += points[i]; + } + mpoints = points; + mcenterofmass = centerofmassn/npoints; + mnpoints = npoints; +} + +PolygonImp::~PolygonImp() +{ +} + +Coordinate PolygonImp::attachPoint() const +{ + return mcenterofmass; +} + +ObjectImp* PolygonImp::transform( const Transformation& t ) const +{ +/*mp: + * any projective transformation makes sense for a polygon, + * since segments transform into segments (but see below...) + * of course regular polygons will no longer be + * regular if t is not homothetic. + * for projective transformations the polygon could transform to + * an unbounded nonconnected polygon; this happens if some side + * of the polygon crosses the critical line that maps to infinity + * this can be easily checked using the getProjectiveIndicator + * function + */ +// if ( ! t.isHomothetic() ) +// return new InvalidImp(); + + if ( ! t.isAffine() ) /* in this case we need a more extensive test */ + { + double maxp = -1.0; + double minp = 1.0; + for ( uint i = 0; i < mpoints.size(); ++i ) + { + double p = t.getProjectiveIndicator( mpoints[i] ); + if ( p > maxp ) maxp = p; + if ( p < minp ) minp = p; + } + if ( maxp > 0 && minp < 0 ) return new InvalidImp; + } + std::vector<Coordinate> np; + for ( uint i = 0; i < mpoints.size(); ++i ) + { + Coordinate nc = t.apply( mpoints[i] ); + if ( !nc.valid() ) + return new InvalidImp; + np.push_back( nc ); + } + return new PolygonImp( np ); +} + +void PolygonImp::draw( KigPainter& p ) const +{ + p.drawPolygon( mpoints ); +} + +bool PolygonImp::isInPolygon( const Coordinate& p ) const +{ + // (algorithm sent to me by domi) + // We intersect with the horizontal ray from point to the right and + // count the number of intersections. That, along with some + // minor optimalisations of the intersection test.. + bool inside_flag = false; + double cx = p.x; + double cy = p.y; + + Coordinate prevpoint = mpoints.back(); + bool prevpointbelow = mpoints.back().y >= cy; + for ( uint i = 0; i < mpoints.size(); ++i ) + { + Coordinate point = mpoints[i]; + bool pointbelow = point.y >= cy; + if ( prevpointbelow != pointbelow ) + { + // possibility of intersection: points on different side from + // the X axis + //bool rightofpt = point.x >= cx; + // mp: we need to be a little bit more conservative here, in + // order to treat properly the case when the point is on the + // boundary + //if ( rightofpt == ( prevpoint.x >= cx ) ) + if ( ( point.x - cx )*(prevpoint.x - cx ) > 0 ) + { + // points on same side of Y axis -> easy to test intersection + // intersection iff one point to the right of c + if ( point.x >= cx ) + inside_flag = !inside_flag; + } + else + { + // points on different sides of Y axis -> we need to calculate + // the intersection. + // mp: we want to check if the point is on the boundary, and + // return false in such case + double num = ( point.y - cy )*( prevpoint.x - point.x ); + double den = prevpoint.y - point.y; + if ( num == den*( point.x - cx ) ) return false; + if ( num/den <= point.x - cx ) + inside_flag = !inside_flag; + } + } + prevpoint = point; + prevpointbelow = pointbelow; + } + return inside_flag; +} +#define selectpolygonwithinside 1 +#ifdef selectpolygonwithinside +bool PolygonImp::contains( const Coordinate& p, int, const KigWidget& ) const +{ + return isInPolygon( p ); +} +#else +bool PolygonImp::contains( const Coordinate& p, int width, const KigWidget& w ) const +{ + bool ret = false; + uint reduceddim = mpoints.size() - 1; + for ( uint i = 0; i < reduceddim; ++i ) + { + ret |= isOnSegment( p, mpoints[i], mpoints[i+1], w.screenInfo().normalMiss( width ) ); + } + ret |= isOnSegment( p, mpoints[reduceddim], mpoints[0], w.screenInfo().normalMiss( width ) ); + + return ret; +} +#endif + +bool PolygonImp::inRect( const Rect& r, int width, const KigWidget& w ) const +{ + bool ret = false; + uint reduceddim = mpoints.size() - 1; + for ( uint i = 0; i < reduceddim; ++i ) + { + SegmentImp* s = new SegmentImp( mpoints[i], mpoints[i+1] ); + ret |= lineInRect( r, mpoints[i], mpoints[i+1], width, s, w ); + delete s; + s = 0; + } + SegmentImp* t = new SegmentImp( mpoints[reduceddim], mpoints[0] ); + ret |= lineInRect( r, mpoints[reduceddim], mpoints[0], width, t, w ); + delete t; + t = 0; + + return ret; +} + +bool PolygonImp::valid() const +{ + return true; +} + +const uint PolygonImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 5; +} + +const QCStringList PolygonImp::propertiesInternalNames() const +{ + QCStringList l = Parent::propertiesInternalNames(); + l += "polygon-number-of-sides"; + l += "polygon-perimeter"; + l += "polygon-surface"; + l += "polygon-center-of-mass"; + l += "polygon-winding-number"; + assert( l.size() == PolygonImp::numberOfProperties() ); + return l; +} + +const QCStringList PolygonImp::properties() const +{ + QCStringList l = Parent::properties(); + l += I18N_NOOP( "Number of sides" ); + l += I18N_NOOP( "Perimeter" ); + l += I18N_NOOP( "Surface" ); + l += I18N_NOOP( "Center of Mass of the Vertices" ); + l += I18N_NOOP( "Winding Number" ); + assert( l.size() == PolygonImp::numberOfProperties() ); + return l; +} + +const ObjectImpType* PolygonImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else return PolygonImp::stype(); +} + +const char* PolygonImp::iconForProperty( uint which ) const +{ + assert( which < PolygonImp::numberOfProperties() ); + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + else if ( which == Parent::numberOfProperties() ) + return "en"; // number of sides + else if ( which == Parent::numberOfProperties() + 1 ) + return "circumference"; // perimeter + else if ( which == Parent::numberOfProperties() + 2 ) + return "areaCircle"; // surface + else if ( which == Parent::numberOfProperties() + 3 ) + return "point"; // center of mass + else if ( which == Parent::numberOfProperties() + 4 ) + return "w"; // winding number + else assert( false ); + return ""; +} + +ObjectImp* PolygonImp::property( uint which, const KigDocument& w ) const +{ + assert( which < PolygonImp::numberOfProperties() ); + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, w ); + else if ( which == Parent::numberOfProperties() ) + { + // number of points + return new IntImp( mnpoints ); + } + else if ( which == Parent::numberOfProperties() + 1) + { + double circumference = 0.; + // circumference + for ( uint i = 0; i < mpoints.size(); ++i ) + { + uint prev = ( i + mpoints.size() - 1 ) % mpoints.size(); + circumference += ( mpoints[i] - mpoints[prev] ).length(); + } + return new DoubleImp( circumference ); + } + else if ( which == Parent::numberOfProperties() + 2) + { + int wn = windingNumber (); // not able to compute area for such polygons... + if ( wn < 0 ) wn = -wn; + if ( wn != 1 ) return new InvalidImp; + double surface2 = 0.0; + Coordinate prevpoint = mpoints.back(); + for ( uint i = 0; i < mpoints.size(); ++i ) + { + Coordinate point = mpoints[i]; + surface2 += ( point.x - prevpoint.x ) * ( point.y + prevpoint.y ); + prevpoint = point; + } + return new DoubleImp( fabs( surface2 / 2 ) ); + } + else if ( which == Parent::numberOfProperties() + 3 ) + { + return new PointImp( mcenterofmass ); + } + else if ( which == Parent::numberOfProperties() + 4 ) + { + // winding number + return new IntImp( windingNumber() ); + } + else assert( false ); + return new InvalidImp; +} + +const std::vector<Coordinate> PolygonImp::points() const +{ + std::vector<Coordinate> np; + np.reserve( mpoints.size() ); + std::copy( mpoints.begin(), mpoints.end(), std::back_inserter( np ) ); + return np; +} + +const uint PolygonImp::npoints() const +{ + return mnpoints; +} + +PolygonImp* PolygonImp::copy() const +{ + return new PolygonImp( mpoints ); +} + +void PolygonImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +bool PolygonImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( PolygonImp::stype() ) && + static_cast<const PolygonImp&>( rhs ).points() == mpoints; +} + +const ObjectImpType* PolygonImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "polygon", + I18N_NOOP( "polygon" ), + I18N_NOOP( "Select this polygon" ), + I18N_NOOP( "Select polygon %1" ), + I18N_NOOP( "Remove a Polygon" ), + I18N_NOOP( "Add a Polygon" ), + I18N_NOOP( "Move a Polygon" ), + I18N_NOOP( "Attach to this polygon" ), + I18N_NOOP( "Show a Polygon" ), + I18N_NOOP( "Hide a Polygon" ) + ); + + return &t; +} + +const ObjectImpType* PolygonImp::stype3() +{ + static const ObjectImpType t3( + PolygonImp::stype(), "triangle", + I18N_NOOP( "triangle" ), + I18N_NOOP( "Select this triangle" ), + I18N_NOOP( "Select triangle %1" ), + I18N_NOOP( "Remove a Triangle" ), + I18N_NOOP( "Add a Triangle" ), + I18N_NOOP( "Move a Triangle" ), + I18N_NOOP( "Attach to this triangle" ), + I18N_NOOP( "Show a Triangle" ), + I18N_NOOP( "Hide a Triangle" ) + ); + + return &t3; +} + +const ObjectImpType* PolygonImp::stype4() +{ + static const ObjectImpType t4( + PolygonImp::stype(), "quadrilateral", + I18N_NOOP( "quadrilateral" ), + I18N_NOOP( "Select this quadrilateral" ), + I18N_NOOP( "Select quadrilateral %1" ), + I18N_NOOP( "Remove a Quadrilateral" ), + I18N_NOOP( "Add a Quadrilateral" ), + I18N_NOOP( "Move a Quadrilateral" ), + I18N_NOOP( "Attach to this quadrilateral" ), + I18N_NOOP( "Show a Quadrilateral" ), + I18N_NOOP( "Hide a Quadrilateral" ) + ); + + return &t4; +} + +const ObjectImpType* PolygonImp::type() const +{ + uint n = mpoints.size(); + + if ( n == 3 ) return PolygonImp::stype3(); + if ( n == 4 ) return PolygonImp::stype4(); + return PolygonImp::stype(); +} + +bool PolygonImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + assert( which < PolygonImp::numberOfProperties() ); + if ( which < Parent::numberOfProperties() ) + return Parent::isPropertyDefinedOnOrThroughThisImp( which ); + return false; +} + +Rect PolygonImp::surroundingRect() const +{ + Rect r( 0., 0., 0., 0. ); + for ( uint i = 0; i < mpoints.size(); ++i ) + { + r.setContains( mpoints[i] ); + } + return r; +} + +int PolygonImp::windingNumber() const +{ + /* + * this is defined as the sum of the external angles while at + * all vertices, then normalized by 2pi. The external angle + * is the angle we steer at each vertex while we walk along the + * boundary of the polygon. + * In the end we only need to count how many time we cross the (1,0) + * direction (positive x-axis) with a positive sign if we cross while + * steering left and a negative sign viceversa + */ + + int winding = 0; + uint npoints = mpoints.size(); + Coordinate prevside = mpoints[0] - mpoints[npoints-1]; + for ( uint i = 0; i < npoints; ++i ) + { + uint nexti = i + 1; + if ( nexti >= npoints ) nexti = 0; + Coordinate side = mpoints[nexti] - mpoints[i]; + double vecprod = side.x*prevside.y - side.y*prevside.x; + int steeringdir = ( vecprod > 0 ) ? 1 : -1; + if ( vecprod == 0.0 || side.y*prevside.y > 0 ) + { + prevside = side; + continue; // cannot cross the (1,0) direction + } + if ( side.y*steeringdir < 0 && prevside.y*steeringdir >= 0 ) + winding -= steeringdir; + prevside = side; + } + return winding; +} + +bool PolygonImp::isMonotoneSteering() const +{ + /* + * returns true if while walking along the boundary, + * steering is always in the same direction + */ + + uint npoints = mpoints.size(); + Coordinate prevside = mpoints[0] - mpoints[npoints-1]; + int prevsteeringdir = 0; + for ( uint i = 0; i < npoints; ++i ) + { + uint nexti = i + 1; + if ( nexti >= npoints ) nexti = 0; + Coordinate side = mpoints[nexti] - mpoints[i]; + double vecprod = side.x*prevside.y - side.y*prevside.x; + int steeringdir = ( vecprod > 0 ) ? 1 : -1; + if ( vecprod == 0.0 ) + { + prevside = side; + continue; // going straight + } + if ( prevsteeringdir*steeringdir < 0 ) return false; + prevside = side; + prevsteeringdir = steeringdir; + } + return true; +} + +bool PolygonImp::isConvex() const +{ + if ( ! isMonotoneSteering() ) return false; + int winding = windingNumber(); + if ( winding < 0 ) winding = -winding; + assert ( winding > 0 ); + return winding == 1; +} + +std::vector<Coordinate> computeConvexHull( const std::vector<Coordinate>& points ) +{ + /* + * compute the convex hull of the set of points, the resulting list + * is the vertices of the resulting polygon listed in a counter clockwise + * order. This algorithm is on order n^2, probably suboptimal, but + * we don't expect to have large values for n. + */ + + if ( points.size() < 3 ) return points; + std::vector<Coordinate> worklist = points; + std::vector<Coordinate> result; + + double ymin = worklist[0].y; + uint imin = 0; + for ( uint i = 1; i < worklist.size(); ++i ) + { + if ( worklist[i].y < ymin ) + { + ymin = worklist[i].y; + imin = i; + } + } + + // worklist[imin] is definitely on the convex hull, let's start from there + result.push_back( worklist[imin] ); + Coordinate startpoint = worklist[imin]; + Coordinate apoint = worklist[imin]; + double aangle = 0.0; + + while ( ! worklist.empty() ) + { + int besti = -1; + double anglemin = 10000.0; + for ( uint i = 0; i < worklist.size(); ++i ) + { + if ( worklist[i] == apoint ) continue; + Coordinate v = worklist[i] - apoint; + double angle = std::atan2( v.y, v.x ); + while ( angle < aangle ) angle += 2*M_PI; + if ( angle < anglemin ) + { // found a better point + besti = i; + anglemin = angle; + } + } + + if ( besti < 0 ) return result; // this happens, e.g. if all points coincide + apoint = worklist[besti]; + aangle = anglemin; + if ( apoint == startpoint ) + { + return result; + } + result.push_back( apoint ); + worklist.erase( worklist.begin() + besti, worklist.begin() + besti + 1 ); + } + assert( false ); + return result; +} diff --git a/kig/objects/polygon_imp.h b/kig/objects/polygon_imp.h new file mode 100644 index 00000000..9a25d516 --- /dev/null +++ b/kig/objects/polygon_imp.h @@ -0,0 +1,94 @@ +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_POLYGON_IMP_H +#define KIG_OBJECTS_POLYGON_IMP_H + +#include "object_imp.h" +#include "../misc/coordinate.h" +#include <vector> + +/** + * An ObjectImp representing a polygon. + */ +class PolygonImp + : public ObjectImp +{ + uint mnpoints; + std::vector<Coordinate> mpoints; + Coordinate mcenterofmass; +public: + typedef ObjectImp Parent; + /** + * Returns the ObjectImpType representing the PolygonImp type.. + */ + static const ObjectImpType* stype(); + static const ObjectImpType* stype3(); + static const ObjectImpType* stype4(); + + /** + * Constructs a polygon. + */ + PolygonImp( const std::vector<Coordinate>& points ); + PolygonImp( const uint nsides, const std::vector<Coordinate>& points, + const Coordinate& centerofmass ); + ~PolygonImp(); + PolygonImp* copy() const; + + Coordinate attachPoint() const; + ObjectImp* transform( const Transformation& ) const; + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& ) const; + bool inRect( const Rect& r, int width, const KigWidget& ) const; + bool valid() const; + Rect surroundingRect() const; + + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& w ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + /** + * Returns the vector with polygon points. + */ + const std::vector<Coordinate> points() const; + /** + * Returns the center of mass of the polygon. + */ + const Coordinate centerOfMass() const; + /** + * Returns the number of points of this polygon. + */ + const uint npoints() const; + + bool equals( const ObjectImp& rhs ) const; + bool isInPolygon( const Coordinate& p ) const; + int windingNumber() const; + bool isMonotoneSteering() const; + bool isConvex() const; +}; + +std::vector<Coordinate> computeConvexHull( const std::vector<Coordinate>& points ); + +#endif diff --git a/kig/objects/polygon_type.cc b/kig/objects/polygon_type.cc new file mode 100644 index 00000000..bca867da --- /dev/null +++ b/kig/objects/polygon_type.cc @@ -0,0 +1,669 @@ +// Copyright (C) 2003 Maurizio Paolini <paolini@dmf.unicatt.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "polygon_type.h" + +#include "bogus_imp.h" +#include "line_imp.h" +#include "point_imp.h" +#include "polygon_imp.h" +#include "object_calcer.h" + +#include "../misc/common.h" + +#include <klocale.h> +#include <cmath> +#include <vector> + +/* + * triangle by its vertices + */ + +static const char triangle_constructstatement[] = I18N_NOOP( "Construct a triangle with this vertex" ); +static const char triangle_constructstatement2[] = I18N_NOOP( "Select a point to be a vertex of the new triangle..." ); + +static const struct ArgsParser::spec argsspecTriangleB3P[] = +{ + { PointImp::stype(), triangle_constructstatement, triangle_constructstatement2, true }, + { PointImp::stype(), triangle_constructstatement, triangle_constructstatement2, true }, + { PointImp::stype(), triangle_constructstatement, triangle_constructstatement2, true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( TriangleB3PType ) + +TriangleB3PType::TriangleB3PType() + : ArgsParserObjectType( "TriangleB3P", argsspecTriangleB3P, 3 ) +{ +} + +TriangleB3PType::~TriangleB3PType() +{ +} + +const TriangleB3PType* TriangleB3PType::instance() +{ + static const TriangleB3PType s; + return &s; +} + +ObjectImp* TriangleB3PType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents, 1 ) ) return new InvalidImp; + std::vector<Coordinate> points; + + Coordinate centerofmass3 = Coordinate( 0, 0 ); + for ( Args::const_iterator i = parents.begin(); i != parents.end(); ++i ) + { + Coordinate point = static_cast<const PointImp*>( *i )->coordinate(); + centerofmass3 += point; + points.push_back( point ); + } + return new PolygonImp( 3, points, centerofmass3/3 ); +} + +const ObjectImpType* TriangleB3PType::resultId() const +{ + return PolygonImp::stype(); +} + +bool TriangleB3PType::canMove( const ObjectTypeCalcer& o ) const +{ + return isFreelyTranslatable( o ); +} + +bool TriangleB3PType::isFreelyTranslatable( const ObjectTypeCalcer& o ) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + return parents[0]->isFreelyTranslatable() && + parents[1]->isFreelyTranslatable() && + parents[2]->isFreelyTranslatable(); +} + +void TriangleB3PType::move( ObjectTypeCalcer& o, const Coordinate& to, + const KigDocument& d ) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + assert( margsparser.checkArgs( parents ) ); + const Coordinate a = static_cast<const PointImp*>( parents[0]->imp() )->coordinate(); + const Coordinate b = static_cast<const PointImp*>( parents[1]->imp() )->coordinate(); + const Coordinate c = static_cast<const PointImp*>( parents[2]->imp() )->coordinate(); + if ( parents[0]->canMove() ) + parents[0]->move( to, d ); + if ( parents[1]->canMove() ) + parents[1]->move( to + b - a, d ); + if ( parents[2]->canMove() ) + parents[2]->move( to + c - a, d ); +} + +const Coordinate TriangleB3PType::moveReferencePoint( const ObjectTypeCalcer& o ) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + assert( margsparser.checkArgs( parents ) ); + return static_cast<const PointImp*>( parents[0]->imp() )->coordinate(); +} + +std::vector<ObjectCalcer*> TriangleB3PType::movableParents( const ObjectTypeCalcer& ourobj ) const +{ + std::vector<ObjectCalcer*> parents = ourobj.parents(); + std::set<ObjectCalcer*> ret; + std::vector<ObjectCalcer*> tmp = parents[0]->movableParents(); + ret.insert( tmp.begin(), tmp.end() ); + tmp = parents[1]->movableParents(); + ret.insert( tmp.begin(), tmp.end() ); + tmp = parents[2]->movableParents(); + ret.insert( tmp.begin(), tmp.end() ); + ret.insert( parents.begin(), parents.end() ); + return std::vector<ObjectCalcer*>( ret.begin(), ret.end() ); +} + +/* + * generic polygon + */ + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( PolygonBNPType ) + +PolygonBNPType::PolygonBNPType() + : ObjectType( "PolygonBNP" ) +{ +} + +PolygonBNPType::~PolygonBNPType() +{ +} + +const PolygonBNPType* PolygonBNPType::instance() +{ + static const PolygonBNPType s; + return &s; +} + +ObjectImp* PolygonBNPType::calc( const Args& parents, const KigDocument& ) const +{ + uint count = parents.size(); + assert (count >= 3); /* non sono ammessi poligoni con meno di tre lati */ +// if ( parents[0] != parents[count] ) return new InvalidImp; + std::vector<Coordinate> points; + + uint npoints = 0; + Coordinate centerofmassn = Coordinate( 0, 0 ); + + for ( uint i = 0; i < count; ++i ) + { + npoints++; + if ( ! parents[i]->inherits( PointImp::stype() ) ) return new InvalidImp; + Coordinate point = static_cast<const PointImp*>( parents[i] )->coordinate(); + centerofmassn += point; + points.push_back( point ); + } + return new PolygonImp( npoints, points, centerofmassn/npoints ); +} + +const ObjectImpType* PolygonBNPType::resultId() const +{ + return PolygonImp::stype(); +} + +const ObjectImpType* PolygonBNPType::impRequirement( const ObjectImp*, const Args& ) const +{ + return PointImp::stype(); +} + +bool PolygonBNPType::isDefinedOnOrThrough( const ObjectImp*, const Args& ) const +{ + return false; /* should be true? */ +} + +std::vector<ObjectCalcer*> PolygonBNPType::sortArgs( const std::vector<ObjectCalcer*>& args ) const +{ + return args; /* should already be in correct order */ +} + +Args PolygonBNPType::sortArgs( const Args& args ) const +{ + return args; +} + +bool PolygonBNPType::canMove( const ObjectTypeCalcer& o ) const +{ + return isFreelyTranslatable( o ); +} + +bool PolygonBNPType::isFreelyTranslatable( const ObjectTypeCalcer& o ) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + for ( uint i = 0; i < parents.size(); ++i ) + { + if ( !parents[i]->isFreelyTranslatable() ) return false; + } + return true; +} + +void PolygonBNPType::move( ObjectTypeCalcer& o, const Coordinate& to, + const KigDocument& d ) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + const Coordinate ref = static_cast<const PointImp*>( parents[0]->imp() )->coordinate(); + for ( uint i = 0; i < parents.size(); ++i ) + { + const Coordinate a = static_cast<const PointImp*>( parents[i]->imp() )->coordinate(); + parents[i]->move( to + a - ref, d ); + } +} + +const Coordinate PolygonBNPType::moveReferencePoint( const ObjectTypeCalcer& o +) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + return static_cast<const PointImp*>( parents[0]->imp() )->coordinate(); +} + +std::vector<ObjectCalcer*> PolygonBNPType::movableParents( const ObjectTypeCalcer& ourobj ) const +{ + std::vector<ObjectCalcer*> parents = ourobj.parents(); + std::set<ObjectCalcer*> ret; + for ( uint i = 0; i < parents.size(); ++i ) + { + std::vector<ObjectCalcer*> tmp = parents[i]->movableParents(); + ret.insert( tmp.begin(), tmp.end() ); + } + ret.insert( parents.begin(), parents.end() ); + return std::vector<ObjectCalcer*>( ret.begin(), ret.end() ); +} + +/* + * regular polygon by center and vertex + */ + +//static const char constructpoligonthroughpointstat[] = I18N_NOOP( "Construct a polygon with this vertex" ); +// +//static const char constructpoligonwithcenterstat[] = I18N_NOOP( "Construct a polygon with this center" ); +// +//static const ArgsParser::spec argsspecPoligonBCV[] = +//{ +// { PointImp::stype(), constructpoligonwithcenterstat, +// I18N_NOOP( "Select the center of the new polygon..." ), false }, +// { PointImp::stype(), constructpoligonthroughpointstat, +// I18N_NOOP( "Select a vertex for the new polygon..." ), true }, +// { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false } +//}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( PolygonBCVType ) + +PolygonBCVType::PolygonBCVType() + : ObjectType( "PoligonBCV" ) +// we keep the name "PoligonBCV" although syntactically incorrect for +// compatibility reasons with old kig files +// : ArgsParserObjectType( "PoligonBCV", argsspecPoligonBCV, 3 ) +{ +} + +PolygonBCVType::~PolygonBCVType() +{ +} + +const PolygonBCVType* PolygonBCVType::instance() +{ + static const PolygonBCVType s; + return &s; +} + +ObjectImp* PolygonBCVType::calc( const Args& parents, const KigDocument& ) const +{ + if ( parents.size() < 3 || parents.size() > 4 ) return new InvalidImp; + + if ( ( ! parents[0]->inherits( PointImp::stype() ) ) || + ( ! parents[1]->inherits( PointImp::stype() ) ) || + ( ! parents[2]->inherits( IntImp::stype() ) ) ) + return new InvalidImp; + + const Coordinate center = + static_cast<const PointImp*>( parents[0] )->coordinate(); + const Coordinate vertex = + static_cast<const PointImp*>( parents[1] )->coordinate(); + const int sides = + static_cast<const IntImp*>( parents[2] )->data(); + int twist = 1; + if ( parents.size() == 4 ) + { + if ( ! parents[3]->inherits( IntImp::stype() ) ) return new InvalidImp; + twist = static_cast<const IntImp*>( parents[3] )->data(); + } + std::vector<Coordinate> vertexes; + + double dx = vertex.x - center.x; + double dy = vertex.y - center.y; + + for ( int i = 1; i <= sides; i++ ) + { + double alfa = 2*twist*M_PI/sides; + double theta1 = alfa*i - alfa; + double ctheta1 = cos(theta1); + double stheta1 = sin(theta1); + + Coordinate v1 = center + Coordinate( ctheta1*dx - stheta1*dy, + stheta1*dx + ctheta1*dy ); + vertexes.push_back( v1 ); + } + return new PolygonImp( uint (sides), vertexes, center ); +} + +const ObjectImpType* PolygonBCVType::resultId() const +{ + return SegmentImp::stype(); +} + +const ObjectImpType* PolygonBCVType::impRequirement( const ObjectImp* obj, const Args& ) const +{ + if ( obj->inherits( PointImp::stype() ) ) + return PointImp::stype(); + + if ( obj->inherits( IntImp::stype() ) ) + return IntImp::stype(); + + return 0; +} + +bool PolygonBCVType::isDefinedOnOrThrough( const ObjectImp*, const Args& ) const +{ + return false; /* should be true? */ +} + +std::vector<ObjectCalcer*> PolygonBCVType::sortArgs( const std::vector<ObjectCalcer*>& args ) const +{ + return args; /* should already be in correct order */ +} + +Args PolygonBCVType::sortArgs( const Args& args ) const +{ + return args; +} + +bool PolygonBCVType::canMove( const ObjectTypeCalcer& o ) const +{ + return isFreelyTranslatable( o ); +} + +bool PolygonBCVType::isFreelyTranslatable( const ObjectTypeCalcer& o ) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + return parents[0]->isFreelyTranslatable() && + parents[1]->isFreelyTranslatable(); +} + +void PolygonBCVType::move( ObjectTypeCalcer& o, const Coordinate& to, + const KigDocument& d ) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + // assert( margsparser.checkArgs( parents ) ); + if ( ! parents[0]->imp()->inherits( PointImp::stype() ) || + ! parents[1]->imp()->inherits( PointImp::stype() ) ) return; + + const Coordinate a = static_cast<const PointImp*>( parents[0]->imp() )->coordinate(); + const Coordinate b = static_cast<const PointImp*>( parents[1]->imp() )->coordinate(); + parents[0]->move( to, d ); + parents[1]->move( to + b - a, d ); +} + +const Coordinate PolygonBCVType::moveReferencePoint( const ObjectTypeCalcer& o) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + // assert( margsparser.checkArgs( parents ) ); + if ( ! parents[0]->imp()->inherits( PointImp::stype() ) ) return Coordinate::invalidCoord(); + + return static_cast<const PointImp*>( parents[0]->imp() )->coordinate(); +} + +std::vector<ObjectCalcer*> PolygonBCVType::movableParents( const ObjectTypeCalcer& ourobj ) const +{ + std::vector<ObjectCalcer*> parents = ourobj.parents(); + std::set<ObjectCalcer*> ret; + std::vector<ObjectCalcer*> tmp = parents[0]->movableParents(); + ret.insert( tmp.begin(), tmp.end() ); + tmp = parents[1]->movableParents(); + ret.insert( tmp.begin(), tmp.end() ); + ret.insert( &parents[0], &parents[1] ); + return std::vector<ObjectCalcer*>( ret.begin(), ret.end() ); +} + +/* polygon-line intersection */ + +static const ArgsParser::spec argsspecPolygonLineIntersection[] = +{ + { PolygonImp::stype(), I18N_NOOP( "Intersect this polygon with a line" ), + I18N_NOOP( "Select the polygon of which you want the intersection with a line..." ), false }, + { AbstractLineImp::stype(), "Intersect this line with a polygon", "Select the line of which you want the intersection with a polygon...", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( PolygonLineIntersectionType ) + +PolygonLineIntersectionType::PolygonLineIntersectionType() + : ArgsParserObjectType( "PolygonLineIntersection", argsspecPolygonLineIntersection, 2 ) +{ +} + +PolygonLineIntersectionType::~PolygonLineIntersectionType() +{ +} + +const PolygonLineIntersectionType* PolygonLineIntersectionType::instance() +{ + static const PolygonLineIntersectionType t; + return &t; +} + +/* + * Intersection of a polygon and a line/ray/segment. + * + * geometrically speaking the result is always a collection of + * collinear nonintersecting open segments (at most one if the + * polygon is convex). Since we don't know in advance how many + * segments will result, the obvious choice is to return an + * InvalidImp in cases when the result is *not* a single segment + * + * computing the two ends of this segment is more tricky then one + * expects especially when intersecting segments/rays. + * + * particularly "difficult" situations are those where we intersect + * a segment/ray with an/the endpoint coinciding with a vertex of + * the polygon, especially if that vertex is a "reentrant" (concave) + * vertex of the polygon. + */ + +ObjectImp* PolygonLineIntersectionType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + const PolygonImp* polygon = static_cast<const PolygonImp*>( parents[0] ); + const std::vector<Coordinate> ppoints = polygon->points(); + const LineData line = static_cast<const AbstractLineImp*>( parents[1] )->data(); + Coordinate intersections[2]; + uint whichintersection = 0; + + bool boundleft = false; + bool boundright = false; + if ( parents[1]->inherits( SegmentImp::stype() ) ) + { + boundleft = boundright = true; + } + if ( parents[1]->inherits( RayImp::stype() ) ) + { + boundleft = true; + } + Coordinate a = line.a; + double abx = line.b.x - a.x; + double aby = line.b.y - a.y; + + double leftendinside = false; + double rightendinside = false; + Coordinate prevpoint = ppoints.back() - a; + bool prevpointbelow = ( abx*prevpoint.y <= aby*prevpoint.x ); + for ( uint i = 0; i < ppoints.size(); ++i ) + { + Coordinate point = ppoints[i] - a; + bool pointbelow = ( abx*point.y <= aby*point.x ); + if ( pointbelow != prevpointbelow ) + { + /* found an intersection with the support line + * compute the value of the parameter... + */ + double dcx = point.x - prevpoint.x; + double dcy = point.y - prevpoint.y; + double num = point.x*dcy - point.y*dcx; + double den = abx*dcy - aby*dcx; + if ( std::fabs( den ) <= 1.e-6*std::fabs( num ) ) continue; //parallel + double t = num/den; + if ( boundleft && t <= 0 ) + { + leftendinside = !leftendinside; + } + else if ( boundright && t >= 1 ) + { + rightendinside = !rightendinside; + } + else + { + if ( whichintersection >= 2 ) return new InvalidImp; + intersections[whichintersection++] = a + t*Coordinate( abx, aby ); + } + } + prevpoint = point; + prevpointbelow = pointbelow; + } + + if ( leftendinside ) + { + if ( whichintersection >= 2 ) return new InvalidImp; + intersections[whichintersection++] = a; + } + + if ( rightendinside ) + { + if ( whichintersection >= 2 ) return new InvalidImp; + intersections[whichintersection++] = line.b; + } + + switch (whichintersection) + { + case 1: /* just for completeness: this should never happen */ + return new PointImp( intersections[0] ); + break; + case 2: + return new SegmentImp( intersections[0], intersections[1] ); + break; + case 0: + default: + return new InvalidImp; + break; + } +} + +const ObjectImpType* PolygonLineIntersectionType::resultId() const +{ + return SegmentImp::stype(); +} + +/* polygon vertices */ + +static const ArgsParser::spec argsspecPolygonVertex[] = +{ + { PolygonImp::stype(), I18N_NOOP( "Construct the vertices of this polygon" ), + I18N_NOOP( "Select the polygon of which you want to construct the vertices..." ), true }, + { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( PolygonVertexType ) + +PolygonVertexType::PolygonVertexType() + : ArgsParserObjectType( "PolygonVertex", argsspecPolygonVertex, 2 ) +{ +} + +PolygonVertexType::~PolygonVertexType() +{ +} + +const PolygonVertexType* PolygonVertexType::instance() +{ + static const PolygonVertexType t; + return &t; +} + +ObjectImp* PolygonVertexType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + const std::vector<Coordinate> ppoints = static_cast<const PolygonImp*>( parents[0] )->points(); + const uint i = static_cast<const IntImp*>( parents[1] )->data(); + + if ( i >= ppoints.size() ) return new InvalidImp; + + return new PointImp( ppoints[i] ); +} + +const ObjectImpType* PolygonVertexType::resultId() const +{ + return PointImp::stype(); +} + +/* polygon sides */ + +static const ArgsParser::spec argsspecPolygonSide[] = +{ + { PolygonImp::stype(), I18N_NOOP( "Construct the sides of this polygon" ), + I18N_NOOP( "Select the polygon of which you want to construct the sides..." ), false }, + { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( PolygonSideType ) + +PolygonSideType::PolygonSideType() + : ArgsParserObjectType( "PolygonSide", argsspecPolygonSide, 2 ) +{ +} + +PolygonSideType::~PolygonSideType() +{ +} + +const PolygonSideType* PolygonSideType::instance() +{ + static const PolygonSideType t; + return &t; +} + +ObjectImp* PolygonSideType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + const std::vector<Coordinate> ppoints = static_cast<const PolygonImp*>( parents[0] )->points(); + const uint i = static_cast<const IntImp*>( parents[1] )->data(); + + if ( i >= ppoints.size() ) return new InvalidImp; + + uint nexti = i + 1; + if ( nexti >= ppoints.size() ) nexti = 0; + + return new SegmentImp( ppoints[i], ppoints[nexti] ); +} + +const ObjectImpType* PolygonSideType::resultId() const +{ + return SegmentImp::stype(); +} + +/* convex hull of a polygon */ + +static const ArgsParser::spec argsspecConvexHull[] = +{ + { PolygonImp::stype(), I18N_NOOP( "Construct the convex hull of this polygon" ), + I18N_NOOP( "Select the polygon of which you want to construct the convex hull..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConvexHullType ) + +ConvexHullType::ConvexHullType() + : ArgsParserObjectType( "ConvexHull", argsspecConvexHull, 1 ) +{ +} + +ConvexHullType::~ConvexHullType() +{ +} + +const ConvexHullType* ConvexHullType::instance() +{ + static const ConvexHullType t; + return &t; +} + +ObjectImp* ConvexHullType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + const std::vector<Coordinate> ppoints = static_cast<const PolygonImp*>( parents[0] )->points(); + + if ( ppoints.size() < 3 ) return new InvalidImp; + + std::vector<Coordinate> hull = computeConvexHull( ppoints ); + if ( hull.size() < 3 ) return new InvalidImp; + return new PolygonImp( hull ); +} + +const ObjectImpType* ConvexHullType::resultId() const +{ + return PolygonImp::stype(); +} diff --git a/kig/objects/polygon_type.h b/kig/objects/polygon_type.h new file mode 100644 index 00000000..a49100bd --- /dev/null +++ b/kig/objects/polygon_type.h @@ -0,0 +1,139 @@ +// Copyright (C) 2003 Maurizio Paolini <paolini@dmf.unicatt.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_POLIGON_TYPE_H +#define KIG_OBJECTS_POLIGON_TYPE_H + +#include "base_type.h" + +/** + * Triangle by its vertices + */ +class TriangleB3PType + : public ArgsParserObjectType +{ + TriangleB3PType(); + ~TriangleB3PType(); +public: + static const TriangleB3PType* instance(); + + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; + bool canMove( const ObjectTypeCalcer& o ) const; + bool isFreelyTranslatable( const ObjectTypeCalcer& o ) const; + std::vector<ObjectCalcer*> movableParents( const ObjectTypeCalcer& ourobj ) const; + void move( ObjectTypeCalcer& o, const Coordinate& to, + const KigDocument& d ) const; + const Coordinate moveReferencePoint( const ObjectTypeCalcer& o ) const; +}; + +/** + * Polygon by its vertices + */ +class PolygonBNPType + : public ObjectType +{ + PolygonBNPType(); + ~PolygonBNPType(); +public: + static const PolygonBNPType* instance(); + + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + bool isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const; + std::vector<ObjectCalcer*> sortArgs( const std::vector<ObjectCalcer*>& args ) const; + Args sortArgs( const Args& args ) const; + + bool canMove( const ObjectTypeCalcer& o ) const; + bool isFreelyTranslatable( const ObjectTypeCalcer& o ) const; + std::vector<ObjectCalcer*> movableParents( const ObjectTypeCalcer& ourobj ) const; + void move( ObjectTypeCalcer& o, const Coordinate& to, + const KigDocument& d ) const; + const Coordinate moveReferencePoint( const ObjectTypeCalcer& o ) const; +}; + +/** + * Polygon by center and vertex + */ +class PolygonBCVType + : public ObjectType +{ + PolygonBCVType(); + ~PolygonBCVType(); +public: + static const PolygonBCVType* instance(); + + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + bool isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const; + std::vector<ObjectCalcer*> sortArgs( const std::vector<ObjectCalcer*>& args ) const; + Args sortArgs( const Args& args ) const; + bool canMove( const ObjectTypeCalcer& o ) const; + bool isFreelyTranslatable( const ObjectTypeCalcer& o ) const; + std::vector<ObjectCalcer*> movableParents( const ObjectTypeCalcer& ourobj ) const; + void move( ObjectTypeCalcer& o, const Coordinate& to, + const KigDocument& d ) const; + const Coordinate moveReferencePoint( const ObjectTypeCalcer& o ) const; +}; + +class PolygonLineIntersectionType + : public ArgsParserObjectType +{ + PolygonLineIntersectionType(); + ~PolygonLineIntersectionType(); +public: + static const PolygonLineIntersectionType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class PolygonVertexType + : public ArgsParserObjectType +{ + PolygonVertexType(); + ~PolygonVertexType(); +public: + static const PolygonVertexType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class PolygonSideType + : public ArgsParserObjectType +{ + PolygonSideType(); + ~PolygonSideType(); +public: + static const PolygonSideType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ConvexHullType + : public ArgsParserObjectType +{ + ConvexHullType(); + ~ConvexHullType(); +public: + static const ConvexHullType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; +#endif diff --git a/kig/objects/special_calcers.cc b/kig/objects/special_calcers.cc new file mode 100644 index 00000000..e70bd4e9 --- /dev/null +++ b/kig/objects/special_calcers.cc @@ -0,0 +1,84 @@ +// Copyright (C) 2004 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "special_calcers.h" + +static const ArgsParser::spec argsspecMeasureTransport[] = +{ + { CircleImp::stype(), I18N_NOOP( "Transport a measure on this circle" ) }, + { PointImp::stype(), I18N_NOOP( "Project this point onto the circle" ) }, + { SegmentImp::stype(), I18N_NOOP( "Segment to transport" ) } +}; + +static ArgsParser measuretransportargsparser( argsspecMeasureTransport, 3 ); + +std::vector<ObjectCalcer*> MeasureTransportCalcer::parents() const +{ + std::vector<ObjectCalcer*> ret; + ret.push_back( mcircle ); + ret.push_back( mpoint ); + ret.push_back( msegment ); + return ret; +} + +MeasureTransportCalcer::MeasureTransportCalcer(ObjectCalcer* circle, ObjectCalcer* point, ObjectCalcer* segment ) + : mcircle( circle ), mpoint( point ), msegment( segment ), mimp( 0 ) +{ +} + +MeasureTransportCalcer::~MeasureTransportCalcer() +{ +} + +void MeasureTransportCalcer::calc( const KigDocument& ) +{ + if ( ! measuretransportargsparser.checkArgs( parents() ) ) + return new InvalidImp(); + + if ( ! isPointOnCurve( mpoint, mcircle ) ) + return new InvalidImp(); + + const CircleImp* c = static_cast<const CircleImp*>( mcircle->imp() ); + const PointImp* p = static_cast<const PointImp*>( mpoint->imp() ); + const SegmentImp* s = static_cast<const SegmentImp*>( msegment->imp() ); + double param = c->getParam( p->coordinate(), doc ); + double measure = s->length(); + measure /= 2*c->radius()*M_PI; + param += measure; + while (param > 1) param -= 1; + + const Coordinate nc = c->getPoint( param, doc ); + if ( nc.valid() ) return new PointImp( nc ); + else return new InvalidImp; +} + +const ObjectImpType* MeasureTransportCalcer::impRequirement( + ObjectCalcer* o, const std::vector<ObjectCalcer*>& os ) const +{ + if ( o->imp()->inherits( CircleImp::stype() ) ) + return CircleImp::stype(); + else if ( o->imp()->inherits( PointImp::stype() ) ) + return PointImp::stype(); + else if ( o->imp()->inherits( SegmentImp::stype() ) ) + return SegmentImp::stype(); + else + { + assert( false ); + return 0; + } +} + diff --git a/kig/objects/special_calcers.h b/kig/objects/special_calcers.h new file mode 100644 index 00000000..640587cc --- /dev/null +++ b/kig/objects/special_calcers.h @@ -0,0 +1,38 @@ +// Copyright (C) 2004 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_SPECIAL_CALCERS_H +#define KIG_OBJECTS_SPECIAL_CALCERS_H + +class MeasureTransportCalcer + : public ObjectCalcer +{ + ObjectCalcer* mcircle; + ObjectCalcer* mpoint; + ObjectCalcer* msegment; + ObjectImp* mimp; +public: + MeasureTransportCalcer(ObjectCalcer* circle, ObjectCalcer* point, ObjectCalcer* segment ); + ~MeasureTransportCalcer(); + + std::vector<ObjectCalcer*> parents() const; + void calc( const KigDocument& ); + const ObjectImpType* impRequirement( + ObjectCalcer* o, const std::vector<ObjectCalcer*>& os ) const; +}; + +#endif diff --git a/kig/objects/tangent_type.cc b/kig/objects/tangent_type.cc new file mode 100644 index 00000000..12ebda23 --- /dev/null +++ b/kig/objects/tangent_type.cc @@ -0,0 +1,285 @@ +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "tangent_type.h" + +#include "bogus_imp.h" +#include "conic_imp.h" +#include "cubic_imp.h" +#include "curve_imp.h" +#include "other_imp.h" +#include "point_imp.h" +#include "line_imp.h" + +#include "../misc/common.h" +#include "../misc/conic-common.h" +//#include "../misc/calcpaths.h" +#include "../kig/kig_part.h" +#include "../kig/kig_view.h" + +static const char constructlinetangentpoint[] = "SHOULDNOTBESEEN"; +static const char selecttangent1[] = + I18N_NOOP( "Select the curve..." ); +static const char selecttangent2[] = + I18N_NOOP( "Select the point for the tangent to go through..." ); + +static const ArgsParser::spec argsspecTangentConic[] = +{ + { ConicImp::stype(), "SHOULDNOTBESEEN", selecttangent1, false }, + { PointImp::stype(), constructlinetangentpoint, selecttangent2, true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( TangentConicType ) + +TangentConicType::TangentConicType() + : ArgsParserObjectType( "TangentConic", argsspecTangentConic, 2 ) +{ +} + +TangentConicType::~TangentConicType() +{ +} + +const TangentConicType* TangentConicType::instance() +{ + static const TangentConicType t; + return &t; +} + +ObjectImp* TangentConicType::calc( const Args& args, const KigDocument& doc ) const +{ + if ( !margsparser.checkArgs( args ) ) + return new InvalidImp; + + const ConicImp* c = static_cast<const ConicImp*>( args[0] ); + const Coordinate& p = static_cast<const PointImp*>( args[1] )->coordinate(); + + if ( !c->containsPoint( p, doc ) ) + return new InvalidImp; + + bool ok; + const LineData tangent = calcConicPolarLine( c->cartesianData(), p, ok ); + + if ( !ok ) + return new InvalidImp; + + return new LineImp( tangent ); +} + +const ObjectImpType* TangentConicType::resultId() const +{ + return LineImp::stype(); +} + +/*** Arc starts here ***/ + +static const ArgsParser::spec argsspecTangentArc[] = +{ + { ArcImp::stype(), "SHOULDNOTBESEEN", selecttangent1, false }, + { PointImp::stype(), constructlinetangentpoint, selecttangent2, true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( TangentArcType ) + +TangentArcType::TangentArcType() + : ArgsParserObjectType( "TangentArc", argsspecTangentArc, 2 ) +{ +} + +TangentArcType::~TangentArcType() +{ +} + +const TangentArcType* TangentArcType::instance() +{ + static const TangentArcType t; + return &t; +} + +ObjectImp* TangentArcType::calc( const Args& args, const KigDocument& doc ) const +{ + if ( !margsparser.checkArgs( args ) ) + return new InvalidImp; + + const ArcImp* arc = static_cast<const ArcImp*>( args[0] ); + const Coordinate& p = static_cast<const PointImp*>( args[1] )->coordinate(); + + if ( !arc->containsPoint( p, doc ) ) + return new InvalidImp; + + Coordinate c = arc->center(); + double sqr = arc->radius(); + sqr *= sqr; + ConicCartesianData data( 1.0, 1.0, 0.0, -2*c.x, -2*c.y, c.x*c.x + c.y*c.y - sqr ); + + bool ok; + const LineData tangent = calcConicPolarLine( data, p, ok ); + + if ( !ok ) + return new InvalidImp; + + return new LineImp( tangent ); +} + +const ObjectImpType* TangentArcType::resultId() const +{ + return LineImp::stype(); +} + +/**** Cubic starts here ****/ + +static const ArgsParser::spec argsspecTangentCubic[] = +{ + { CubicImp::stype(), "SHOULDNOTBESEEN", selecttangent1, false }, + { PointImp::stype(), constructlinetangentpoint, selecttangent2, true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( TangentCubicType ) + +TangentCubicType::TangentCubicType() + : ArgsParserObjectType( "TangentCubic", argsspecTangentCubic, 2 ) +{ +} + +TangentCubicType::~TangentCubicType() +{ +} + +const TangentCubicType* TangentCubicType::instance() +{ + static const TangentCubicType t; + return &t; +} + +ObjectImp* TangentCubicType::calc( const Args& args, const KigDocument& doc ) const +{ + if ( !margsparser.checkArgs( args ) ) + return new InvalidImp; + + const CubicImp* cubic = static_cast<const CubicImp*>( args[0] ); + const Coordinate& p = static_cast<const PointImp*>( args[1] )->coordinate(); + + if ( !cubic->containsPoint( p, doc ) ) + return new InvalidImp; + + double x = p.x; + double y = p.y; + CubicCartesianData data = cubic->data(); +// double aconst = data.coeffs[0]; + double ax = data.coeffs[1]; + double ay = data.coeffs[2]; + double axx = data.coeffs[3]; + double axy = data.coeffs[4]; + double ayy = data.coeffs[5]; + double axxx = data.coeffs[6]; + double axxy = data.coeffs[7]; + double axyy = data.coeffs[8]; + double ayyy = data.coeffs[9]; + + /* mp: the tangent vector (-gy,gx) is orthogonal to the gradient (gx,gy) + * which is easy to compute from the CartesianData + * + * note: same thing could be done for conics, which would be + * much more efficient... + */ + + Coordinate tangvec = Coordinate ( + - axxy*x*x - 2*axyy*x*y - 3*ayyy*y*y - axy*x - 2*ayy*y - ay, + 3*axxx*x*x + 2*axxy*x*y + axyy*y*y + 2*axx*x + axy*y + ax + ); + const LineData tangent = LineData( p, p + tangvec ); + + return new LineImp( tangent ); +} + +const ObjectImpType* TangentCubicType::resultId() const +{ + return LineImp::stype(); +} + +/**** Curve (locus) starts here ****/ + +static const ArgsParser::spec argsspecTangentCurve[] = +{ + { CurveImp::stype(), "SHOULDNOTBESEEN", selecttangent1, false }, + { PointImp::stype(), constructlinetangentpoint, selecttangent2, true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( TangentCurveType ) + +TangentCurveType::TangentCurveType() + : ArgsParserObjectType( "TangentCurve", argsspecTangentCurve, 2 ) +{ +} + +TangentCurveType::~TangentCurveType() +{ +} + +const TangentCurveType* TangentCurveType::instance() +{ + static const TangentCurveType t; + return &t; +} + +ObjectImp* TangentCurveType::calc( const Args& args, const KigDocument& doc ) const +{ + if ( !margsparser.checkArgs( args ) ) + return new InvalidImp; + + const CurveImp* curve = static_cast<const CurveImp*>( args[0] ); + const Coordinate& p = static_cast<const PointImp*>( args[1] )->coordinate(); + if ( !curve->containsPoint( p, doc ) ) + return new InvalidImp; + + const double t = curve->getParam( p, doc ); + const double tau0 = 1e-3; + const double sigma = 1e-5; + const int maxiter = 20; + + double tau = tau0; + Coordinate tang, err; + double tplus = t + tau; + double tminus = t - tau; + if ( tplus > 1 ) {tplus = 1; tminus = 1 - 2*tau;} + if ( tminus < 0 ) {tminus = 0; tplus = 2*tau;} + Coordinate tangold = (curve->getPoint( tplus, doc ) - curve->getPoint( tminus, doc ))/(2*tau); + + for (int i = 0; i < maxiter; i++) + { + tau = tau/2; + tplus = t + tau; + tminus = t - tau; + if ( tplus > 1 ) {tplus = 1; tminus = 1 - 2*tau;} + if ( tminus < 0 ) {tminus = 0; tplus = 2*tau;} + tang = (curve->getPoint( tplus, doc ) - curve->getPoint( tminus, doc ))/(2*tau); + err = (tangold - tang)/3; + if (err.length() < sigma) + { + tang = (4*tang - tangold)/3; + const LineData tangent = LineData( p, p + tang ); + return new LineImp( tangent ); + } + tangold = tang; + } + return new InvalidImp; +} + +const ObjectImpType* TangentCurveType::resultId() const +{ + return LineImp::stype(); +} diff --git a/kig/objects/tangent_type.h b/kig/objects/tangent_type.h new file mode 100644 index 00000000..ccc00dea --- /dev/null +++ b/kig/objects/tangent_type.h @@ -0,0 +1,83 @@ +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_TANGENT_TYPE_H +#define KIG_OBJECTS_TANGENT_TYPE_H + +#include "base_type.h" + +/** + * the line tangent to a generic conic + */ +class TangentConicType + : public ArgsParserObjectType +{ + typedef ArgsParserObjectType Parent; + TangentConicType(); + ~TangentConicType(); +public: + static const TangentConicType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +/** + * the line tangent to an arc + */ +class TangentArcType + : public ArgsParserObjectType +{ + typedef ArgsParserObjectType Parent; + TangentArcType(); + ~TangentArcType(); +public: + static const TangentArcType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +/** + * the line tangent to a cubic + */ +class TangentCubicType + : public ArgsParserObjectType +{ + typedef ArgsParserObjectType Parent; + TangentCubicType(); + ~TangentCubicType(); +public: + static const TangentCubicType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +/** + * the line tangent to a curve + */ +class TangentCurveType + : public ArgsParserObjectType +{ + typedef ArgsParserObjectType Parent; + TangentCurveType(); + ~TangentCurveType(); +public: + static const TangentCurveType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/tests_type.cc b/kig/objects/tests_type.cc new file mode 100644 index 00000000..e85c111e --- /dev/null +++ b/kig/objects/tests_type.cc @@ -0,0 +1,382 @@ +// Copyright (C) 2004 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "tests_type.h" + +#include "line_imp.h" +#include "polygon_imp.h" +#include "point_imp.h" +#include "bogus_imp.h" +#include "other_imp.h" + +#include <cmath> + +static const ArgsParser::spec argsspecAreParallel[] = +{ + { AbstractLineImp::stype(), I18N_NOOP( "Is this line parallel?" ), + I18N_NOOP( "Select the first of the two possibly parallel lines..." ), false }, + { AbstractLineImp::stype(), I18N_NOOP( "Parallel to this line?" ), + I18N_NOOP( "Select the other of the two possibly parallel lines..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( AreParallelType ) + +AreParallelType::AreParallelType() + : ArgsParserObjectType( "AreParallel", + argsspecAreParallel, 2 ) +{ +} + +AreParallelType::~AreParallelType() +{ +} + +const AreParallelType* AreParallelType::instance() +{ + static const AreParallelType t; + return &t; +} + +ObjectImp* AreParallelType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + const LineData& l1 = static_cast<const AbstractLineImp*>( parents[0] )->data(); + const LineData& l2 = static_cast<const AbstractLineImp*>( parents[1] )->data(); + + if ( l1.isParallelTo( l2 ) ) + return new TestResultImp( i18n( "These lines are parallel." ) ); + else + return new TestResultImp( i18n( "These lines are not parallel." ) ); + +} + +const ObjectImpType* AreParallelType::resultId() const +{ + return TestResultImp::stype(); +} + +static const ArgsParser::spec argsspecAreOrthogonal[] = +{ + { AbstractLineImp::stype(), I18N_NOOP( "Is this line orthogonal?" ), + I18N_NOOP( "Select the first of the two possibly orthogonal lines..." ), false }, + { AbstractLineImp::stype(), I18N_NOOP( "Orthogonal to this line?" ), + I18N_NOOP( "Select the other of the two possibly orthogonal lines..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( AreOrthogonalType ) + +AreOrthogonalType::AreOrthogonalType() + : ArgsParserObjectType( "AreOrthogonal", + argsspecAreOrthogonal, 2 ) +{ +} + +AreOrthogonalType::~AreOrthogonalType() +{ +} + +const AreOrthogonalType* AreOrthogonalType::instance() +{ + static const AreOrthogonalType t; + return &t; +} + +ObjectImp* AreOrthogonalType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + const LineData& l1 = static_cast<const AbstractLineImp*>( parents[0] )->data(); + const LineData& l2 = static_cast<const AbstractLineImp*>( parents[1] )->data(); + + if ( l1.isOrthogonalTo( l2 ) ) + return new TestResultImp( i18n( "These lines are orthogonal." ) ); + else + return new TestResultImp( i18n( "These lines are not orthogonal." ) ); + +} + +const ObjectImpType* AreOrthogonalType::resultId() const +{ + return TestResultImp::stype(); +} + +static const ArgsParser::spec argsspecAreCollinear[] = +{ + { PointImp::stype(), I18N_NOOP( "Check collinearity of this point" ), + I18N_NOOP( "Select the first of the three possibly collinear points..." ), false }, + { PointImp::stype(), I18N_NOOP( "and this second point" ), + I18N_NOOP( "Select the second of the three possibly collinear points..." ), false }, + { PointImp::stype(), I18N_NOOP( "with this third point" ), + I18N_NOOP( "Select the last of the three possibly collinear points..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( AreCollinearType ) + +AreCollinearType::AreCollinearType() + : ArgsParserObjectType( "AreCollinear", + argsspecAreCollinear, 3 ) +{ +} + +AreCollinearType::~AreCollinearType() +{ +} + +const AreCollinearType* AreCollinearType::instance() +{ + static const AreCollinearType t; + return &t; +} + +ObjectImp* AreCollinearType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + const Coordinate& p1 = static_cast<const PointImp*>( parents[0] )->coordinate(); + const Coordinate& p2 = static_cast<const PointImp*>( parents[1] )->coordinate(); + const Coordinate& p3 = static_cast<const PointImp*>( parents[2] )->coordinate(); + + if ( areCollinear( p1, p2, p3 ) ) + return new TestResultImp( i18n( "These points are collinear." ) ); + else + return new TestResultImp( i18n( "These points are not collinear." ) ); +} + +const ObjectImpType* AreCollinearType::resultId() const +{ + return TestResultImp::stype(); +} + +static const ArgsParser::spec containsTestArgsSpec[] = +{ + { PointImp::stype(), I18N_NOOP( "Check whether this point is on a curve" ), + I18N_NOOP( "Select the point you want to test..." ), false }, + { CurveImp::stype(), I18N_NOOP( "Check whether the point is on this curve" ), + I18N_NOOP( "Select the curve that the point might be on..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ContainsTestType ) + +ContainsTestType::ContainsTestType() + : ArgsParserObjectType( "ContainsTest", containsTestArgsSpec, 2 ) +{ +} + +ContainsTestType::~ContainsTestType() +{ +} + +const ContainsTestType* ContainsTestType::instance() +{ + static const ContainsTestType t; + return &t; +} + +ObjectImp* ContainsTestType::calc( const Args& parents, const KigDocument& doc ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + const Coordinate& p = static_cast<const PointImp*>( parents[0] )->coordinate(); + const CurveImp* c = static_cast<const CurveImp*>( parents[1] ); + + if ( c->containsPoint( p, doc ) ) + return new TestResultImp( i18n( "This curve contains the point." ) ); + else + return new TestResultImp( i18n( "This curve does not contain the point." ) ); +} + +const ObjectImpType* ContainsTestType::resultId() const +{ + return TestResultImp::stype(); +} + +/* + * containment test of a point in a polygon + */ + +static const ArgsParser::spec InPolygonTestArgsSpec[] = +{ + { PointImp::stype(), I18N_NOOP( "Check whether this point is in a polygon" ), + I18N_NOOP( "Select the point you want to test..." ), false }, + { PolygonImp::stype(), I18N_NOOP( "Check whether the point is in this polygon" ), + I18N_NOOP( "Select the polygon that the point might be in..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( InPolygonTestType ) + +InPolygonTestType::InPolygonTestType() + : ArgsParserObjectType( "InPolygonTest", InPolygonTestArgsSpec, 2 ) +{ +} + +InPolygonTestType::~InPolygonTestType() +{ +} + +const InPolygonTestType* InPolygonTestType::instance() +{ + static const InPolygonTestType t; + return &t; +} + +ObjectImp* InPolygonTestType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + const Coordinate& p = static_cast<const PointImp*>( parents[0] )->coordinate(); + const PolygonImp* pol = static_cast<const PolygonImp*>( parents[1] ); + + if ( pol->isInPolygon( p ) ) + return new TestResultImp( i18n( "This polygon contains the point." ) ); + else + return new TestResultImp( i18n( "This polygon does not contain the point." ) ); +} + +const ObjectImpType* InPolygonTestType::resultId() const +{ + return TestResultImp::stype(); +} + +/* + * test if a polygon is convex + */ + +static const ArgsParser::spec ConvexPolygonTestArgsSpec[] = +{ + { PolygonImp::stype(), I18N_NOOP( "Check whether this polygon is convex" ), + I18N_NOOP( "Select the polygon you want to test for convexity..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConvexPolygonTestType ) + +ConvexPolygonTestType::ConvexPolygonTestType() + : ArgsParserObjectType( "ConvexPolygonTest", ConvexPolygonTestArgsSpec, 1 ) +{ +} + +ConvexPolygonTestType::~ConvexPolygonTestType() +{ +} + +const ConvexPolygonTestType* ConvexPolygonTestType::instance() +{ + static const ConvexPolygonTestType t; + return &t; +} + +ObjectImp* ConvexPolygonTestType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + const PolygonImp* pol = static_cast<const PolygonImp*>( parents[0] ); + + if ( pol->isConvex() ) + return new TestResultImp( i18n( "This polygon is convex." ) ); + else + return new TestResultImp( i18n( "This polygon is not convex." ) ); +} + +const ObjectImpType* ConvexPolygonTestType::resultId() const +{ + return TestResultImp::stype(); +} + +/* + * same distance test + */ + +static const ArgsParser::spec argsspecSameDistanceType[] = +{ + { PointImp::stype(), I18N_NOOP( "Check if this point has the same distance" ), + I18N_NOOP( "Select the point which might have the same distance from two other points..." ), false }, + { PointImp::stype(), I18N_NOOP( "from this point" ), + I18N_NOOP( "Select the first of the two other points..." ), false }, + { PointImp::stype(), I18N_NOOP( "and from this second point" ), + I18N_NOOP( "Select the other of the two other points..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( SameDistanceType ) + +SameDistanceType::SameDistanceType() + : ArgsParserObjectType( "SameDistanceType", argsspecSameDistanceType, 3 ) +{ +} + +SameDistanceType::~SameDistanceType() +{ +} + +const SameDistanceType* SameDistanceType::instance() +{ + static const SameDistanceType t; + return &t; +} + +ObjectImp* SameDistanceType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + const Coordinate& p1 = static_cast<const PointImp*>( parents[0] )->coordinate(); + const Coordinate& p2 = static_cast<const PointImp*>( parents[1] )->coordinate(); + const Coordinate& p3 = static_cast<const PointImp*>( parents[2] )->coordinate(); + + if ( fabs( ( p1 - p2 ).length() - ( p1 - p3 ).length() ) < 10e-5 ) + return new TestResultImp( i18n( "The two distances are the same." ) ); + else + return new TestResultImp( i18n( "The two distances are not the same." ) ); +} + +const ObjectImpType* SameDistanceType::resultId() const +{ + return TestResultImp::stype(); +} + +static const ArgsParser::spec vectorEqualityArgsSpec[] = +{ + { VectorImp::stype(), I18N_NOOP( "Check whether this vector is equal to another vector" ), + I18N_NOOP( "Select the first of the two possibly equal vectors..." ), false }, + { VectorImp::stype(), I18N_NOOP( "Check whether this vector is equal to the other vector" ), + I18N_NOOP( "Select the other of the two possibly equal vectors..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( VectorEqualityTestType ) + +VectorEqualityTestType::VectorEqualityTestType() + : ArgsParserObjectType( "VectorEquality", vectorEqualityArgsSpec, 2 ) +{ +} + +VectorEqualityTestType::~VectorEqualityTestType() +{ +} + +const VectorEqualityTestType* VectorEqualityTestType::instance() +{ + static const VectorEqualityTestType t; + return &t; +} + +ObjectImp* VectorEqualityTestType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + const Coordinate& v1 = static_cast<const VectorImp*>( parents[0] )->dir(); + const Coordinate& v2 = static_cast<const VectorImp*>( parents[1] )->dir(); + + if ( ( v1 - v2 ).length() < 10e-5 ) + return new TestResultImp( i18n( "The two vectors are the same." ) ); + else + return new TestResultImp( i18n( "The two vectors are not the same." ) ); +} + +const ObjectImpType* VectorEqualityTestType::resultId() const +{ + return TestResultImp::stype(); +} diff --git a/kig/objects/tests_type.h b/kig/objects/tests_type.h new file mode 100644 index 00000000..7498fc4f --- /dev/null +++ b/kig/objects/tests_type.h @@ -0,0 +1,111 @@ +// Copyright (C) 2004 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_TESTS_TYPE_H +#define KIG_OBJECTS_TESTS_TYPE_H + +#include "base_type.h" + +class AreParallelType + : public ArgsParserObjectType +{ + AreParallelType(); + ~AreParallelType(); +public: + static const AreParallelType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class AreOrthogonalType + : public ArgsParserObjectType +{ + AreOrthogonalType(); + ~AreOrthogonalType(); +public: + static const AreOrthogonalType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class AreCollinearType + : public ArgsParserObjectType +{ + AreCollinearType(); + ~AreCollinearType(); +public: + static const AreCollinearType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ContainsTestType + : public ArgsParserObjectType +{ + ContainsTestType(); + ~ContainsTestType(); +public: + static const ContainsTestType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class InPolygonTestType + : public ArgsParserObjectType +{ + InPolygonTestType(); + ~InPolygonTestType(); +public: + static const InPolygonTestType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ConvexPolygonTestType + : public ArgsParserObjectType +{ + ConvexPolygonTestType(); + ~ConvexPolygonTestType(); +public: + static const ConvexPolygonTestType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class SameDistanceType + : public ArgsParserObjectType +{ + SameDistanceType(); + ~SameDistanceType(); +public: + static const SameDistanceType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class VectorEqualityTestType + : public ArgsParserObjectType +{ + VectorEqualityTestType(); + ~VectorEqualityTestType(); +public: + static const VectorEqualityTestType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/text_imp.cc b/kig/objects/text_imp.cc new file mode 100644 index 00000000..f7b2f1be --- /dev/null +++ b/kig/objects/text_imp.cc @@ -0,0 +1,173 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "text_imp.h" + +#include "bogus_imp.h" +#include "../misc/kigpainter.h" + +TextImp::TextImp( const QString& text, const Coordinate& loc, bool frame ) + : mtext( text), mloc( loc ), mframe( frame ), mboundrect( Rect::invalidRect() ) +{ +} + +TextImp* TextImp::copy() const +{ + return new TextImp( mtext, mloc ); +} + +TextImp::~TextImp() +{ +} + +Coordinate TextImp::attachPoint() const +{ + return Coordinate::invalidCoord(); +} + +ObjectImp* TextImp::transform( const Transformation& t ) const +{ + Coordinate nloc = t.apply( mloc ); + return new TextImp( mtext, nloc, mframe ); +} + +void TextImp::draw( KigPainter& p ) const +{ + mboundrect = p.simpleBoundingRect( mloc, mtext ); + p.drawTextFrame( mboundrect, mtext, mframe ); +} + +bool TextImp::contains( const Coordinate& p, int, const KigWidget& ) const +{ + return mboundrect.contains( p ); +} + +bool TextImp::inRect( const Rect& r, int, const KigWidget& ) const +{ + return mboundrect.intersects( r ); +} + +bool TextImp::valid() const +{ + return true; +} + +const uint TextImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 1; +} + +const QCStringList TextImp::propertiesInternalNames() const +{ + QCStringList ret = Parent::propertiesInternalNames(); + ret << "kig_text"; + return ret; +} + +const QCStringList TextImp::properties() const +{ + QCStringList ret = Parent::properties(); + ret << I18N_NOOP( "Text" ); + return ret; +} + +const ObjectImpType* TextImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + return TextImp::stype(); +} + +const char* TextImp::iconForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + else if ( which == Parent::numberOfProperties() ) + return "text"; // text + else assert( false ); + return ""; +} + +ObjectImp* TextImp::property( uint which, const KigDocument& w ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, w ); + else if ( which == Parent::numberOfProperties() ) + return new StringImp( text() ); + else assert( false ); + return new InvalidImp; +} + +QString TextImp::text() const +{ + return mtext; +} + +void TextImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +const Coordinate TextImp::coordinate() const +{ + return mloc; +} + +bool TextImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( TextImp::stype() ) && + static_cast<const TextImp&>( rhs ).coordinate() == coordinate() && + static_cast<const TextImp&>( rhs ).text() == text() && + static_cast<const TextImp&>( rhs ).hasFrame() == hasFrame(); +} + +bool TextImp::hasFrame() const +{ + return mframe; +} + +const ObjectImpType* TextImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "label", + I18N_NOOP( "label" ), + I18N_NOOP( "Select this label" ), + I18N_NOOP( "Select label %1" ), + I18N_NOOP( "Remove a Label" ), + I18N_NOOP( "Add a Label" ), + I18N_NOOP( "Move a Label" ), + I18N_NOOP( "Attach to this label" ), + I18N_NOOP( "Show a Label" ), + I18N_NOOP( "Hide a Label" ) + ); + return &t; +} + +const ObjectImpType* TextImp::type() const +{ + return TextImp::stype(); +} + +bool TextImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + return Parent::isPropertyDefinedOnOrThroughThisImp( which ); +} + +Rect TextImp::surroundingRect() const +{ + return mboundrect; +} diff --git a/kig/objects/text_imp.h b/kig/objects/text_imp.h new file mode 100644 index 00000000..8ad92b84 --- /dev/null +++ b/kig/objects/text_imp.h @@ -0,0 +1,70 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_TEXT_IMP_H +#define KIG_OBJECTS_TEXT_IMP_H + +#include "object_imp.h" + +#include "../misc/coordinate.h" +#include "../misc/rect.h" + +class TextImp + : public ObjectImp +{ + QString mtext; + Coordinate mloc; + bool mframe; + // with this var, we keep track of the place we drew in, for use in + // the contains() function.. + mutable Rect mboundrect; +public: + typedef ObjectImp Parent; + static const ObjectImpType* stype(); + + Coordinate attachPoint() const; + TextImp( const QString& text, const Coordinate& loc, bool frame = false ); + TextImp* copy() const; + ~TextImp(); + + ObjectImp* transform( const Transformation& ) const; + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& ) const; + bool inRect( const Rect& r, int width, const KigWidget& ) const; + bool valid() const; + Rect surroundingRect() const; + + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& w ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + QString text() const; + const Coordinate coordinate() const; + bool hasFrame() const; + + bool equals( const ObjectImp& rhs ) const; +}; + +#endif diff --git a/kig/objects/text_type.cc b/kig/objects/text_type.cc new file mode 100644 index 00000000..6594c05a --- /dev/null +++ b/kig/objects/text_type.cc @@ -0,0 +1,215 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "text_type.h" + +#include "text_imp.h" +#include "bogus_imp.h" +#include "point_imp.h" +#include "line_imp.h" + +#include "../kig/kig_view.h" +#include "../kig/kig_part.h" +#include "../kig/kig_commands.h" +#include "../modes/label.h" +#include "../misc/coordinate_system.h" + +#include <algorithm> + +#include <qclipboard.h> +#include <qstringlist.h> + +#include <kapplication.h> + +#include <cmath> + +static const ArgsParser::spec arggspeccs[] = +{ + { IntImp::stype(), "UNUSED", "SHOULD NOT BE SEEN", false }, + { PointImp::stype(), "UNUSED", "SHOULD NOT BE SEEN", false }, + { StringImp::stype(), "UNUSED", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( TextType ) + +TextType::TextType() + : ObjectType( "Label" ), mparser( arggspeccs, 3 ) +{ +} + +TextType::~TextType() +{ +} + +const TextType* TextType::instance() +{ + static const TextType t; + return &t; +} + +const ObjectImpType* TextType::resultId() const +{ + return TextImp::stype(); +} + +const ObjectImpType* TextType::impRequirement( const ObjectImp* o, const Args& args ) const +{ + assert( args.size() >= 3 ); + Args firstthree( args.begin(), args.begin() + 3 ); + if ( o == args[0] || o == args[1] || o == args[2] ) + return mparser.impRequirement( o, firstthree ); + else + return ObjectImp::stype(); +} + +ObjectImp* TextType::calc( const Args& parents, const KigDocument& doc ) const +{ + if( parents.size() < 3 ) return new InvalidImp; + Args firstthree( parents.begin(), parents.begin() + 3 ); + Args varargs( parents.begin() + 3, parents.end() ); + + if ( ! mparser.checkArgs( firstthree ) ) return new InvalidImp; + + int frame = static_cast<const IntImp*>( firstthree[0] )->data(); + bool needframe = frame != 0; + const Coordinate t = static_cast<const PointImp*>( firstthree[1] )->coordinate(); + QString s = static_cast<const StringImp*>( firstthree[2] )->data(); + + for ( Args::iterator i = varargs.begin(); i != varargs.end(); ++i ) + (*i)->fillInNextEscape( s, doc ); + + return new TextImp( s, t, needframe ); +} + +bool TextType::canMove( const ObjectTypeCalcer& ) const +{ + return true; +} + +bool TextType::isFreelyTranslatable( const ObjectTypeCalcer& ) const +{ + return true; +} + +void TextType::move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& d ) const +{ + const std::vector<ObjectCalcer*> parents = ourobj.parents(); + assert( parents.size() >= 3 ); + const std::vector<ObjectCalcer*> firstthree( parents.begin(), parents.begin() + 3 ); + if( dynamic_cast<ObjectConstCalcer*>( firstthree[1] ) ) + { + ObjectConstCalcer* c = static_cast<ObjectConstCalcer*>( firstthree[1] ); + c->setImp( new PointImp( to ) ); + } + else + firstthree[1]->move( to, d ); +} + +QStringList TextType::specialActions() const +{ + QStringList ret; + ret << i18n( "&Copy Text" ); + ret << i18n( "&Toggle Frame" ); + ret << i18n( "&Redefine..." ); + return ret; +} + +void TextType::executeAction( int i, ObjectHolder& o, ObjectTypeCalcer& c, + KigPart& doc, KigWidget&, + NormalMode& ) const +{ + std::vector<ObjectCalcer*> parents = c.parents(); + assert( parents.size() >= 3 ); + + std::vector<ObjectCalcer*> firstthree( parents.begin(), parents.begin() + 3 ); + + assert( mparser.checkArgs( firstthree ) ); + assert( dynamic_cast<ObjectConstCalcer*>( firstthree[0] ) ); + assert( dynamic_cast<ObjectConstCalcer*>( firstthree[2] ) ); + + if ( i == 0 ) + { + QClipboard* cb = kapp->clipboard(); + + // copy the text into the clipboard + const TextImp* ti = static_cast<const TextImp*>( c.imp() ); + cb->setText( ti->text(), QClipboard::Clipboard ); + } + else if ( i == 1 ) + { + // toggle label frame + int n = (static_cast<const IntImp*>( firstthree[0]->imp() )->data() + 1) % 2; + KigCommand* kc = new KigCommand( doc, i18n( "Toggle Label Frame" ) ); + kc->addTask( new ChangeObjectConstCalcerTask( + static_cast<ObjectConstCalcer*>( firstthree[0] ), + new IntImp( n ) ) ); + doc.history()->addCommand( kc ); + } + else if ( i == 2 ) + { + assert( dynamic_cast<ObjectTypeCalcer*>( o.calcer() ) ); + // redefine.. + TextLabelRedefineMode m( doc, static_cast<ObjectTypeCalcer*>( o.calcer() ) ); + doc.runMode( &m ); + } + else assert( false ); +} + +const ArgsParser& TextType::argParser() const +{ + return mparser; +} + +const Coordinate TextType::moveReferencePoint( const ObjectTypeCalcer& ourobj ) const +{ + assert( ourobj.imp()->inherits( TextImp::stype() ) ); + return static_cast<const TextImp*>( ourobj.imp() )->coordinate(); +} + +std::vector<ObjectCalcer*> TextType::sortArgs( const std::vector<ObjectCalcer*>& os ) const +{ + assert( os.size() >= 3 ); + std::vector<ObjectCalcer*> ret( os.begin(), os.begin() + 3 ); + ret = mparser.parse( ret ); + std::copy( os.begin() + 3, os.end(), std::back_inserter( ret ) ); + return ret; +} + +Args TextType::sortArgs( const Args& args ) const +{ + assert( args.size() >= 3 ); + Args ret( args.begin(), args.begin() + 3 ); + ret = mparser.parse( ret ); + std::copy( args.begin() + 3, args.end(), std::back_inserter( ret ) ); + return ret; +} + +std::vector<ObjectCalcer*> TextType::movableParents( const ObjectTypeCalcer& ourobj ) const +{ + const std::vector<ObjectCalcer*> parents = ourobj.parents(); + assert( parents.size() >= 3 ); + std::vector<ObjectCalcer*> ret = parents[1]->movableParents(); + ret.push_back( parents[1] ); + return ret; +} + +bool TextType::isDefinedOnOrThrough( const ObjectImp*, const Args& ) const +{ + return false; +} + diff --git a/kig/objects/text_type.h b/kig/objects/text_type.h new file mode 100644 index 00000000..6368cafa --- /dev/null +++ b/kig/objects/text_type.h @@ -0,0 +1,55 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_TEXT_TYPE_H +#define KIG_OBJECTS_TEXT_TYPE_H + +#include "object_type.h" + +class TextType + : public ObjectType +{ + const ArgsParser mparser; + TextType(); + ~TextType(); +public: + static const TextType* instance(); + + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + bool isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const; + const ObjectImpType* resultId() const; + + ObjectImp* calc( const Args& parents, const KigDocument& d ) const; + + std::vector<ObjectCalcer*> sortArgs( const std::vector<ObjectCalcer*>& os ) const; + Args sortArgs( const Args& args ) const; + + bool canMove( const ObjectTypeCalcer& ourobj ) const; + bool isFreelyTranslatable( const ObjectTypeCalcer& ourobj ) const; + std::vector<ObjectCalcer*> movableParents( const ObjectTypeCalcer& ourobj ) const; + const Coordinate moveReferencePoint( const ObjectTypeCalcer& ourobj ) const; + void move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& ) const; + + QStringList specialActions() const; + void executeAction( int i, ObjectHolder& o, ObjectTypeCalcer& c, + KigPart& d, KigWidget& w, NormalMode& m ) const; + + const ArgsParser& argParser() const; +}; + +#endif diff --git a/kig/objects/transform_types.cc b/kig/objects/transform_types.cc new file mode 100644 index 00000000..3a8d32db --- /dev/null +++ b/kig/objects/transform_types.cc @@ -0,0 +1,874 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "transform_types.h" + +#include "bogus_imp.h" +#include "point_imp.h" +#include "line_imp.h" +#include "other_imp.h" +#include "polygon_imp.h" +#include "../misc/coordinate.h" +#include "../misc/kigtransform.h" + +#include <cmath> + +static const ArgsParser::spec argsspecTranslation[] = +{ + { ObjectImp::stype(), I18N_NOOP("Translate this object"), + I18N_NOOP( "Select the object to translate..." ), false }, + { VectorImp::stype(), I18N_NOOP("Translate by this vector"), + I18N_NOOP( "Select the vector to translate by..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( TranslatedType ) + +TranslatedType::TranslatedType() + : ArgsParserObjectType( "Translation", argsspecTranslation, 2 ) +{ +} + +TranslatedType::~TranslatedType() +{ +} + +const TranslatedType* TranslatedType::instance() +{ + static const TranslatedType t; + return &t; +} + +ObjectImp* TranslatedType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + Coordinate dir = static_cast<const VectorImp*>( args[1] )->dir(); + Transformation t = Transformation::translation( dir ); + + return args[0]->transform( t ); +} + +static const ArgsParser::spec argsspecPointReflection[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Reflect this object" ), + I18N_NOOP( "Select the object to reflect..." ), false }, + { PointImp::stype(), I18N_NOOP( "Reflect in this point" ), + I18N_NOOP( "Select the point to reflect in..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( PointReflectionType ) + +PointReflectionType::PointReflectionType() + : ArgsParserObjectType( "PointReflection", argsspecPointReflection, 2 ) +{ +} + +PointReflectionType::~PointReflectionType() +{ +} + +const PointReflectionType* PointReflectionType::instance() +{ + static const PointReflectionType t; + return &t; +} + +ObjectImp* PointReflectionType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + Coordinate center = static_cast<const PointImp*>( args[1] )->coordinate(); + Transformation t = Transformation::pointReflection( center ); + + return args[0]->transform( t ); +} + +static const ArgsParser::spec argsspecLineReflection[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Reflect this object" ), + I18N_NOOP( "Select the object to reflect..." ), false }, + { AbstractLineImp::stype(), I18N_NOOP( "Reflect in this line" ), + I18N_NOOP( "Select the line to reflect in..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( LineReflectionType ) + +LineReflectionType::LineReflectionType() + : ArgsParserObjectType( "LineReflection", argsspecLineReflection, 2 ) +{ +} + +LineReflectionType::~LineReflectionType() +{ +} + +const LineReflectionType* LineReflectionType::instance() +{ + static const LineReflectionType t; + return &t; +} + +ObjectImp* LineReflectionType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + LineData d = static_cast<const AbstractLineImp*>( args[1] )->data(); + Transformation t = Transformation::lineReflection( d ); + + return args[0]->transform( t ); +} + +static const ArgsParser::spec argsspecRotation[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Rotate this object" ), + I18N_NOOP( "Select the object to rotate..." ), false }, + { PointImp::stype(), I18N_NOOP( "Rotate around this point" ), + I18N_NOOP( "Select the center point of the rotation..." ), false }, + { AngleImp::stype(), I18N_NOOP( "Rotate by this angle" ), + I18N_NOOP( "Select the angle of the rotation..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( RotationType ) + +RotationType::RotationType() + : ArgsParserObjectType( "Rotation", argsspecRotation, 3 ) +{ +} + +RotationType::~RotationType() +{ +} + +const RotationType* RotationType::instance() +{ + static const RotationType t; + return &t; +} + +ObjectImp* RotationType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + Coordinate center = static_cast<const PointImp*>( args[1] )->coordinate(); + double angle = static_cast<const AngleImp*>( args[2] )->size(); + + return args[0]->transform( Transformation::rotation( angle, center ) ); +} + +static const ArgsParser::spec argsspecScalingOverCenter[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Scale this object" ), + I18N_NOOP( "Select the object to scale..." ), false }, + { PointImp::stype(), I18N_NOOP( "Scale with this center" ), + I18N_NOOP( "Select the center point of the scaling..." ), false }, + { SegmentImp::stype(), I18N_NOOP( "Scale by the length of this segment" ), + I18N_NOOP( "Select a segment whose length is the factor of the scaling..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ScalingOverCenterType ) + +ScalingOverCenterType::ScalingOverCenterType() + : ArgsParserObjectType( "ScalingOverCenter", argsspecScalingOverCenter, 3 ) +{ +} + +ScalingOverCenterType::~ScalingOverCenterType() +{ +} + +const ScalingOverCenterType* ScalingOverCenterType::instance() +{ + static const ScalingOverCenterType t; + return &t; +} + +ObjectImp* ScalingOverCenterType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + Coordinate center = static_cast<const PointImp*>( args[1] )->coordinate(); + double ratio = static_cast<const SegmentImp*>( args[2] )->length(); + + return args[0]->transform( Transformation::scalingOverPoint( ratio, center ) ); +} + +static const ArgsParser::spec argsspecScalingOverCenter2[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Scale this object" ), + I18N_NOOP( "Select the object to scale..." ), false }, + { PointImp::stype(), I18N_NOOP( "Scale with this center" ), + I18N_NOOP( "Select the center point of the scaling..." ), false }, + { SegmentImp::stype(), I18N_NOOP( "Scale the length of this segment..." ), + I18N_NOOP( "Select the first of two segments whose ratio is the factor of the scaling..." ), false }, + { SegmentImp::stype(), I18N_NOOP( "...to the length of this other segment" ), + I18N_NOOP( "Select the second of two segments whose ratio is the factor of the scaling..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ScalingOverCenter2Type ) + +ScalingOverCenter2Type::ScalingOverCenter2Type() + : ArgsParserObjectType( "ScalingOverCenter2", argsspecScalingOverCenter2, 4 ) +{ +} + +ScalingOverCenter2Type::~ScalingOverCenter2Type() +{ +} + +const ScalingOverCenter2Type* ScalingOverCenter2Type::instance() +{ + static const ScalingOverCenter2Type t; + return &t; +} + +ObjectImp* ScalingOverCenter2Type::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + Coordinate center = static_cast<const PointImp*>( args[1] )->coordinate(); + double ratio = static_cast<const SegmentImp*>( args[3] )->length()/ + static_cast<const SegmentImp*>( args[2] )->length(); + + return args[0]->transform( Transformation::scalingOverPoint( ratio, center ) ); +} + +static const ArgsParser::spec argsspecScalingOverLine[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Scale this object" ), I18N_NOOP( "Select the object to scale" ), false }, + { AbstractLineImp::stype(), I18N_NOOP( "Scale over this line" ), I18N_NOOP( "Select the line to scale over" ), false }, + { SegmentImp::stype(), I18N_NOOP( "Scale by the length of this segment" ), I18N_NOOP( "Select a segment whose length is the factor for the scaling" ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ScalingOverLineType ) + +ScalingOverLineType::ScalingOverLineType() + : ArgsParserObjectType( "ScalingOverLine", argsspecScalingOverLine, 3 ) +{ +} + +ScalingOverLineType::~ScalingOverLineType() +{ +} + +const ScalingOverLineType* ScalingOverLineType::instance() +{ + static const ScalingOverLineType t; + return &t; +} + +ObjectImp* ScalingOverLineType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + LineData line = static_cast<const AbstractLineImp*>( args[1] )->data(); + double ratio = static_cast<const SegmentImp*>( args[2] )->length(); + + return args[0]->transform( Transformation::scalingOverLine( ratio, line ) ); +} + +static const ArgsParser::spec argsspecScalingOverLine2[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Scale this object" ), I18N_NOOP( "Select the object to scale" ), false }, + { AbstractLineImp::stype(), I18N_NOOP( "Scale over this line" ), I18N_NOOP( "Select the line to scale over" ), false }, + { SegmentImp::stype(), I18N_NOOP( "Scale the length of this segment..." ), I18N_NOOP( "Select the first of two segments whose ratio is the factor for the scaling" ), false }, + { SegmentImp::stype(), I18N_NOOP( "...to the length of this segment" ), I18N_NOOP( "Select the second of two segments whose ratio is the factor for the scaling" ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ScalingOverLine2Type ) + +ScalingOverLine2Type::ScalingOverLine2Type() + : ArgsParserObjectType( "ScalingOverLine2", argsspecScalingOverLine2, 4 ) +{ +} + +ScalingOverLine2Type::~ScalingOverLine2Type() +{ +} + +const ScalingOverLine2Type* ScalingOverLine2Type::instance() +{ + static const ScalingOverLine2Type t; + return &t; +} + +ObjectImp* ScalingOverLine2Type::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + LineData line = static_cast<const AbstractLineImp*>( args[1] )->data(); + double ratio = static_cast<const SegmentImp*>( args[3] )->length()/ + static_cast<const SegmentImp*>( args[2] )->length(); + + return args[0]->transform( Transformation::scalingOverLine( ratio, line ) ); +} + +static const ArgsParser::spec argsspecProjectiveRotation[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Projectively rotate this object" ), I18N_NOOP( "Select the object to rotate projectively" ), false }, + { RayImp::stype(), I18N_NOOP( "Projectively rotate with this half-line" ), I18N_NOOP( "Select the half line of the projective rotation that you want to apply to the object" ), false }, + { AngleImp::stype(), I18N_NOOP( "Projectively rotate by this angle" ), I18N_NOOP( "Select the angle of the projective rotation that you want to apply to the object" ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ProjectiveRotationType ) + +ProjectiveRotationType::ProjectiveRotationType() + : ArgsParserObjectType( "ProjectiveRotation", argsspecProjectiveRotation, 3 ) +{ +} + +ProjectiveRotationType::~ProjectiveRotationType() +{ +} + +const ProjectiveRotationType* ProjectiveRotationType::instance() +{ + static const ProjectiveRotationType t; + return &t; +} + +ObjectImp* ProjectiveRotationType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + const RayImp* ray = static_cast<const RayImp*>( args[1] ); + Coordinate c1 = ray->data().a; + Coordinate dir = ray->data().dir().normalize(); + double alpha = static_cast<const AngleImp*>( args[2] )->size(); + + return args[0]->transform( + Transformation::projectiveRotation( alpha, dir, c1 ) ); +} + +static const ArgsParser::spec argsspecHarmonicHomology[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Harmonic Homology of this object" ), + I18N_NOOP( "Select the object to transform..." ), false }, + { PointImp::stype(), I18N_NOOP( "Harmonic Homology with this center" ), + I18N_NOOP( "Select the center point of the harmonic homology..." ), false }, + { AbstractLineImp::stype(), I18N_NOOP( "Harmonic Homology with this axis" ), + I18N_NOOP( "Select the axis of the harmonic homology..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( HarmonicHomologyType ) + +HarmonicHomologyType::HarmonicHomologyType() + : ArgsParserObjectType( "HarmonicHomology", argsspecHarmonicHomology, 3 ) +{ +} + +HarmonicHomologyType::~HarmonicHomologyType() +{ +} + +const HarmonicHomologyType* HarmonicHomologyType::instance() +{ + static const HarmonicHomologyType t; + return &t; +} + +ObjectImp* HarmonicHomologyType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + Coordinate center = static_cast<const PointImp*>( args[1] )->coordinate(); + LineData axis = static_cast<const AbstractLineImp*>( args[2] )->data(); + return args[0]->transform( + Transformation::harmonicHomology( center, axis ) ); +} + +static const ArgsParser::spec argsspecAffinityB2Tr[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Generic affinity of this object" ), + I18N_NOOP( "Select the object to transform..." ), false }, + { PolygonImp::stype3(), I18N_NOOP( "Map this triangle" ), + I18N_NOOP( "Select the triangle that has to be transformed onto a given triangle..." ), false }, + { PolygonImp::stype3(), I18N_NOOP( "onto this other triangle" ), + I18N_NOOP( "Select the triangle that is the image by the affinity of the first triangle..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( AffinityB2TrType ) + +AffinityB2TrType::AffinityB2TrType() + : ArgsParserObjectType( "AffinityB2Tr", argsspecAffinityB2Tr, 3 ) +{ +} + +AffinityB2TrType::~AffinityB2TrType() +{ +} + +const AffinityB2TrType* AffinityB2TrType::instance() +{ + static const AffinityB2TrType t; + return &t; +} + +ObjectImp* AffinityB2TrType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + std::vector<Coordinate> frompoints = static_cast<const PolygonImp*>( args[1] )->points(); + std::vector<Coordinate> topoints = static_cast<const PolygonImp*>( args[2] )->points(); + + bool valid = true; + Transformation t = Transformation::affinityGI3P( frompoints, topoints, + valid ); + + if (valid == false) return new InvalidImp; + return args[0]->transform( t ); +} + +static const ArgsParser::spec argsspecAffinityGI3P[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Generic affinity of this object" ), + I18N_NOOP( "Select the object to transform..." ), false }, + { PointImp::stype(), I18N_NOOP( "First of 3 starting points" ), + I18N_NOOP( "Select the first of the three starting points of the generic affinity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Second of 3 starting points" ), + I18N_NOOP( "Select the second of the three starting points of the generic affinity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Third of 3 starting points" ), + I18N_NOOP( "Select the third of the three starting points of the generic affinity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Transformed position of first point" ), + I18N_NOOP( "Select the first of the three end points of the generic affinity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Transformed position of second point" ), + I18N_NOOP( "Select the second of the three end points of the generic affinity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Transformed position of third point" ), + I18N_NOOP( "Select the third of the three end points of the generic affinity..." ), false }, +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( AffinityGI3PType ) + +AffinityGI3PType::AffinityGI3PType() + : ArgsParserObjectType( "AffinityGI3P", argsspecAffinityGI3P, 7 ) +{ +} + +AffinityGI3PType::~AffinityGI3PType() +{ +} + +const AffinityGI3PType* AffinityGI3PType::instance() +{ + static const AffinityGI3PType t; + return &t; +} + +ObjectImp* AffinityGI3PType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + std::vector<Coordinate> frompoints; + std::vector<Coordinate> topoints; + for ( uint i = 0; i < 3; ++i ) + { + frompoints.push_back( + static_cast<const PointImp*>( args[i+1] )->coordinate() ); + topoints.push_back( + static_cast<const PointImp*>( args[i+4] )->coordinate() ); + } + + bool valid = true; + Transformation t = Transformation::affinityGI3P( frompoints, topoints, + valid ); + + if (valid == false) return new InvalidImp; + return args[0]->transform( t ); +} + +static const ArgsParser::spec argsspecProjectivityB2Qu[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Generic projective transformation of this object" ), + I18N_NOOP( "Select the object to transform..." ), false }, + { PolygonImp::stype4(), I18N_NOOP( "Map this quadrilateral" ), + I18N_NOOP( "Select the quadrilateral that has to be transformed onto a given quadrilateral..." ), false }, + { PolygonImp::stype4(), I18N_NOOP( "onto this other quadrilateral" ), + I18N_NOOP( "Select the quadrilateral that is the image by the projective transformation of the first quadrilateral..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ProjectivityB2QuType ) + +ProjectivityB2QuType::ProjectivityB2QuType() + : ArgsParserObjectType( "ProjectivityB2Qu", argsspecProjectivityB2Qu, 3 ) +{ +} + +ProjectivityB2QuType::~ProjectivityB2QuType() +{ +} + +const ProjectivityB2QuType* ProjectivityB2QuType::instance() +{ + static const ProjectivityB2QuType t; + return &t; +} + +ObjectImp* ProjectivityB2QuType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + std::vector<Coordinate> frompoints = static_cast<const PolygonImp*>( args[1] )->points(); + std::vector<Coordinate> topoints = static_cast<const PolygonImp*>( args[2] )->points(); + + bool valid = true; + Transformation t = Transformation::projectivityGI4P( frompoints, topoints, + valid ); + + if (valid == false) return new InvalidImp; + return args[0]->transform( t ); +} + +static const ArgsParser::spec argsspecProjectivityGI4P[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Generic projective transformation of this object" ), + I18N_NOOP( "Select the object to transform..." ), false }, + { PointImp::stype(), I18N_NOOP( "First of 4 starting points" ), + I18N_NOOP( "Select the first of the four starting points of the generic projectivity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Second of 4 starting points" ), + I18N_NOOP( "Select the second of the four starting points of the generic projectivity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Third of 4 starting points" ), + I18N_NOOP( "Select the third of the four starting points of the generic projectivity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Fourth of 4 starting points" ), + I18N_NOOP( "Select the fourth of the four starting points of the generic projectivity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Transformed position of first point" ), + I18N_NOOP( "Select the first of the four end points of the generic projectivity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Transformed position of second point" ), + I18N_NOOP( "Select the second of the four end points of the generic projectivity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Transformed position of third point" ), + I18N_NOOP( "Select the third of the four end points of the generic projectivity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Transformed position of fourth point" ), + I18N_NOOP( "Select the fourth of the four end points of the generic projectivity..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ProjectivityGI4PType ) + +ProjectivityGI4PType::ProjectivityGI4PType() + : ArgsParserObjectType( "ProjectivityGI4P", argsspecProjectivityGI4P, 9 ) +{ +} + +ProjectivityGI4PType::~ProjectivityGI4PType() +{ +} + +const ProjectivityGI4PType* ProjectivityGI4PType::instance() +{ + static const ProjectivityGI4PType t; + return &t; +} + +ObjectImp* ProjectivityGI4PType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + std::vector<Coordinate> frompoints; + std::vector<Coordinate> topoints; + for ( uint i = 0; i < 4; ++i ) + { + frompoints.push_back( + static_cast<const PointImp*>( args[i+1] )->coordinate() ); + topoints.push_back( + static_cast<const PointImp*>( args[i+5] )->coordinate() ); + } + + bool valid = true; + Transformation t = Transformation::projectivityGI4P( frompoints, topoints, + valid ); + + if (valid == false) return new InvalidImp; + return args[0]->transform( t ); +} + +static const ArgsParser::spec argsspecCastShadow[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Cast the shadow of this object" ), + I18N_NOOP( "Select the object of which you want to construct the shadow..." ), false }, + { PointImp::stype(), I18N_NOOP( "Cast a shadow from this light source" ), + I18N_NOOP( "Select the light source from which the shadow should originate..." ), false }, + { AbstractLineImp::stype(), + I18N_NOOP( "Cast a shadow on the horizon represented by this line" ), + I18N_NOOP( "Select the horizon for the shadow..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CastShadowType ) + +CastShadowType::CastShadowType() + : ArgsParserObjectType( "CastShadow", argsspecCastShadow, 3 ) +{ +} + +CastShadowType::~CastShadowType() +{ +} + +const CastShadowType* CastShadowType::instance() +{ + static const CastShadowType t; + return &t; +} + +ObjectImp* CastShadowType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + Coordinate lightsrc = static_cast<const PointImp*>( args[1] )->coordinate(); + LineData d = static_cast<const AbstractLineImp*>( args[2] )->data(); + return args[0]->transform( + Transformation::castShadow( lightsrc, d ) ); +} + +const ObjectImpType* TranslatedType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* PointReflectionType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* LineReflectionType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* RotationType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* ScalingOverCenterType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* ScalingOverCenter2Type::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* ScalingOverLineType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* ScalingOverLine2Type::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* ProjectiveRotationType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* HarmonicHomologyType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* AffinityB2TrType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* AffinityGI3PType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* ProjectivityB2QuType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* ProjectivityGI4PType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* CastShadowType::resultId() const +{ + return ObjectImp::stype(); +} + +bool TranslatedType::isTransform() const +{ + return true; +} + +bool PointReflectionType::isTransform() const +{ + return true; +} + +bool LineReflectionType::isTransform() const +{ + return true; +} + +bool RotationType::isTransform() const +{ + return true; +} + +bool ScalingOverCenterType::isTransform() const +{ + return true; +} + +bool ScalingOverCenter2Type::isTransform() const +{ + return true; +} + +bool ScalingOverLineType::isTransform() const +{ + return true; +} + +bool ScalingOverLine2Type::isTransform() const +{ + return true; +} + +bool ProjectiveRotationType::isTransform() const +{ + return true; +} + +bool HarmonicHomologyType::isTransform() const +{ + return true; +} + +bool AffinityB2TrType::isTransform() const +{ + return true; +} + +bool AffinityGI3PType::isTransform() const +{ + return true; +} + +bool ProjectivityB2QuType::isTransform() const +{ + return true; +} + +bool ProjectivityGI4PType::isTransform() const +{ + return true; +} + +bool CastShadowType::isTransform() const +{ + return true; +} + +static const ArgsParser::spec argsspecApplyTransformation[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Transform this object" ), "SHOULD NOT BE SEEN", false }, + { TransformationImp::stype(), I18N_NOOP( "Transform using this transformation" ), "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ApplyTransformationObjectType ) + +ApplyTransformationObjectType::ApplyTransformationObjectType() + : ArgsParserObjectType( "ApplyTransformation", argsspecApplyTransformation, 2 ) +{ +} + +ApplyTransformationObjectType::~ApplyTransformationObjectType() +{ +} + +const ApplyTransformationObjectType* ApplyTransformationObjectType::instance() +{ + static const ApplyTransformationObjectType t; + return &t; +} + +ObjectImp* ApplyTransformationObjectType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + return args[0]->transform( static_cast<const TransformationImp*>( args[1] )->data() ); +} + +const ObjectImpType* ApplyTransformationObjectType::resultId() const +{ + return ObjectImp::stype(); +} + +bool ApplyTransformationObjectType::isTransform() const +{ + return true; +} + +bool SimilitudeType::isTransform() const +{ + return true; +} + +const ObjectImpType* SimilitudeType::resultId() const +{ + return ObjectImp::stype(); +} + +const SimilitudeType* SimilitudeType::instance() +{ + static const SimilitudeType t; + return &t; +} + +ObjectImp* SimilitudeType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + Coordinate c = static_cast<const PointImp*>( args[1] )->coordinate(); + Coordinate a = static_cast<const PointImp*>( args[2] )->coordinate(); + Coordinate b = static_cast<const PointImp*>( args[3] )->coordinate(); + a -= c; + b -= c; + double factor = sqrt( b.squareLength()/a.squareLength() ); + double theta = atan2( b.y, b.x ) - atan2( a.y, a.x ); + + return args[0]->transform( Transformation::similitude( c, theta, factor ) ); +} + +SimilitudeType::~SimilitudeType() +{ +} + +static const ArgsParser::spec argsspecSimilitude[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Apply a similitude to this object" ), + I18N_NOOP( "Select the object to transform..." ), false }, + { PointImp::stype(), I18N_NOOP( "Apply a similitude with this center" ), + I18N_NOOP( "Select the center for the similitude..." ), false }, + { PointImp::stype(), I18N_NOOP( "Apply a similitude mapping this point onto another point" ), + I18N_NOOP( "Select the point which the similitude should map onto another point..." ), false }, + { PointImp::stype(), I18N_NOOP( "Apply a similitude mapping a point onto this point" ), + I18N_NOOP( "Select the point onto which the similitude should map the first point..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( SimilitudeType ) + +SimilitudeType::SimilitudeType() + : ArgsParserObjectType( "Similitude", argsspecSimilitude, 4 ) +{ +} diff --git a/kig/objects/transform_types.h b/kig/objects/transform_types.h new file mode 100644 index 00000000..038be068 --- /dev/null +++ b/kig/objects/transform_types.h @@ -0,0 +1,243 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_TRANSFORM_TYPES_H +#define KIG_OBJECTS_TRANSFORM_TYPES_H + +#include "object_type.h" + +class TranslatedType + : public ArgsParserObjectType +{ + TranslatedType(); + ~TranslatedType(); +public: + static const TranslatedType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class PointReflectionType + : public ArgsParserObjectType +{ + PointReflectionType(); + ~PointReflectionType(); +public: + static const PointReflectionType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class LineReflectionType + : public ArgsParserObjectType +{ + LineReflectionType(); + ~LineReflectionType(); +public: + static const LineReflectionType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class RotationType + : public ArgsParserObjectType +{ + RotationType(); + ~RotationType(); +public: + static const RotationType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class ScalingOverCenterType + : public ArgsParserObjectType +{ + ScalingOverCenterType(); + ~ScalingOverCenterType(); +public: + static const ScalingOverCenterType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class ScalingOverCenter2Type + : public ArgsParserObjectType +{ + ScalingOverCenter2Type(); + ~ScalingOverCenter2Type(); +public: + static const ScalingOverCenter2Type* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class ScalingOverLineType + : public ArgsParserObjectType +{ + ScalingOverLineType(); + ~ScalingOverLineType(); +public: + static const ScalingOverLineType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class ScalingOverLine2Type + : public ArgsParserObjectType +{ + ScalingOverLine2Type(); + ~ScalingOverLine2Type(); +public: + static const ScalingOverLine2Type* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class ProjectiveRotationType + : public ArgsParserObjectType +{ + ProjectiveRotationType(); + ~ProjectiveRotationType(); +public: + static const ProjectiveRotationType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class HarmonicHomologyType + : public ArgsParserObjectType +{ + HarmonicHomologyType(); + ~HarmonicHomologyType(); +public: + static const HarmonicHomologyType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class AffinityB2TrType + : public ArgsParserObjectType +{ + AffinityB2TrType(); + ~AffinityB2TrType(); +public: + static const AffinityB2TrType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class AffinityGI3PType + : public ArgsParserObjectType +{ + AffinityGI3PType(); + ~AffinityGI3PType(); +public: + static const AffinityGI3PType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class ProjectivityB2QuType + : public ArgsParserObjectType +{ + ProjectivityB2QuType(); + ~ProjectivityB2QuType(); +public: + static const ProjectivityB2QuType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class ProjectivityGI4PType + : public ArgsParserObjectType +{ + ProjectivityGI4PType(); + ~ProjectivityGI4PType(); +public: + static const ProjectivityGI4PType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class CastShadowType + : public ArgsParserObjectType +{ + CastShadowType(); + ~CastShadowType(); +public: + static const CastShadowType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class ApplyTransformationObjectType + : public ArgsParserObjectType +{ + ApplyTransformationObjectType(); + ~ApplyTransformationObjectType(); +public: + static const ApplyTransformationObjectType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + bool isTransform() const; +}; + +class SimilitudeType + : public ArgsParserObjectType +{ + SimilitudeType(); + ~SimilitudeType(); +public: + static const SimilitudeType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +#endif diff --git a/kig/objects/vector_type.cc b/kig/objects/vector_type.cc new file mode 100644 index 00000000..d96be07b --- /dev/null +++ b/kig/objects/vector_type.cc @@ -0,0 +1,100 @@ +// Copyright (C) 2004 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "vector_type.h" + +#include "point_imp.h" +#include "other_imp.h" +#include "bogus_imp.h" + +static const ArgsParser::spec argsspecVector[] = +{ + { PointImp::stype(), I18N_NOOP( "Construct a vector from this point" ), + I18N_NOOP( "Select the start point of the new vector..." ), true }, + { PointImp::stype(), I18N_NOOP( "Construct a vector to this point" ), + I18N_NOOP( "Select the end point of the new vector..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( VectorType ) + +VectorType::VectorType() + : ObjectABType( "Vector", argsspecVector, 2 ) +{ +} + +VectorType::~VectorType() +{ +} + +const VectorType* VectorType::instance() +{ + static const VectorType t; + return &t; +} + +ObjectImp* VectorType::calc( const Coordinate& a, const Coordinate& b ) const +{ + return new VectorImp( a, b ); +} + +const ObjectImpType* VectorType::resultId() const +{ + return VectorImp::stype(); +} + +static const ArgsParser::spec argsspecVectorSum[] = +{ + { VectorImp::stype(), I18N_NOOP( "Construct the vector sum of this vector and another one." ), + I18N_NOOP( "Select the first of the two vectors of which you want to construct the sum..." ), false }, + { VectorImp::stype(), I18N_NOOP( "Construct the vector sum of this vector and the other one." ), + I18N_NOOP( "Select the other of the two vectors of which you want to construct the sum..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct the vector sum starting at this point." ), + I18N_NOOP( "Select the point to construct the sum vector in..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( VectorSumType ) + +VectorSumType::VectorSumType() + : ArgsParserObjectType( "VectorSum", argsspecVectorSum, 3 ) +{ +} + +VectorSumType::~VectorSumType() +{ +} + +const VectorSumType* VectorSumType::instance() +{ + static const VectorSumType t; + return &t; +} + +ObjectImp* VectorSumType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + const VectorImp& a = *static_cast<const VectorImp*>( args[0] ); + const VectorImp& b = *static_cast<const VectorImp*>( args[1] ); + const PointImp& p = *static_cast<const PointImp*>( args[2] ); + + return new VectorImp( p.coordinate(), p.coordinate() + a.dir() + b.dir() ); +} + +const ObjectImpType* VectorSumType::resultId() const +{ + return VectorImp::stype(); +} diff --git a/kig/objects/vector_type.h b/kig/objects/vector_type.h new file mode 100644 index 00000000..e1756ba5 --- /dev/null +++ b/kig/objects/vector_type.h @@ -0,0 +1,45 @@ +// Copyright (C) 2004 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_OBJECTS_VECTOR_TYPE_H +#define KIG_OBJECTS_VECTOR_TYPE_H + +#include "base_type.h" + +class VectorType + : public ObjectABType +{ + VectorType(); + ~VectorType(); +public: + static const VectorType* instance(); + ObjectImp* calc( const Coordinate& a, const Coordinate& b ) const; + const ObjectImpType* resultId() const; +}; + +class VectorSumType + : public ArgsParserObjectType +{ + VectorSumType(); + ~VectorSumType(); +public: + static const VectorSumType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/package-kig.sh.in b/kig/package-kig.sh.in new file mode 100644 index 00000000..7f3d609e --- /dev/null +++ b/kig/package-kig.sh.in @@ -0,0 +1,28 @@ +#! /bin/bash + +# this is mostly a log for myself for remembering how to build the kig +# packages. + +OLDPWD=$(pwd) + +VERSION="@KIGVERSION@" +NAME="kig" + +TEMPDIR="/tmp/$NAME-package-temp" +rm -rf $TEMPDIR +mkdir $TEMPDIR +cd $TEMPDIR + +~domi/src/kdesdk/scripts/cvs2dist \ + --name "$NAME" \ + --version "$VERSION" \ + --log="$TEMPDIR/log" \ + ~/src/package/kdeedu kig + +cd $OLDPWD + +echo " !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! " +echo "Don't forget to change #MIN_CONFIG in " +echo "configure.in.in to " +echo "#MIN_CONFIG( 3.0 ) on packaging, so kig " +echo "won't depend on Qt 3.1 but 3.0." diff --git a/kig/pykig/API.txt b/kig/pykig/API.txt new file mode 100644 index 00000000..e775ac76 --- /dev/null +++ b/kig/pykig/API.txt @@ -0,0 +1,182 @@ +Version 0.2.4 + +There are two way to use pykig.py: +- as a program: + $ pykig.py <nameprog>.kpy +- as a Python library within a Python program: + from pykig import * + +A ".kpy" file is a "python" script. + +A new object is created by callig a python "constructor"; +the result can be stored in a python variable for later +use. E.g.: + + a = Point(0.5, 1.5, name="A") + +to create a point with coordinates (0.5, 1.5), give it a +the name "A" (at kig level) and recall it in the python +variable a. See the examples for practical use. + +All constructors accept some optional parameters: + +shown = HIDDEN | VISIBLE default value: VISIBLE +name = string a name to refer to the object within + kig +internal = bool this object is internal and cannot be + made visible +width = integer the line width +pointstyle = "Round"|"RoundEmpty"|"Rectangular"|"RectangularEmpty"|"Cross" +linestyle = "SolidLine"|"DashLine"|"DashDotLine"|"DashDotDotLine"|"DotLine" +color = "#RRGGBB" where RR, GG, BB are three numbers + wich represent the red, green, blue + components + +The kigdocument is a global object to allow two methods for modify Kig look and +default behaviour: + +kigdocument.noaxes() +kigdocument.nogrid() +kigdocument.hideobjects() +kigdocument.showobjects() +kigdocument.setwidth() +kigdocument.setpointstyle() +kigdocument.setname() +kigdocument.setlinestyle() +kigdocument.setshown() +kigdocument.setcolor() +kigdocument.setinternal() + + +Generic methods for objects: + +obj.hide() +obj.show() hide/show given object +obj.setwidth(width) set obj width +obj.setpointstyle(self, pointstyle) set obj point style +obj.setlinestyle(self, linestyle) set obj line style +obj.setcolor(self, color) set obj color +obj.setname() set obj name +obj.type() return obj type + +Some objects have other methods: + +obj.coordinate() for points +obj.xcoord() +obj.ycoord() +obj.midpoint() for segments +obj.endpointA() +obj.endpointB() +obj.length() +obj.equation() +obj.slope() +obj.numofsides() for polygons +obj.perimeter() +obj.surface() +obj.centerofmass() +obj.windingnumber() +obj.center() for circles +obj.bisector() for angles +obj.support() + +==================================================================== + +Properties: + +Type(object) type of object +Coordinate(point) coordinate of point +XCoord(point) +YCoord(point) +MidPoints(a, b) midpoint of two points a and b +MidPoint(segment) midpoint of a segment +EndPointA(segment) +EndPointB(segment) +Length(segment) +Equation(segment) +Slope(segment) +NumOfSides(poly) +Perimeter(poly) +Surface(poly) +CenterOfMass(poly) +WindingNumber(poly) +Center(circle) +Bisector(angle) +Support(object) +==================================================================== + +Objects: + +Point(x, y) free (unconstrained) point +ConstrainedPoint(t, curve) constrained point on 'curve'; t + *must* be in [0,1]; for segments and + arcs the position of the point is + proportional to the value of t +Line(a, b) straight line through a and b +Ray(a, b) halfline starting in a through b +Segment(a, b) segment from a to b +Orthogonal(line, point) line through 'point' orthogonal to + 'line' + +Circle(center, point) +CircleByCenterRadius(center, radius) +CircleBy3Points(p1, p2, p3) +ArcBy3Points(p1, p2, p3) +ArcByCenterPointAngle(center, point, angle) + +ParabolaByDirectrixFocus(directrix, focus) +VerticalCubic(p1, p2, p3, p4) +ConicArc(p1, p2, p3, center) + +LineLineIntersection(line1, line2) produces a point +CircleCircleIntersection(c1, c2, which) c1 and c2 are two circles, 'which' + is an integer that can only be +1 + or -1 and tells which one of the + two intersections has to be created. + To have both you must call this + function twice. +ConicLineIntersection(conic, line, which) conic can also be a circle; which + has the same meaning as for the + CircleCircleIntersection + +Polygon((p1, p2,..., pn)) A polygon with the given vertices +PolygonBCV(center, vertex, n) A regular polygon with 'n' sides +PolygonVertex(polygon, i) Generate the i-th vertex of the + given polygon +PolygonSide(polygon, i) Generate the i-th side of the given + polygon + +Vector(p1, p2) +Angle(p1, center, p2) + +Text(point, string, boxed) point is a Point or a tuple of + two numbers + boxed is a integer in [0, 1] telling + if we want the frame +VarText(point, string, vars, boxed) point is a Point or a tuple of + two numbers + vars hold variables parts +Label(obj, displ, string, boxed) is a Text connected tu a object +VarLabel(obj, displ, string, vars, boxed) + +PythonScript(script, argvec) "script" is a string containing the + python script + +Translate(object, vector) The result is an object of the same + kind as 'object' +CentralSymmetry(object, center) +AxialSymmetry(object, line) +Rotate(object,center, angle) +Scale(object,center, segment) The length of the segment is the + scaling ratio +Scale2(object, center, s1, s2) The ratio of the lengths of s1 and + s2 is the scaling ratio +InvertPoint(point, circle) Circular invertion of: a point +InvertLine(line, circle) ... an other object +InvertCircle(circle, circle) +InvertArc(arc, circle) +InvertSegment(segment, circle) +CircularInversion(object, circle) + +------------------------------------------------------------------------------- + + diff --git a/kig/pykig/Makefile.am b/kig/pykig/Makefile.am new file mode 100644 index 00000000..40402a36 --- /dev/null +++ b/kig/pykig/Makefile.am @@ -0,0 +1 @@ +bin_SCRIPTS = pykig.py diff --git a/kig/pykig/VERSION b/kig/pykig/VERSION new file mode 100644 index 00000000..d3b5ba4b --- /dev/null +++ b/kig/pykig/VERSION @@ -0,0 +1 @@ +0.2.11 diff --git a/kig/pykig/changelog.txt b/kig/pykig/changelog.txt new file mode 100644 index 00000000..94c604c0 --- /dev/null +++ b/kig/pykig/changelog.txt @@ -0,0 +1,64 @@ +0.2.10 +Aggiunte alcune virgole mancanti +cambiato il nome dell'oggetti da InvertObject a CircularInversion +0.2.9 +Eliminati gli oggetti: InvertLine, InvertCircle, InvertArc, InvertSegment +Aggiunto gli oggetti InvertObject, VerticalCubic, ConicArc +Aggiunta la proproietà support +Modificati alcuni esempi +0.2.8 +Aggiunte a Triangle le proprietà dei poligoni. +0.2.7 +Aggiunta, al manuale, la sezione sui metodi di kigdocument. +E sugli oggetti Punto, Linea, Circonferenza, Parabola. +Corretto un bug sul valore di default di internal. +0.2.6 +Aggiustata la codifica dei caratteri nel manuale. +0.2.5 +Scritti i primi 3 capitoli del manuale. +Modificato gli esempi in modo che funzionino. +7 nuovi metodi in KigDocument per settare i valori di default. +eliminate la funzioni per settare i valori di default- +Semplificata la gerarchia di classi con l'eliminazione di KigOut. +0.2.4 +Aggiunti: gli oggetti "InvertArc" e "InvertSegment" +Predisposte le tuple per l'aggiunta di propietà di alcuni oggetti. +0.2.3 +Corretta la generazione del file .kig +Aggiunta la proprietà "Bisector" +Aggiunto il metodo "bisector" +Aggiunti 4 nuovi esempi. +Nell'esempio 1170Circocentro2.kpy è inserito un esempio di OOP + in 3 righe di codice: + la cerazione della classe Segmento che estende la classe "Segment" +0.2.2 +Modificato il nome del programma: da pykig a pykig.py +Modificato il nome della funzione main() ora si chiama prog() +Modificato l'impianto del programma in modo che possa essere eseguito sia + come programma, sia come libreria. +In particolare: modificati i metodi di KigDocument: __init__() e close() +Inseriti dei controlli sugli errori in scrittura del file +Aggiunti gli esempi esempi01, equivalenti a quelli di esempi00, che + utilizzano pykig.py come libreria +Rinominata la directory con gli esempi seri in esempi02. +Rimodificato il comportamento di pykig.py, quando si chiede di avere un file + in output non chiama Kig. +Ora le stringhe visualizzano correttamente le lettere accentate. +Modificata la funzione convstr(s), aggiunta la costante DICT. +Aggiunto l'oggetto Label che associa una stringa ad un oggetto. +Aggiornato il file API.txt per allinearlo ai cambiamenti. +0.2.1: +Modificato il numero di versione, per renderlo coerente tra codice e nome +del file compresso. +Modificata la gerarchia di classi con l'aggiunta di KigOut +Modificati i nomi di variabili che hanno visibilità all'interno dei file .pyk +Riunito tutte le istruzioni di scrittura sul file in un'unica procedura: + KigDocument.close() +Resa inutile la chiamata a kigopen() +Aggiunte le due funzioni noaxes() e nogrid() +Riuniti una sequenza di comandi nella funzione main() che viene eseguita + quando pykig viene chiamato come programma. +Modificata la costruzione di alcuni oggetti: dove è richiesto un punto di tipo + internal si può passare una tupla con le due coordinate. +Modificati gli esempi in modo da farli funzionere con le nuove + caratteristiche. diff --git a/kig/pykig/pykig.pth b/kig/pykig/pykig.pth new file mode 100644 index 00000000..203b81d4 --- /dev/null +++ b/kig/pykig/pykig.pth @@ -0,0 +1,2 @@ +# +pykig diff --git a/kig/pykig/pykig.py b/kig/pykig/pykig.py new file mode 100755 index 00000000..106c8a0c --- /dev/null +++ b/kig/pykig/pykig.py @@ -0,0 +1,824 @@ +#!/usr/bin/env python +# -*- coding: iso-8859-15 -*- +#-------------------------------python------------------------pykig.py--# +# # +# Da Python a Kig # +# # +#--Maurizio Paolini-Daniele Zambelli-----------------------------2005---# +# +# (licenza GPL) + +version="0.2.11" + +##### +# Type constant +##### +TI=type(0) +TF=type(0.) +TS=type("") +TT=type((0, 0)) + +##### +# Constants: Point Style, Line Style, defaults values... +##### +PS=("Round", "RoundEmpty", "Rectangular", "RectangularEmpty", "Cross") +LS=("SolidLine", "DashLine", "DashDotLine", "DashDotDotLine", "DotLine") +KIGTRUE="true" +KIGFALSE="false" +DEFWIDTH=-1 +DEFCOLOR="#0000ff" +DEFNAME="none" +PROPERTY_INI="Property which" +OBJECT_INI="Object type" +PROPERTY_END="Property" +OBJECT_END="Object" +DICT=(("&","&"), ("<","<"), (">",">"), + ("à","à "), ("è","è"), ("ì","ì"), ("ò","ò"), ("ù","ù"), ("é","é")) + +# +# this is a trick to allow definitions like "p=Point(0,0,HIDDEN)" +# +HIDDEN=KIGFALSE +VISIBLE=KIGTRUE + +##### +# Validation parameters +##### + +def parameter(val, defval): + if val==None: return defval + else: return val + +def validshown(shown): + if shown==KIGTRUE or shown==KIGFALSE: return shown + +def validwidth(width): + if type(width)==TI: return width + +def validpointstyle(ps): + if ps in PS: return ps + +def validname(name): + if type(name)==TS: return name + +def validlinestyle(ls): + if ls in LS: return ls + +def validcolor(color): + if type(color)==TS: return color + +##### +# if as function +##### + +def rif(condition, val1, val2): + """Return val1 if condition is True else return val2.""" + if condition: return val1 + else: return val2 + +##### +# Force some Python variables as kig variables +##### + +def kig_double(val): + tp=type(val) + if tp==TI or tp==TF: return Double(val) + else: return val + +def kig_int(val): + tp=type(val) + if tp==TI: return Int(val) + else: return val + +def kig_string(val): + tp=type(val) + if tp==TS: return String(val) + else: return val + +def kig_point(val): + tp=type(val) + if tp==TT: + x, y = val + return Point(x, y, internal=True) + else: + return val + +def kig_relpoint(obj, displ): + x, y = displ + return RelativePoint(x, y, obj, internal=True) + +##### +# base classes +##### + +##### +# Classe KigDocument +##### + +class KigDocument(object): + """ Classe che produce il documento kig. + + genealogia: + KigDocument <- object + + attributi di classe: + + attributi: + axes + grid + outfilename + outfile + callkig + of + viewkig + hierarchy + internal + width + pointstyle + name + linestyle + shown + color + + metodi: + viewappend + hierarchyappend + setcallkig + setof + str_open + close + noaxes + nogrid + hideobjects + showobjects + setwidth + setpointstyle + setname + setlinestyle + setshown + setcolor + setinternal + """ + + def __init__(self, outfilename, callkig=True, of=False): +# print "KigDocument.__init__()" + self.axes = "1" + self.grid = "1" + self.outfilename=outfilename + self.callkig=callkig + self.of=of + try: + self.outfile = open(outfilename, 'w') + except IOError, value: +# print >> sys.stderr, outfilename, 'unwritable' + print >> sys.stderr, value + sys.exit(2) +# KigOut._kigdocument=self + KigDOP._kd=self + KigView._kd=self + self.viewkig=[] + self.hierarchy=[] +# Defaults values + self.internal=False + self.width=DEFWIDTH + self.pointstyle=PS[0] + self.name=DEFNAME + self.linestyle=LS[0] + self.shown=VISIBLE + self.color=DEFCOLOR + + + def viewappend(self, e): self.viewkig.append(e) + def hierarchyappend(self, e): self.hierarchy.append(e) + def setcallkig(v): self.callkig=v + def setof(v): self.of=v + + def str_open(self): + return """<!DOCTYPE KigDocument> +<KigDocument axes="%s" grid="%s" CompatibilityVersion="0.7.0" Version="0.9.1" > + <CoordinateSystem>Euclidean</CoordinateSystem> + <Hierarchy> +""" % (self.axes, self.grid) + + def close(self): + try: + self.outfile.write(self.str_open()) + self.outfile.writelines(self.hierarchy) + self.outfile.write(" </Hierarchy>\n <View>\n") + for f in self.viewkig: + self.outfile.write(f.str_view()) + self.outfile.write(" </View>\n</KigDocument>\n") + if self.outfile != sys.stdout: + self.outfile.close() + except IOError, value: + print >> sys.stderr, value + sys.exit(2) + try: + if self.callkig: + err = os.system('kig --nofork ' + self.outfilename) + except Exception, value: + print >> sys.stderr, value + if not self.of: + os.system('rm ' + self.outfilename) + + def noaxes(self): self.axes="0" + def nogrid(self): self.grid="0" + def hideobjects(self): self.shown=HIDDEN + def showobjects(self): self.shown=VISIBLE + def setwidth(self, w): self.width=w + def setpointstyle(self, ps): self.pointstyle=ps + def setname(self, n): self.name=n + def setlinestyle(self, ls): self.linestyle=ls + def setshown(self, s): self.shown=s + def setcolor(self, c): self.color=c + def setinternal(self, v): self.internal=v + +##### +# Classe KigDOP +##### + +#class KigDOP(KigOut): +class KigDOP(object): + """Classe da cui deriva ogni elemento che ha un id: Data, Object, Property. + + genealogia: + kigDOP <- object + + attributo di classe: + id-counter + + attributi: + id + type + + metodi: + getid + str_hierarchy + """ + _kd=None + _id_counter=0 + + def __init__(self, type): + KigDOP._id_counter+=1 + self.id=KigDOP._id_counter + self._type=type +# self.getkigdocument().hierarchyappend(self.str_hierarchy()) + KigDOP._kd.hierarchyappend(self.str_hierarchy()) + + def getid(self): return str(self.id) + def str_hierarchy(self): pass + +##### +# Classe KigView +##### + +#class KigView(KigOut): +class KigView(object): + """ Classe con i dati di visualizzazione + + genealogia: + KigView <- object + + attributi di classe: + _kd + + attributi: + shown + width + style + color + name + pointstyle + + metodi: + str_view + show + hide + """ + _kd=None + + def __init__(self, object, shown, name, width, pointstyle, linestyle, color): + self.object=object + self.shown = parameter(shown, KigView._kd.shown) + self.width = parameter(width, KigView._kd.width) + self.pointstyle = parameter(pointstyle, KigView._kd.pointstyle) + self.linestyle = parameter(linestyle, KigView._kd.linestyle) + self.color = parameter(color, KigView._kd.color) + self.name = parameter(name, KigView._kd.name) + KigView._kd.viewappend(self) + + def str_view(self): + """Produce la stringa che viene scritta sotto <View>. + + esempio: + <Draw width="-1" point-style="Round" namecalcer="none" +style="SolidLine" shown=None color="#0000ff" object="3" /> +""" + + return ' <Draw width="%s" point-style="%s" namecalcer="%s"\ + style="%s" shown="%s" color="%s" object="%s" />\n' %\ + (self.width, self.pointstyle, self.name, + self.linestyle, self.shown, self.color, self.object.getid()) + +##### +# Classe Data +##### + +class Data(KigDOP): + """ Classe da cui deriva ogni elemento Data + + genealogia: + Data <- KigDOP <- object + + attributi: + val + + metodi: + str_hierarchy +""" + def __init__(self, type, val): + self.val=val + KigDOP.__init__(self, type) + + def str_hierarchy(self): + """Produce la stringa che viene scritta sotto <Data>. + + esempio: + <Data type="double" id="170" >0.1</Data> +""" + return ' <Data type="%s" id="%s" >%s</Data>\n' % \ + (self._type, self.getid(), self.val) + +##### +# Classe PropObj +##### + +class PropObj(KigDOP): + """ Classe da cui deriva ogni elemento visibile + + genealogia: + PropObj <- KigDOP <- object + + attributi di classe: + + attributi: + prop + objvec + view + + metodi: + str_hierarchy + showname(self, n) + show(self) + hide(self) + setwidth(self, width) + setcolor(self, color) + setlinestyle(self, linestyle) + setpointstyle(self, pointstyle) + setname(self, n) + setshown(self, s) + getwidth(self) + getcolor(self) + getlinestyle(self) + getpointstyle(self) + """ + + def __init__(self, prop, type, objvec, shown, name, internal, + width, pointstyle, linestyle, color): + self.prop=prop + self.objvec=objvec + self.n_lb=None + KigDOP.__init__(self, type) + internal=parameter(internal, KigDOP._kd.internal) + if internal: + self.view = None + else: +# Qui si assume che, se viene dato un nome ad un oggetto, +# si voglia anche visualizzare questo nome + if name: n_id=self.showname(name, shown, width, pointstyle, linestyle, + color) + else: n_id=None + self.view = KigView(self, shown, n_id, width, pointstyle, linestyle, + color) + + def str_hierarchy(self): + """Produce la stringa che viene scritta sotto <Data>. + + esempio: + <Property which="mid-point" id="170" > + <Parent id="..." /> + </Property> + + oppure: + <Object type="ConstrainedPoint" id="14" > + <Parent id="13" /> + <Parent id="10" /> + </Object> +""" + retstring = ' <%s="%s" id="%s" >' %\ + ((self.prop and PROPERTY_INI or OBJECT_INI), + self._type, self.getid()) + for p in self.objvec: + retstring = retstring + '\n <Parent id="%s" />' % p.getid() + retstring = retstring + '\n </%s>\n' % (self.prop and PROPERTY_END or + OBJECT_END) + return retstring + + def showname(self, name, shown, width, pointstyle, linestyle, color): + n=String(name) + self.n_lb=Label(self, (0, 0), n, 0, shown, None, False, + width, pointstyle, linestyle, color) + return n.getid() + + def show(self): + if self.view: self.view.shown=None + def hide(self): + if self.view: self.view.shown=KIGFALSE + def setwidth(self, width): self.view.width=width + def setcolor(self, color): self.view.color=color + def setlinestyle(self, linestyle): + if linestyle in LS: self.view.linestyle=linestyle + def setpointstyle(self, pointstyle): + if pointstyle in PS: self.view.pointstyle=pointstyle + def type(self): return Type(self) + def setname(self, n): + v=self.view + v.name=self.showname(n, v.shown, v.width, v.pointstyle, v.linestyle, + v.color) + def setshown(self, s): self.view.shown=s + +##### +# Classe Property +##### + +class Property(PropObj): + """ Classe da cui deriva ogni elemento Property + + genealogia: + Property <- PropObj <- KigDOP <- object + """ + def __init__(self, type, parent, shown, name, internal, + width, pointstyle, linestyle, color): + PropObj.__init__(self, True, type, (parent,), shown, name, internal, + width, pointstyle, linestyle, color) +# print shown + +##### +# Classe Object +##### + +class Object(PropObj): + """ Classe da cui deriva ogni elemento Oggetto + + genealogia: + Object <- PropObj <- KigDOP <- object + """ + + def __init__(self, type, objvec, shown, name, internal, + width, pointstyle, linestyle, color): + PropObj.__init__(self, False, type, objvec, shown, name, internal, + width, pointstyle, linestyle, color) + +##### +# Data +##### + +data=(\ +("Int", "int", "val"), +("Double", "double", "val"), +("String", "string", "convstr(val)"), +) + +def convstr(s): + for o, n in DICT: + s=s.replace(o, n) + return s + +def databuild(nomeclasse, nomekig, v="val"): + """Create string with a Data class definition.""" + return """class %s(Data): + + def __init__(self, val): + Data.__init__(self, "%s", %s) +""" % (nomeclasse, nomekig, v) + +for d in data: + p1, p2, p3 = d + exec databuild(p1, p2, p3) + +##### +# Objects +##### +"""Da aggiungere: +("ConvexHall", "ConvexHall", "polygon,", "(polygon,),"), +("EllipseByFocusFocusPoint", "EllipseBFFP", "p1, p2, p3,", "(p1, p2, p3,),"), +("HyperbolaByFocusFocusPoint", "HyperbolaBFFP", "p1, p2, p3,", "(p1, p2, p3,),"), +(ConicsBy5Points", "ConicB5P", "p1, p2, p3, p4, p5,", "(p1, p2, p3, p4, p5),"), +("ParabolaBy3Points", "ParabolaBTP", "p1, p2, p3,", "(p1, p2, p3,),"), +("CocCurve", "CocCurve", "line, point,", "(line, point,),"), +""" +objects=(\ +###### Points class +("Point", "FixedPoint", "x, y,", "(kig_double(x), kig_double(y)),"), +("ConstrainedPoint", "ConstrainedPoint", + "t, curve,", "(kig_double(t), curve),"), +("RelativePoint", "RelativePoint", + "x, y, p,", "(kig_double(x), kig_double(y), p),"), +###### segments, rays, lines +("Line", "LineAB", "p1, p2,", "(p1, p2),"), +("Segment", "SegmentAB", "p1, p2,", "(p1, p2),"), +("Ray", "RayAB", "p1, p2,", "(p1, p2),"), +("Orthogonal", "LinePerpend", "line, point,", "(line, point,),"), +("Parallel", "LineParallel", "line, point,", "(line, point,),"), +###### Circles, arcs, ... +("Circle", "CircleBCP", "center, point,", "(center, point,),"), +("CircleByCenterRadius", "CircleBPR", "center, radius,", "(center, radius,),"), +("CircleBy3Points", "CircleBTP", "p1, p2, p3,", "(p1, p2, p3,),"), +("ArcBy3Points", "ArcBTP", "p1, p2, p3,", "(p1, p2, p3,),"), +("ArcByCenterPointAngle", "ArcBCPA", + "center, point, angle,", "(center, point, angle),"), +###### Conics... +("ParabolaByDirectrixFocus", "ParabolaBDP", "line, point,", "(line, point,),"), +("VerticalCubic", "VerticalCubicB4P", "p1, p2, p3, p4,", "(p1, p2, p3, p4),"), +("ConicArc", "ConicArcBTPC", "p1, p2, p3, center,", "(p1, p2, p3, center),"), +##### +# intersections. The only standard object is the intersection +# of two lines, which always gives one single point +##### +("LineLineIntersection", "LineLineIntersection", "l1, l2,", "(l1, l2),"), +##### +# Classe CircleCircleIntersection e ConicLineIntersection +# l'intero "which" puo' assumere i valori 1 o -1 per indicare quale +# delle due intersezioni si desidera ottenere +# si potrebbe mettere un controllo... +##### +("CircleCircleIntersection", "CircleCircleIntersection", + "c1, c2, witch,", "(c1, c2, Int(witch),),"), +("ConicLineIntersection", "ConicLineIntersection", + "conic, line, witch,", "(conic, line, Int(witch),),"), +###### Classe Triangle +("Triangle", "TriangleB3P", "p1, p2, p3,", "(p1, p2, p3),"), +###### Classe Polygon (the only argument is a points vect) +("Polygon", "PolygonBNP", "pvec,", "pvec,"), +###### Classe PolygonBCV +# Poligono regolare dati il centro e un vertice; il terzo argomento +# e' un intero contenente il numero di lati +("PolygonBCV", "PoligonBCV", + "center, vertex, n,", "(center, vertex, Int(n)),"), +##### Classe PolygonVertex (poligono, intero >= 0) +("PolygonVertex", "PolygonVertex", + "polygon, i,", "(polygon, Int(i)),"), +##### Classe PolygonSide (poligono, intero >= 0) +("PolygonSide", "PolygonSide", + "polygon, i,", "(polygon, Int(i)),"), +###### vector, angle,... +("Vector", "Vector", "p1, p2,", "(p1, p2),"), +("Angle", "Angle", "p1, v, p2,", "(p1, v, p2),"), +###### Transformations +("Translate", "Translation", "obj, vector,", "(obj, vector),"), +("CentralSymmetry", "PointReflection", "obj, center,", "(obj, center),"), +("AxialSymmetry", "LineReflection", "obj, center,", "(obj, center),"), +("Rotate", "Rotation", + "obj, center, angle,", "(obj, center, angle),"), +("Scale", "ScalingOverCenter", + "obj, center, segment,", "(obj, center, segment),"), +("Scale2", "ScalingOverCenter2", + "obj, center, s1, s2,", "(obj, center, s1, s2),"), +("InvertPoint", "InvertPoint", + "point, circle,", "(point, circle),"), +("CircularInversion", "CircularInversion", + "objecttoinvert, circle,", "(objecttoinvert, circle),"), +("InvertLine", "InvertLine", + "line, circle,", "(line, circle),"), +("InvertCircle", "InvertCircle", + "circletoinvert, circle,", "(circletoinvert, circle),"), +("InvertArc", "InvertArc", + "arctoinvert, circle,", "(arctoinvert, circle),"), +("InvertSegment", "InvertSegment", + "segment, circle,", "(segment, circle),"), +###### Text, Label, ... +("Text", "Label", + "point, string, boxed=0,", + "(Int(boxed), kig_point(point), kig_string(string)),"), +("Label", "Label", + "obj, displ, string, boxed=0,", + "(Int(boxed),kig_relpoint(obj, displ),kig_string(string)),"), +("VarText", "Label", + "point, string, vars, boxed=0,", + "(Int(boxed), kig_point(point), \ + kig_string(string))+tuple(vars),"), +("VarLabel", "Label", + "obj, displ, string, vars, boxed=0,", + "(Int(boxed), kig_relpoint(obj, displ), \ + kig_string(string))+tuple(vars),"), +###### Python scripting... we need some work here... +("PythonScript", "PythonExecuteType", + "script, argvec,", + '(Object("PythonCompileType", (kig_string(script),), shown,\ + name, internal, width, pointstyle, linestyle,\ + color),)+tuple(argvec),'), +) + +def objectbuild(nameclass, namekig, params, objparams): + """Create string with a Object class definition.""" + return """class %s(Object): + + def __init__(self, %s shown=None, name=None, internal=None, + width=None, pointstyle=None, linestyle=None, color=None): + Object.__init__(self, "%s", %s shown, name, internal, + width, pointstyle, linestyle, color) +""" % (nameclass, params, namekig, objparams) + +for o in objects: + p1, p2, p3, p4 = o + exec objectbuild(p1, p2, p3, p4) + +##### +# Propertys +##### + +property=(\ +("Type", "base-object-type", "o,", "o,"), +("Coordinate", "coordinate", "point,", "point,"), +("XCoord", "coordinate-x", "point,", "point,"), +("YCoord", "coordinate-y", "point,", "point,"), +("MidPoints", "mid-point", "a, b,", "Segment(a, b, internal=True),"), +("MidPoint", "mid-point", "segment,", "segment,"), +("EndPointA", "end-point-A", "segment,", "segment,"), +("EndPointB", "end-point-B", "segment,", "segment,"), +("Length", "length", "segment,", "segment,"), +("Equation", "equation", "segment,", "segment,"), +("Slope", "slope", "segment,", "segment,"), +("NumOfSides", "polygon-number-of-sides", "poly,", "poly,"), +("Perimeter", "polygon-perimeter", "poly,", "poly,"), +("Surface", "polygon-surface", "poly,", "poly,"), +("CenterOfMass", "polygon-center-of-mass", "poly,", "poly,"), +("WindingNumber", "polygon-winding-number", "poly,", "poly,"), +("Radius", "radius", "circle,", "circle,"), +("Center", "center", "circle,", "circle,"), +("Bisector", "angle-bisector", "angle,", "angle,"), +("Support", "support", "object,", "object,"), +) + +def propertybuild(nameclass, namekig, params, objparams): + """Create string with a Property class definition.""" + return """class %s(Property): + + def __init__(self, %s shown=None, name=None, internal=False, + width=None, pointstyle=None, linestyle=None, color=None): + Property.__init__(self, "%s", %s shown, name, internal, + width, pointstyle, linestyle, color) +""" % (nameclass, params, namekig, objparams) + +for p in property: + p1, p2, p3, p4 = p + exec propertybuild(p1, p2, p3, p4) + +##### +# Start of properties definitions as Object's metod +##### +# da sistemare! +points =(Point, ConstrainedPoint, RelativePoint, PolygonVertex) +lines=(Segment, Ray, Vector, InvertLine) +segments=(Segment, Vector, PolygonSide, InvertSegment) +circles =(Circle, CircleBy3Points, CircularInversion, ArcBy3Points, + ArcByCenterPointAngle, InvertCircle) +polygons=(Polygon, PolygonBCV, Triangle) +angles =(Angle,) +supp = circles+lines + +methods=(\ +("coordinate", "coordinate", points), +("coordinate-x", "xcoord", points), +("coordinate-y", "ycoord", points), +("mid-point", "midpoint", segments), +("end-point-A", "endpointA", segments), +("end-point-B", "endpointB", segments), +("length", "length", segments), +("equation", "equation", lines), +("slope", "slope", lines), +("polygon-number-of-sides", "numofsides", polygons), +("polygon-perimeter", "perimeter", polygons), +("polygon-surface", "surface", polygons), +("polygon-center-of-mass", "centerofmass", polygons), +("polygon-winding-number", "windingnumber", polygons), +("center", "center", polygons), +("center", "center", circles), +("angle-bisector", "bisector", angles), +("support", "support", supp), +) + +def methodsbuild(namekig): + """Create string with a method class definition.""" + return """def method(self,shown=None, name=None, internal=False, + width=None, pointstyle=None, linestyle=None, color=None): + return Property("%s", self, shown, name, internal, + width, pointstyle, linestyle, color) +""" % (namekig, ) + +for p in methods: + p1, p2, cl = p + exec methodsbuild(p1) + for c in cl: + setattr(c, p2, method) + +##### +# Usage +##### + +def usage(codexit): + print >> sys.stderr, """ +usage: pykig.py [options...] file ... + +Options: + -h, --help Show this text. + -o <kig_file> + --output <kig_file> output <kig_file> no call Kig + -v, --version output version + -n, --nokig no call Kig + +examples: + $ pykig.py my_file.kpy + $ pykig.py -o output_file.kig my_file.kpy + $ ... +""" + sys.exit(codexit) + +##### +# Main body +##### + +import sys, traceback, os +#from math import * # for user's programs +import math # for user's programs +import getopt +import atexit + +def prog(): + try: + _opts, _args = getopt.getopt(sys.argv[1:], "hvno:",\ + ["help", "version", "nokig", "output="]) + except getopt.GetoptError: + print "GetoptError" + usage(2) + _callKig=True + _of=False + for _opt, _arg in _opts: + if _opt in ("-h", "--help"): + usage(0) + if _opt in ("-v", "--version"): + print "version:", version + sys.exit(0) + if _opt in ("-n", "--nokig"): + _callKig=False + elif _opt in ("-o", "--output"): + _outfilename=_arg + _of=True + _callKig=False # se c'è il file di output, non viene chiamato Kig + if len(_args)==0: + _infilename=raw_input("Nome del file di input: ") + if not _infilename: + print "No Input filename" + usage(2) + elif len(_args)==1: + _infilename=_args[0] + else: + print "No infilename" + usage(2) + try: + _infile = open(_infilename, 'r') + except: + print >> sys.stderr, _infilename, 'unreadable' + sys.exit(2) + if _of: + if _outfilename=="-": + _n, _e = os.path.splitext(_infilename) + _outfilename=_n+'.kig' + else: + _outfilename="/tmp/pykig" + str(os.getpid()) + ".kig" + global kigdocument + kigdocument=KigDocument(_outfilename, _callKig, _of) + kd=kigdocument + try: + execfile(_infilename, globals()) + except: + print >> sys.stderr, 'syntax error in', _infilename + _info = sys.exc_info() # vorrei stampare il traceback... + traceback.print_exc() + sys.exit(3) + kigdocument.close() + + if _infile != sys.stdin: + _infile.close() + +def lib(): + _outfilename="/tmp/pykig" + str(os.getpid()) + ".kig" + global kigdocument + kigdocument=KigDocument(_outfilename) + kd=kigdocument + atexit.register(kigdocument.close) + +if __name__ == "__main__": + prog() +else: + lib() diff --git a/kig/scripting/Makefile.am b/kig/scripting/Makefile.am new file mode 100644 index 00000000..c007a612 --- /dev/null +++ b/kig/scripting/Makefile.am @@ -0,0 +1,22 @@ +INCLUDES=$(all_includes) $(BOOST_PYTHON_INCLUDES) +KDE_CXXFLAGS=$(USE_EXCEPTIONS) +noinst_LTLIBRARIES=libscripting.la +noinst_HEADERS = \ + python_type.h \ + python_scripter.h \ + script-common.h \ + script_mode.h \ + newscriptwizard.h +libscripting_la_SOURCES = \ + python_type.cc \ + python_scripter.cc \ + script-common.cc \ + script_mode.cc \ + newscriptwizardbase.ui \ + newscriptwizard.cc +libscripting_la_LIBADD = $(BOOST_PYTHON_LIBS) $(PYTHON_LIBS) -lkatepartinterfaces +libscripting_la_LDFLAGS = $(all_libraries) $(PYTHON_LDFLAGS) +METASOURCES=AUTO + +xml_DATA = python-kig.xml +xmldir = $(kde_datadir)/katepart/syntax diff --git a/kig/scripting/newscriptwizard.cc b/kig/scripting/newscriptwizard.cc new file mode 100644 index 00000000..165f8e98 --- /dev/null +++ b/kig/scripting/newscriptwizard.cc @@ -0,0 +1,234 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "newscriptwizard.h" +#include "newscriptwizard.moc" + +#include "script_mode.h" + +#include <qlabel.h> +#include <qlayout.h> + +//#include <kactionclasses.h> +//#include <kactioncollection.h> +// make it still work on old kde 3.1... +#include <kaction.h> +// +#include <kapplication.h> +#include <kglobalsettings.h> +#include <kpopupmenu.h> +#include <ktextedit.h> +#include <ktexteditor/clipboardinterface.h> +#include <ktexteditor/dynwordwrapinterface.h> +#include <ktexteditor/editinterface.h> +#include <ktexteditor/editorchooser.h> +#include <ktexteditor/popupmenuinterface.h> +#include <ktexteditor/undointerface.h> +#include <ktexteditor/view.h> + +#include <assert.h> + +NewScriptWizard::~NewScriptWizard() +{ + if ( !document ) + { + delete textedit; + } + else + { + //restoring the state of the dynamic word wrap + dynamic_cast<KTextEditor::DynWordWrapInterface*>( editor )->setDynWordWrap( prevDynWordWrap ); + delete editor->document(); + } +} + +NewScriptWizard::NewScriptWizard( QWidget* parent, ScriptModeBase* mode ) + : NewScriptWizardBase( parent, "New Script Wizard" ), + mmode( mode ) +{ + document = KTextEditor::EditorChooser::createDocument( 0, "KTextEditor::Document" ); +// document = 0; + + gridLayout->expand( 2, 1 ); + + if ( !document ) + { + // there is no KDE textditor component installed, so we'll use a + // simplier KTextEdit + textedit = new KTextEdit( mpcode, "textedit" ); + textedit->setFont( KGlobalSettings::fixedFont() ); + gridLayout->addWidget( textedit, 1, 0 ); + } + else + { + // creating the 'view', hat is what the user see and interact with + editor = document->createView( mpcode, "editor" ); + gridLayout->addWidget( editor, 1, 0 ); + + // casting to the interfaces we'll use often + hli = dynamic_cast<KTextEditor::HighlightingInterface*>( document ); + + // displaying the left border with line numbers + KToggleAction *a = dynamic_cast<KToggleAction*>( editor->actionCollection()->action("view_line_numbers") ); + a->activate(); + + // saving the state of dynamic word wrap and disabling it + prevDynWordWrap = dynamic_cast<KTextEditor::DynWordWrapInterface*>( editor )->dynWordWrap(); + dynamic_cast<KTextEditor::DynWordWrapInterface*>( editor )->setDynWordWrap( false ); + + // saving the "no highlight" id + noHlStyle = hli->hlMode(); + + // creating the popup menu + KPopupMenu* pm = new KPopupMenu( editor ); + // creating the actions for the code editor... + KActionCollection* ac = new KActionCollection( editor ); + KAction* undoAction = KStdAction::undo( this, SLOT( slotUndo() ), ac ); + KAction* redoAction = KStdAction::redo( this, SLOT( slotRedo() ), ac ); + KAction* cutAction = KStdAction::cut( this, SLOT( slotCut() ), ac ); + KAction* copyAction = KStdAction::copy( this, SLOT( slotCopy() ), ac ); + KAction* pasteAction = KStdAction::paste( this, SLOT( slotPaste() ), ac ); + // ... and plugging them into the popup menu (to build it, of course :) ) + undoAction->plug( pm ); + redoAction->plug( pm ); + pm->insertSeparator(); + cutAction->plug( pm ); + copyAction->plug( pm ); + pasteAction->plug( pm ); + + // finally, we install the popup menu + dynamic_cast<KTextEditor::PopupMenuInterface*>( editor )->installPopup( pm ); + } + + connect( this, SIGNAL( helpClicked() ), this, SLOT( slotHelpClicked() ) ); +} + +void NewScriptWizard::back() +{ + if ( currentPage() == mpcode ) + { + // currentPage() is not yet updated, so we're now entering the + // args page.. + mmode->argsPageEntered(); + } + else assert( false ); + NewScriptWizardBase::back(); +} + +void NewScriptWizard::next() +{ + if ( currentPage() == mpargs ) + mmode->codePageEntered(); + else assert( false ); + if ( !document ) + { + textedit->setFocus(); + } + else + { + editor->setFocus(); + } + NewScriptWizardBase::next(); +} + +void NewScriptWizard::reject() +{ + if ( mmode->queryCancel() ) + NewScriptWizardBase::reject(); +} + +void NewScriptWizard::accept() +{ + if ( mmode->queryFinish() ) + NewScriptWizardBase::accept(); +} + +void NewScriptWizard::slotHelpClicked() +{ + kapp->invokeHelp( QString::fromLatin1( "scripting" ), + QString::fromLatin1( "kig" ) ); +} + +void NewScriptWizard::setText( const QString& text ) +{ + if ( !document ) + { + textedit->setText( text ); + } + else + { + dynamic_cast<KTextEditor::EditInterface*>( document )->setText( text ); + } +} + +QString NewScriptWizard::text() +{ + if ( !document ) + { + return textedit->text(); + } + else + { + return dynamic_cast<KTextEditor::EditInterface*>( document )->text(); + } +} + +void NewScriptWizard::setType( ScriptType::Type type ) +{ + labelFillCode->setText( ScriptType::fillCodeStatement( type ) ); + + if ( !!document ) + { + if ( type != ScriptType::Unknown ) + { + for ( uint i = 0; i < hli->hlModeCount(); ++i ) + { + if ( hli->hlModeName( i ) == ScriptType::highlightStyle( type ) ) + { + // we found our highlight style, setting it + hli->setHlMode( i ); + return; + } + } + } + else + { + hli->setHlMode( noHlStyle ); + } + } +} + +void NewScriptWizard::slotUndo() +{ + dynamic_cast<KTextEditor::UndoInterface*>( document )->undo(); +} + +void NewScriptWizard::slotRedo() { + dynamic_cast<KTextEditor::UndoInterface*>( document )->redo(); +} + +void NewScriptWizard::slotCut() { + dynamic_cast<KTextEditor::ClipboardInterface*>( editor )->cut(); +} + +void NewScriptWizard::slotCopy() { + dynamic_cast<KTextEditor::ClipboardInterface*>( editor )->copy(); +} + +void NewScriptWizard::slotPaste() { + dynamic_cast<KTextEditor::ClipboardInterface*>( editor )->paste(); +} diff --git a/kig/scripting/newscriptwizard.h b/kig/scripting/newscriptwizard.h new file mode 100644 index 00000000..67b51d94 --- /dev/null +++ b/kig/scripting/newscriptwizard.h @@ -0,0 +1,72 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_SCRIPTING_NEWSCRIPTWIZARD_H +#define KIG_SCRIPTING_NEWSCRIPTWIZARD_H + +#include "newscriptwizardbase.h" + +#include "script-common.h" + +#include <ktextedit.h> +#include <ktexteditor/document.h> +#include <ktexteditor/highlightinginterface.h> +#include <ktexteditor/view.h> + +#include <algorithm> + +class ScriptModeBase; + +class NewScriptWizard + : public NewScriptWizardBase +{ + Q_OBJECT + ScriptModeBase* mmode; +public: + NewScriptWizard( QWidget* parent, ScriptModeBase* mode ); + ~NewScriptWizard(); + + void back(); + void next(); + void reject(); + + void setText( const QString& text ); + QString text(); + + void setType( ScriptType::Type type ); + +public slots: + void slotHelpClicked(); + void accept(); + + void slotUndo(); + void slotRedo(); + void slotCut(); + void slotCopy(); + void slotPaste(); + +protected: + KTextEdit* textedit; + KTextEditor::Document* document; + KTextEditor::HighlightingInterface* hli; + KTextEditor::View* editor; + + uint noHlStyle; + bool prevDynWordWrap; +}; + +#endif diff --git a/kig/scripting/newscriptwizardbase.ui b/kig/scripting/newscriptwizardbase.ui new file mode 100644 index 00000000..30117961 --- /dev/null +++ b/kig/scripting/newscriptwizardbase.ui @@ -0,0 +1,81 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>NewScriptWizardBase</class> +<widget class="QWizard"> + <property name="name"> + <cstring>NewScriptWizardBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>610</width> + <height>360</height> + </rect> + </property> + <property name="caption"> + <string>New Script</string> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>mpargs</cstring> + </property> + <attribute name="title"> + <string>Select Arguments</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Select the argument objects ( if any ) +in the Kig window and press "Next".</string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>mpcode</cstring> + </property> + <attribute name="title"> + <string>Enter Code</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>gridLayout</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>labelFillCode</cstring> + </property> + <property name="text"> + <string>Now fill in the code:</string> + </property> + </widget> + </grid> + </widget> + </vbox> + </widget> +</widget> +<customwidgets> +</customwidgets> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>ktextedit.h</includehint> +</includehints> +</UI> diff --git a/kig/scripting/python-kig.xml b/kig/scripting/python-kig.xml new file mode 100644 index 00000000..930d5f8f --- /dev/null +++ b/kig/scripting/python-kig.xml @@ -0,0 +1,289 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE language> +<!-- Python syntax highlightning v0.9 by Per Wigren --> +<!-- Python syntax highlightning v1.2.x for Kig by Pino Toscano --> +<language name="Python-Kig" version="1.2.1" kateversion="2.1" section="Scripts" extensions="*.py;*.pyw" mimetype="application/x-python;text/x-python" casesensitive="1"> + <highlighting> + <list name="prep"> + <item> import </item> + <item> from </item> + <item> as </item> + </list> + + <list name="statements"> + <item> and </item> + <item> assert </item> + <item> break </item> + <item> class </item> + <item> continue </item> + <item> def </item> + <item> del </item> + <item> elif </item> + <item> else </item> + <item> except </item> + <item> exec </item> + <item> finally </item> + <item> for </item> + <item> global </item> + <item> if </item> + <item> in </item> + <item> is </item> + <item> lambda </item> + <item> not </item> + <item> or </item> + <item> pass </item> + <item> print </item> + <item> raise </item> + <item> return </item> + <item> try </item> + <item> while </item> + <item> yield </item> + </list> + + <list name="builtinfuncs"> + <item> abs </item> + <item> apply </item> + <item> buffer </item> + <item> callable </item> + <item> chr </item> + <item> cmp </item> + <item> coerce </item> + <item> compile </item> + <item> complex </item> + <item> copyright </item> + <item> credits </item> + <item> delattr </item> + <item> dir </item> + <item> divmod </item> + <item> eval </item> + <item> execfile </item> + <item> exit </item> + <item> filter </item> + <item> float </item> + <item> getattr </item> + <item> globals </item> + <item> hasattr </item> + <item> hash </item> + <item> hex </item> + <item> id </item> + <item> input </item> + <item> int </item> + <item> intern </item> + <item> isinstance </item> + <item> issubclass </item> + <item> iter </item> + <item> len </item> + <item> license </item> + <item> list </item> + <item> locals </item> + <item> long </item> + <item> map </item> + <item> max </item> + <item> min </item> + <item> oct </item> + <item> open </item> + <item> ord </item> + <item> pow </item> + <item> quit </item> + <item> range </item> + <item> raw_input </item> + <item> reduce </item> + <item> reload </item> + <item> repr </item> + <item> round </item> + <item> setattr </item> + <item> slice </item> + <item> str </item> + <item> tuple </item> + <item> type </item> + <item> unichr </item> + <item> unicode </item> + <item> vars </item> + <item> xrange </item> + <item> zip </item> + <!-- BEGIN: math module functions --> + <item> acos </item> + <item> asin </item> + <item> atan </item> + <item> atan2 </item> + <item> ceil </item> + <item> cos </item> + <item> cosh </item> + <item> degrees </item> + <item> exp </item> + <item> fabs </item> + <item> floor </item> + <item> fmod </item> + <item> frexp </item> + <item> hypot </item> + <item> ldexp </item> + <item> log </item> + <item> log10 </item> + <item> modf </item> + <item> radians </item> + <item> sin </item> + <item> sinh </item> + <item> sqrt </item> + <item> tan </item> + <item> tanh </item> + <!-- END: math module functions --> + </list> + + <list name="specialvars"> + <item> None </item> + <item> self </item> + </list> + + <list name="kigobjects"> + <item> AbstractLine </item> + <item> Angle </item> + <item> Arc </item> + <item> BogusObject </item> + <item> CartesianConic </item> + <item> Circle </item> + <item> Conic </item> + <item> ConicCartesianData </item> + <item> ConicPolarData </item> + <item> Coordinate </item> + <item> Cubic </item> + <item> CubicCartesianData </item> + <item> Curve </item> + <item> DoubleObject </item> + <item> IntObject </item> + <item> InvalidObject </item> + <item> Line </item> + <item> LineData </item> + <item> Object </item> + <item> ObjectType </item> + <item> Point </item> + <item> PolarConic </item> + <item> Ray </item> + <item> Segment </item> + <item> StringObject </item> + <item> TestResultObject </item> + <item> Transformation </item> + <item> Vector </item> + </list> + + <list name="mathconsts"> + <item> e </item> + <item> pi </item> + </list> + + <contexts> + <context name="Normal" attribute="Normal Text" lineEndContext="#stay"> + <keyword attribute="Preprocessor" String="prep" context="#stay"/> + <keyword attribute="Keyword" String="statements" context="#stay"/> + <keyword attribute="Builtin Function" String="builtinfuncs" context="#stay"/> + <keyword attribute="Special Variable" String="specialvars" context="#stay"/> + <keyword attribute="Kig Object" String="kigobjects" context="#stay" /> + <keyword attribute="Math Constants" String="mathconsts" context="#stay" /> + <RegExpr attribute="Normal" String="[a-zA-Z_][a-zA-Z_0-9]+" context="#stay"/> + + <RegExpr attribute="Complex" String=" ((([0-9]*\.[0-9]+|[0-9]+\.)|([0-9]+|([0-9]*\.[0-9]+|[0-9]+\.))[eE](\+|-)?[0-9]+)|[0-9]+)[jJ]" context="#stay"/> + <RegExpr attribute="Float" String="([0-9]+\.[0-9]*|\.[0-9]+)([eE][0-9]+)?" context="#stay"/> + <RegExpr attribute="Int" String="([1-9][0-9]*([eE][0-9]+)?|0)" context="#stay"/> + <RegExpr attribute="Long" String="[1-9][0-9]*([eE][0-9.]+)?[Ll]" context="#stay"/> + <RegExpr attribute="Hex" String="0[Xx][0-9a-fA-F]+" context="#stay"/> + <RegExpr attribute="Octal" String="0[1-9][0-9]*" context="#stay"/> + + <RegExpr attribute="Raw String" String="[rR]'" context="Raw A-string"/> + <RegExpr attribute="Raw String" String="[rR]"" context="Raw Q-string"/> + + <RegExpr attribute="Comment" String="#.*$" context="#stay"/> + <RegExpr attribute="Comment" String="^\s*'''" context="Tripple A-comment"/> + <RegExpr attribute="Comment" String="^\s*"""" context="Tripple Q-comment"/> + + <StringDetect attribute="String" String="'''" context="Tripple A-string"/> + <StringDetect attribute="String" String=""""" context="Tripple Q-string"/> + <DetectChar attribute="String" char="'" context="Single A-string"/> + <DetectChar attribute="String" char=""" context="Single Q-string"/> + + <RegExpr attribute="Operator" String="[+*/\(\)%\|\[\]\{\}:=;\!<>!^&~-]" context="#stay"/> + + </context> + + <context name="Tripple A-comment" attribute="Comment" lineEndContext="#stay"> + <HlCChar attribute="Comment" context="#stay"/> + <RegExpr attribute="Comment" String="'''" context="#pop"/> + </context> + + <context name="Tripple Q-comment" attribute="Comment" lineEndContext="#stay"> + <HlCChar attribute="Comment" context="#stay"/> + <RegExpr attribute="Comment" String=""""" context="#pop"/> + </context> + + <context name="Tripple A-string" attribute="String" lineEndContext="#stay"> + <HlCChar attribute="String" context="#stay"/> + <RegExpr attribute="Operator" String="%[a-zA-Z]" context="#stay"/> + <RegExpr attribute="String" String="'''" context="#pop"/> + </context> + + <context name="Tripple Q-string" attribute="String" lineEndContext="#stay"> + <HlCStringChar attribute="String" context="#stay"/> + <RegExpr attribute="Operator" String="%[a-zA-Z]" context="#stay"/> + <RegExpr attribute="String" String=""""" context="#pop"/> + </context> + + <context name="Single A-comment" attribute="Comment" lineEndContext="#stay"> + <HlCStringChar attribute="Comment" context="#stay"/> + <DetectChar attribute="Comment" char="'" context="#pop"/> + </context> + + <context name="Single Q-comment" attribute="Comment" lineEndContext="#stay"> + <HlCStringChar attribute="Comment" context="#stay"/> + <DetectChar attribute="Comment" char=""" context="#pop"/> + </context> + + <context name="Single A-string" attribute="String" lineEndContext="#stay"> + <HlCStringChar attribute="String" context="#stay"/> + <RegExpr attribute="Operator" String="%[a-zA-Z]" context="#stay"/> + <DetectChar attribute="String" char="'" context="#pop"/> + </context> + + <context name="Single Q-string" attribute="String" lineEndContext="#stay"> + <HlCStringChar attribute="String" context="#stay"/> + <RegExpr attribute="Operator" String="%[a-zA-Z]" context="#stay"/> + <DetectChar attribute="String" char=""" context="#pop"/> + </context> + + <context name="Raw A-string" attribute="Raw String" lineEndContext="#stay"> + <HlCStringChar attribute="Raw String" context="#stay"/> + <DetectChar attribute="Raw String" char="'" context="#pop"/> + </context> + + <context name="Raw Q-string" attribute="Raw String" lineEndContext="#stay"> + <HlCStringChar attribute="Raw String" context="#stay"/> + <DetectChar attribute="Raw String" char=""" context="#pop"/> + </context> + + + </contexts> + <itemDatas> + <itemData name="Normal Text" defStyleNum="dsNormal"/> + <itemData name="Operator" defStyleNum="dsChar"/> + <itemData name="Keyword" defStyleNum="dsKeyword"/> + <itemData name="Builtin Function" defStyleNum="dsDataType"/> + <itemData name="Kig Object" defStyleNum="dsKeyword" color="#000080" selColor="#ffffff" bold="0" italic="0"/> + <itemData name="Math Constants" defStyleNum="dsKeyword" color="#008000" selColor="#00FF00" bold="0" italic="0"/> + <itemData name="Special Variable" defStyleNum="dsOthers"/> + <itemData name="Preprocessor" defStyleNum="dsChar"/> + <itemData name="Long" defStyleNum="dsOthers"/> + <itemData name="Float" defStyleNum="dsFloat"/> + <itemData name="Int" defStyleNum="dsDecVal"/> + <itemData name="Hex" defStyleNum="dsOthers"/> + <itemData name="Octal" defStyleNum="dsOthers"/> + <itemData name="Complex" defStyleNum="dsOthers"/> + <itemData name="Comment" defStyleNum="dsComment"/> + <itemData name="String" defStyleNum="dsString"/> + <itemData name="Raw String" defStyleNum="dsString"/> + </itemDatas> + </highlighting> + <general> +<!-- <folding indentationsensitive="1" /> --> + <comments> + <comment name="singleLine" start="#" /> + </comments> + <keywords casesensitive="1" /> + </general> +</language> diff --git a/kig/scripting/python-scripting-api-dox-mainpage.dox b/kig/scripting/python-scripting-api-dox-mainpage.dox new file mode 100644 index 00000000..9bf6bbcb --- /dev/null +++ b/kig/scripting/python-scripting-api-dox-mainpage.dox @@ -0,0 +1,26 @@ +/** \mainpage Kig Python Scripting API Documentation + * \section Introduction + * This is the documentation of the Kig Python Scripting API. It is + * intended as a reference for people using the Kig Python Scripting + * System. The Documentation is generated from the C++ sources for + * the classes, and unfortunately, this means that the documentation + * uses a C++ syntax. However, if you ignore this fact, and simply + * look at the API as a set of classes with member and static + * functions, then you should be fine. + * + * \section The API + * This API is mostly meant to be used from user-defined calc() + * functions. In these functions, it is necessary to + * \li access the information of the argument \ref ObjectImp's + * \li construct and return a new ObjectImp + * + * Both uses require the ObjectImp API to be exported to Python, and + * this is the major part of this API. Some more classes are also + * exported, but mostly because they are used from one of the + * ObjectImp's APIs. + * + * \section Links + * + * Next suggested reading is the + * \ref ObjectImp "documentation of the ObjectImp class". + */ diff --git a/kig/scripting/python_scripter.cc b/kig/scripting/python_scripter.cc new file mode 100644 index 00000000..7e833d5c --- /dev/null +++ b/kig/scripting/python_scripter.cc @@ -0,0 +1,578 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "python_scripter.h" + +#include <iostream> +#include <string> +#include <Python.h> +#include <boost/python.hpp> +#include <boost/mpl/bool.hpp> + +#include "../misc/common.h" +#include "../misc/coordinate.h" +#include "../misc/cubic-common.h" +#include "../misc/kigtransform.h" +#include "../objects/bogus_imp.h" +#include "../objects/common.h" +#include "../objects/circle_imp.h" +#include "../objects/cubic_imp.h" +#include "../objects/line_imp.h" +#include "../objects/other_imp.h" +#include "../objects/point_imp.h" + +using namespace boost::python; + +BOOST_PYTHON_MODULE_INIT( kig ) +{ + class_<Coordinate>( "Coordinate" ) + .def( init<double, double>() ) + .def( init<const Coordinate&>() ) + .def( "invalidCoord", &Coordinate::invalidCoord ) + .staticmethod( "invalidCoord" ) + .def( "valid", &Coordinate::valid ) + .def( "distance", &Coordinate::distance ) + .def( "length", &Coordinate::length ) + .def( "squareLength", &Coordinate::squareLength ) + .def( "orthogonal", &Coordinate::orthogonal ) + .def( "round", &Coordinate::round ) + .def( "normalize", &Coordinate::normalize ) + .def( -self ) +// .def( self = self ) + .def( self += self ) + .def( self -= self ) + .def( self *= other<double>() ) + .def( self *= other<int>() ) + .def( self /= other<double>() ) + .def( self / other<double>() ) + .def( self + self ) + .def( self - self ) + .def( self * other<double>() ) + .def( other<double>() * self ) + .def( self * self ) + .def_readwrite( "x", &Coordinate::x ) + .def_readwrite( "y", &Coordinate::y ) + ; + + class_<LineData>( "LineData" ) + .def( init<Coordinate, Coordinate>() ) + .def( "dir", &LineData::dir ) + .def( "length", &LineData::length ) + .def( "isParallelTo", &LineData::isParallelTo ) + .def_readwrite( "a", &LineData::a ) + .def_readwrite( "b", &LineData::b ) + ; + + // we need this cause Transformation::apply is overloaded and + // otherwise using Transformation::apply would be ambiguous.. + const Coordinate (Transformation::*transformapplyfunc)( const Coordinate& ) const = &Transformation::apply; + class_<Transformation>( "Transformation", no_init ) + .def( "apply", transformapplyfunc ) + .def( "isHomothetic", &Transformation::isHomothetic ) + .def( "inverse", &Transformation::inverse ) + .def( "identity", &Transformation::identity ) + .def( "translation", &Transformation::translation ) + .def( "rotation", &Transformation::rotation ) + .def( "pointReflection", &Transformation::pointReflection ) + .def( "lineReflection", &Transformation::lineReflection ) + .def( "castShadow", &Transformation::castShadow ) + .def( "projectiveRotation", &Transformation::projectiveRotation ) + .def( "scalingOverPoint", &Transformation::scalingOverPoint ) + .def( "scalingOverLine", &Transformation::scalingOverLine ) + .def( self * self ) + .def( self == self ) + .staticmethod( "identity" ) + .staticmethod( "translation" ) + .staticmethod( "rotation" ) + .staticmethod( "pointReflection" ) + .staticmethod( "lineReflection" ) + .staticmethod( "castShadow" ) + .staticmethod( "projectiveRotation" ) + .staticmethod( "scalingOverPoint" ) + .staticmethod( "scalingOverLine" ) + ; + + class_<ObjectImpType, boost::noncopyable>( "ObjectType", no_init ) + .def( "fromInternalName", &ObjectImpType::typeFromInternalName, + return_value_policy<reference_existing_object>() ) + .staticmethod( "fromInternalName" ) + .def( "inherits", &ObjectImpType::inherits ) + .def( "internalName", &ObjectImpType::internalName ) + .def( "translatedName", &ObjectImpType::translatedName ) + .def( "selectStatement", &ObjectImpType::selectStatement ) + .def( "removeAStatement", &ObjectImpType::removeAStatement ) + .def( "addAStatement", &ObjectImpType::addAStatement ) + .def( "moveAStatement", &ObjectImpType::moveAStatement ) + .def( "attachToThisStatement", &ObjectImpType::attachToThisStatement ) + ; + + class_<ObjectImp, boost::noncopyable>( "Object", no_init ) + .def( "stype", &ObjectImp::stype, + return_value_policy<reference_existing_object>() ) + .staticmethod( "stype" ) + .def( "inherits", &ObjectImp::inherits ) + .def( "transform", &ObjectImp::transform, + return_value_policy<manage_new_object>() ) + .def( "valid", &ObjectImp::valid ) + .def( "copy", &ObjectImp::copy, + return_value_policy<manage_new_object>() ) + .def( "equals", &ObjectImp::equals ) + ; + + class_<CurveImp, bases<ObjectImp>, boost::noncopyable>( "Curve", no_init ) + .def( "stype", &CurveImp::stype, + return_value_policy<reference_existing_object>() ) + .staticmethod( "stype" ) +// .def( "getParam", &CurveImp::getParam ) +// .def( "getPoint", &CurveImp::getPoint ); + ; + class_<PointImp, bases<ObjectImp> >( "Point", init<Coordinate>() ) + .def( "stype", &PointImp::stype, + return_value_policy<reference_existing_object>() ) + .staticmethod( "stype" ) + .def( "coordinate", &PointImp::coordinate, + return_internal_reference<1>() ) + .def( "setCoordinate", &PointImp::setCoordinate ) + ; + + class_<AbstractLineImp, bases<CurveImp>, boost::noncopyable >( "AbstractLine", no_init ) + .def( "stype", &AbstractLineImp::stype, + return_value_policy<reference_existing_object>() ) + .staticmethod( "stype" ) + .def( "slope", &AbstractLineImp::slope ) + .def( "equationString", &AbstractLineImp::equationString ) + .def( "data", &AbstractLineImp::data ) + ; + + class_<SegmentImp, bases<AbstractLineImp> >( "Segment", init<Coordinate, Coordinate>() ) + .def( "stype", &SegmentImp::stype, + return_value_policy<reference_existing_object>() ) + .staticmethod( "stype" ) + .def( init<LineData>() ) + .def( "length", &SegmentImp::length ) + ; + + class_<RayImp, bases<AbstractLineImp> >( "Ray", init<Coordinate, Coordinate>() ) + .def( "stype", &RayImp::stype, + return_value_policy<reference_existing_object>() ) + .staticmethod( "stype" ) + .def( init<LineData>() ) + ; + + class_<LineImp, bases<AbstractLineImp> >( "Line", init<Coordinate, Coordinate>() ) + .def( "stype", &LineImp::stype, + return_value_policy<reference_existing_object>() ) + .staticmethod( "stype" ) + .def( init<LineData>() ) + ; + + class_<ConicCartesianData>( "ConicCartesianData", init<double,double,double,double,double,double>() ) + .def( init<ConicPolarData>() ) + .def( "invalidData", &ConicCartesianData::invalidData ) + .staticmethod( "invalidData" ) + .def( "valid", &ConicCartesianData::valid ) +// .def( init<double[6]>() ) +// .def_readwrite( "coeffs", &ConicCartesianData::coeffs ) + ; + + class_<ConicPolarData>( "ConicPolarData", init<Coordinate, double, double, double>() ) + .def( init<ConicCartesianData>() ) + .def_readwrite( "focus1", &ConicPolarData::focus1 ) + .def_readwrite( "pdimen", &ConicPolarData::pdimen ) + .def_readwrite( "ecostheta0", &ConicPolarData::ecostheta0 ) + .def_readwrite( "esintheta0", &ConicPolarData::esintheta0 ) + ; + + class_<ConicImp, bases<CurveImp>, boost::noncopyable >( "Conic", no_init ) + .def( "stype", &ConicImp::stype, + return_value_policy<reference_existing_object>() ) + .staticmethod( "stype" ) + .def( "conicType", &ConicImp::conicType ) +// .def( "conicTypeString", &ConicImp::conicTypeString ) +// .def( "cartesianEquationString", &ConicImp::cartesianEquationString ) +// .def( "polarEquationString", &ConicImp::polarEquationString ) + .def( "cartesianData", &ConicImp::cartesianData ) + .def( "polarData", &ConicImp::polarData ) + .def( "focus1", &ConicImp::focus1 ) + .def( "focus2", &ConicImp::focus2 ) + ; + + class_<ConicImpCart, bases<ConicImp> >( "CartesianConic", init<ConicCartesianData>() ) + ; + class_<ConicImpPolar, bases<ConicImp> >( "PolarConic", init<ConicPolarData>() ) + ; + + class_<CircleImp, bases<ConicImp> >( "Circle", init<Coordinate, double>() ) + .def( "stype", &CircleImp::stype, + return_value_policy<reference_existing_object>() ) + .staticmethod( "stype" ) + .def( "center", &CircleImp::center ) + .def( "radius", &CircleImp::radius ) + .def( "squareRadius", &CircleImp::squareRadius ) + .def( "surface", &CircleImp::surface ) + .def( "circumference", &CircleImp::circumference ) + ; + + class_<VectorImp, bases<CurveImp> >( "Vector", init<Coordinate, Coordinate>() ) + .def( "stype", &VectorImp::stype, + return_value_policy<reference_existing_object>() ) + .staticmethod( "stype" ) + .def( "length", &VectorImp::length ) + .def( "dir", &VectorImp::dir ) + .def( "data", &VectorImp::data ) + ; + + class_<AngleImp, bases<ObjectImp> >( "Angle", init<Coordinate, double, double>() ) + .def( "stype", &AngleImp::stype, + return_value_policy<reference_existing_object>() ) + .staticmethod( "stype" ) + .def( "size", &AngleImp::size ) + .def( "point", &AngleImp::point ) + .def( "startAngle", &AngleImp::startAngle ) + .def( "angle", &AngleImp::angle ) + ; + + class_<ArcImp, bases<ObjectImp> >( "Arc", init<Coordinate, double, double, double>() ) + .def( "stype", &ArcImp::stype, + return_value_policy<reference_existing_object>() ) + .staticmethod( "stype" ) + .def( "startAngle", &ArcImp::startAngle ) + .def( "angle", &ArcImp::angle ) + .def( "radius", &ArcImp::radius ) + .def( "center", &ArcImp::center ) + .def( "firstEndPoint", &ArcImp::firstEndPoint ) + .def( "secondEndPoint", &ArcImp::secondEndPoint ) + .def( "sectorSurface", &ArcImp::sectorSurface ) + ; + + class_<BogusImp, bases<ObjectImp>, boost::noncopyable >( "BogusObject", no_init ) + .def( "stype", &BogusImp::stype, + return_value_policy<reference_existing_object>() ) + .staticmethod( "stype" ) + ; + + class_<InvalidImp, bases<BogusImp> >( "InvalidObject", init<>() ) + .def( "stype", &InvalidImp::stype, + return_value_policy<reference_existing_object>() ) + .staticmethod( "stype" ) + ; + + class_<DoubleImp, bases<BogusImp> >( "DoubleObject", init<double>() ) + .def( "stype", &DoubleImp::stype, + return_value_policy<reference_existing_object>() ) + .staticmethod( "stype" ) + .def( "data", &DoubleImp::data ) + .def( "setData", &DoubleImp::setData ) + ; + + class_<IntImp, bases<BogusImp> >( "IntObject", init<int>() ) + .def( "stype", &IntImp::stype, + return_value_policy<reference_existing_object>() ) + .staticmethod( "stype" ) + .def( "data", &IntImp::data ) + .def( "setData", &IntImp::setData ) + ; + + class_<StringImp, bases<BogusImp> >( "StringObject", no_init ) + .def( "stype", &StringImp::stype, + return_value_policy<reference_existing_object>() ) + .staticmethod( "stype" ) +// .def( "data", &StringImp::data ) +// .def( "setData", &StringImp::setData ) + ; + + class_<TestResultImp, bases<BogusImp> >( "TestResultObject", no_init ) + .def( "stype", &TestResultImp::stype, + return_value_policy<reference_existing_object>() ) + .staticmethod( "stype" ) +// .def( "data", &TestResultImp::data ) + ; + +// class_<TextImp, bases<ObjectImp> >( "Text", init<string, Coordinate, bool>() ) +// .def( "stype", &TextImp::stype, +// return_value_policy<reference_existing_object>() ) +// .staticmethod( "stype" ) +// .def( "text", &TextImp::text ) +// .def( "coordinate", &TextImp::coordinate ) +// .def( "hasFrame", &TextImp::hasFrame ) +// ; + + class_<CubicCartesianData>( "CubicCartesianData", init<double,double,double,double,double,double,double,double,double,double>() ) + .def( "invalidData", &CubicCartesianData::invalidData ) + .staticmethod( "invalidData" ) + .def( "valid", &CubicCartesianData::valid ) +// .def( init<double[10]>() ) +// .def_readwrite( "coeffs", &CubicCartesianData::coeffs ) + ; + + class_<CubicImp, bases<CurveImp> >( "Cubic", init<CubicCartesianData>() ) + .def( "stype", &CubicImp::stype, + return_value_policy<reference_existing_object>() ) + .staticmethod( "stype" ) + .def( "data", &CubicImp::data ) + ; + +} + +PythonScripter* PythonScripter::instance() +{ + static PythonScripter t; + return &t; +} + +class PythonScripter::Private +{ +public: + dict mainnamespace; +}; + +// allocates a new string using new [], and copies contents into it.. +static char* newstring( const char* contents ) +{ + char* ret = new char[strlen( contents ) + 1]; + strcpy( ret, contents ); + return ret; +} + +PythonScripter::PythonScripter() +{ + d = new Private; + + // tell the python interpreter about our API.. + + // the newstring stuff is to prevent warnings about conversion from + // const char* to char*.. + char* s = newstring( "kig" ); + PyImport_AppendInittab( s, initkig ); + // we can't delete this yet, since python keeps a pointer to it.. + // This means we have a small but harmless memory leak here, but it + // doesn't hurt at all, since it could only be freed at the end of + // the program, at which time it is freed by the system anyway if we + // don't do it.. + //delete [] s; + + Py_Initialize(); + + s = newstring( "import math; from math import *;" ); + PyRun_SimpleString( s ); + delete [] s; + s = newstring( "import kig; from kig import *;" ); + PyRun_SimpleString( s ); + delete [] s; + s = newstring( "import traceback;" ); + PyRun_SimpleString( s ); + delete [] s; + + // find the main namespace.. + + s = newstring( "__main__" ); + handle<> main_module( borrowed( PyImport_AddModule( s ) ) ); + delete [] s; + + handle<> mnh(borrowed( PyModule_GetDict(main_module.get()) )); + d->mainnamespace = extract<dict>( mnh.get() ); +} + +PythonScripter::~PythonScripter() +{ + PyErr_Clear(); + Py_Finalize(); + delete d; +} + +class CompiledPythonScript::Private +{ +public: + int ref; + object calcfunc; + // TODO +// object movefunc; +}; + +ObjectImp* CompiledPythonScript::calc( const Args& args, const KigDocument& ) +{ + return PythonScripter::instance()->calc( *this, args ); +} + +CompiledPythonScript::~CompiledPythonScript() +{ + --d->ref; + if ( d->ref == 0 ) + delete d; +} + +CompiledPythonScript::CompiledPythonScript( Private* ind ) + : d( ind ) +{ + ++d->ref; +} + +CompiledPythonScript PythonScripter::compile( const char* code ) +{ + clearErrors(); + dict retdict; + bool error = false; + try + { + (void) PyRun_String( const_cast<char*>( code ), Py_file_input, + d->mainnamespace.ptr(), retdict.ptr() ); + } + catch( ... ) + { + error = true; + }; + error |= static_cast<bool>( PyErr_Occurred() ); + if ( error ) + { + saveErrors(); + retdict.clear(); + } + + // debugging stuff, removed. +// std::string dictstring = extract<std::string>( str( retdict ) ); + + CompiledPythonScript::Private* ret = new CompiledPythonScript::Private; + ret->ref = 0; + ret->calcfunc = retdict.get( "calc" ); + return CompiledPythonScript( ret ); +} + +CompiledPythonScript::CompiledPythonScript( const CompiledPythonScript& s ) + : d( s.d ) +{ + ++d->ref; +} + +std::string PythonScripter::lastErrorExceptionType() const +{ + return lastexceptiontype; +} + +std::string PythonScripter::lastErrorExceptionValue() const +{ + return lastexceptionvalue; +} + +std::string PythonScripter::lastErrorExceptionTraceback() const +{ + return lastexceptiontraceback; +} + +ObjectImp* PythonScripter::calc( CompiledPythonScript& script, const Args& args ) +{ + clearErrors(); + object calcfunc = script.d->calcfunc; + try + { + std::vector<object> objectvect; + objectvect.reserve( args.size() ); + + for ( int i = 0; i < (int) args.size(); ++i ) + { + object o( boost::ref( *args[i] ) ); + objectvect.push_back( o ); + } + + handle<> argstuph( PyTuple_New( args.size() ) ); + for ( int i = 0; i < (int) objectvect.size(); ++i ) + { + PyTuple_SetItem( argstuph.get(), i, (objectvect.begin() +i)->ptr() ); + }; + tuple argstup( argstuph ); + + handle<> reth( PyEval_CallObject( calcfunc.ptr(), argstup.ptr() ) ); +// object resulto = calcfunc( argstup ); +// handle<> reth( PyEval_CallObject( calcfunc.ptr(), args ) ); + object resulto( reth ); + + extract<ObjectImp&> result( resulto ); + if( ! result.check() ) return new InvalidImp; + else + { + ObjectImp& ret = result(); + return ret.copy(); + }; + } + catch( ... ) + { + saveErrors(); + + return new InvalidImp; + }; +} + +void PythonScripter::saveErrors() +{ + erroroccurred = true; + PyObject* poexctype; + PyObject* poexcvalue; + PyObject* poexctraceback; + PyErr_Fetch( &poexctype, &poexcvalue, &poexctraceback ); + handle<> exctypeh( poexctype ); + handle<> excvalueh( poexcvalue ); + + object exctype( exctypeh ); + object excvalue( excvalueh ); + object exctraceback; + if ( poexctraceback ) + { + handle<> exctracebackh( poexctraceback ); + exctraceback = object( exctracebackh ); + } + + lastexceptiontype = extract<std::string>( str( exctype ) )(); + lastexceptionvalue = extract<std::string>( str( excvalue ) )(); + + object printexcfunc = d->mainnamespace[ "traceback" ].attr( "format_exception" ); + + list tracebacklist = extract<list>( printexcfunc( exctype, excvalue, exctraceback ) )(); + str tracebackstr( "" ); + while ( true ) + { + try { + str s = extract<str>( tracebacklist.pop() ); + tracebackstr += s; + } + catch( ... ) + { + break; + } + } + + lastexceptiontraceback = extract<std::string>( tracebackstr )(); + PyErr_Clear(); +} + +void PythonScripter::clearErrors() +{ + PyErr_Clear(); + lastexceptiontype.clear(); + lastexceptionvalue.clear(); + lastexceptiontraceback.clear(); + erroroccurred = false; +} + +bool CompiledPythonScript::valid() +{ + return !!d->calcfunc; +} + +bool PythonScripter::errorOccurred() const +{ + return erroroccurred; +} + diff --git a/kig/scripting/python_scripter.h b/kig/scripting/python_scripter.h new file mode 100644 index 00000000..464b5d3e --- /dev/null +++ b/kig/scripting/python_scripter.h @@ -0,0 +1,69 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_SCRIPTING_PYTHON_SCRIPTER_H +#define KIG_SCRIPTING_PYTHON_SCRIPTER_H + +#include "../objects/common.h" + +#include <string> + +class KigDocument; +class ObjectImp; + +class CompiledPythonScript +{ + friend class PythonScripter; + class Private; + Private* const d; + CompiledPythonScript( Private* ); +public: + CompiledPythonScript( const CompiledPythonScript& s ); + ~CompiledPythonScript(); + ObjectImp* calc( const Args& a, const KigDocument& doc ); + + bool valid(); +}; + +class PythonScripter +{ + friend class CompiledPythonScript; + class Private; + Private* d; + PythonScripter(); + ~PythonScripter(); + + void clearErrors(); + void saveErrors(); + + bool erroroccurred; + std::string lastexceptiontype; + std::string lastexceptionvalue; + std::string lastexceptiontraceback; +public: + static PythonScripter* instance(); + + bool errorOccurred() const; + std::string lastErrorExceptionType() const; + std::string lastErrorExceptionValue() const; + std::string lastErrorExceptionTraceback() const; + + CompiledPythonScript compile( const char* code ); + ObjectImp* calc( CompiledPythonScript& script, const Args& args ); +}; + +#endif diff --git a/kig/scripting/python_type.cc b/kig/scripting/python_type.cc new file mode 100644 index 00000000..0180e828 --- /dev/null +++ b/kig/scripting/python_type.cc @@ -0,0 +1,195 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "python_type.h" + +#include "python_scripter.h" + +#include "../objects/object_imp.h" +#include "../objects/bogus_imp.h" + +class PythonCompiledScriptImp + : public BogusImp +{ + mutable CompiledPythonScript mscript; +public: + typedef BogusImp Parent; + static const ObjectImpType* stype(); + const ObjectImpType* type() const; + + PythonCompiledScriptImp( const CompiledPythonScript& s ); + + void visit( ObjectImpVisitor* vtor ) const; + ObjectImp* copy() const; + bool equals( const ObjectImp& rhs ) const; + + bool isCache() const; + + CompiledPythonScript& data() const { return mscript; }; +}; + +PythonCompiledScriptImp::PythonCompiledScriptImp( const CompiledPythonScript& s ) + : BogusImp(), mscript( s ) +{ + +} + +const ObjectImpType* PythonCompiledScriptImp::stype() +{ + static const ObjectImpType t( BogusImp::stype(), "python-compiled-script-imp", + 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + return &t; +} + +const ObjectImpType* PythonCompiledScriptImp::type() const +{ + return PythonCompiledScriptImp::stype(); +} + +void PythonCompiledScriptImp::visit( ObjectImpVisitor* ) const +{ + // TODO ? +} + +ObjectImp* PythonCompiledScriptImp::copy() const +{ + return new PythonCompiledScriptImp( mscript ); +} + +bool PythonCompiledScriptImp::equals( const ObjectImp& ) const +{ + // problem ? + return true; +} + +bool PythonCompiledScriptImp::isCache() const +{ + return true; +} + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( PythonCompileType ) + +PythonCompileType::PythonCompileType() + : ObjectType( "PythonCompileType" ) +{ +} + +PythonCompileType::~PythonCompileType() +{ +} + +const PythonCompileType* PythonCompileType::instance() +{ + static const PythonCompileType t; + return &t; +} + +const ObjectImpType* PythonCompileType::impRequirement( const ObjectImp*, const Args& ) const +{ + return StringImp::stype(); +} + +const ObjectImpType* PythonCompileType::resultId() const +{ + return PythonCompiledScriptImp::stype(); +} + +ObjectImp* PythonCompileType::calc( const Args& parents, const KigDocument& ) const +{ + assert( parents.size() == 1 ); + if ( !parents[0]->inherits( StringImp::stype() ) ) return new InvalidImp; + + const StringImp* si = static_cast<const StringImp*>( parents[0] ); + QString s = si->data(); + + CompiledPythonScript cs = PythonScripter::instance()->compile( s.latin1() ); + + if ( cs.valid() ) + return new PythonCompiledScriptImp( cs ); + else + return new InvalidImp(); +} + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( PythonExecuteType ) + +PythonExecuteType::PythonExecuteType() + : ObjectType( "PythonExecuteType" ) +{ +} + +PythonExecuteType::~PythonExecuteType() +{ +} + +const PythonExecuteType* PythonExecuteType::instance() +{ + static const PythonExecuteType t; + return &t; +} + +ObjectImp* PythonExecuteType::calc( const Args& parents, const KigDocument& d ) const +{ + assert( parents.size() >= 1 ); + if( !parents[0]->inherits( PythonCompiledScriptImp::stype() ) ) return new InvalidImp; + + CompiledPythonScript& script = static_cast<const PythonCompiledScriptImp*>( parents[0] )->data(); + + Args args( parents.begin() + 1, parents.end() ); + return script.calc( args, d ); +} + +const ObjectImpType* PythonExecuteType::impRequirement( const ObjectImp* o, const Args& parents ) const +{ + if ( o == parents[0] ) return PythonCompiledScriptImp::stype(); + else return ObjectImp::stype(); +} + +const ObjectImpType* PythonExecuteType::resultId() const +{ + return ObjectImp::stype(); +} + +std::vector<ObjectCalcer*> PythonCompileType::sortArgs( const std::vector<ObjectCalcer*>& args ) const +{ + return args; +} + +Args PythonCompileType::sortArgs( const Args& args ) const +{ + return args; +} + +std::vector<ObjectCalcer*> PythonExecuteType::sortArgs( const std::vector<ObjectCalcer*>& args ) const +{ + return args; +} + +Args PythonExecuteType::sortArgs( const Args& args ) const +{ + return args; +} + +bool PythonCompileType::isDefinedOnOrThrough( const ObjectImp*, const Args& ) const +{ + return false; +} + +bool PythonExecuteType::isDefinedOnOrThrough( const ObjectImp*, const Args& ) const +{ + return false; +} + diff --git a/kig/scripting/python_type.h b/kig/scripting/python_type.h new file mode 100644 index 00000000..1e76b0b8 --- /dev/null +++ b/kig/scripting/python_type.h @@ -0,0 +1,63 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_SCRIPTING_PYTHON_TYPE_H +#define KIG_SCRIPTING_PYTHON_TYPE_H + +#include "../objects/object_type.h" + +class PythonCompileType + : public ObjectType +{ + PythonCompileType(); + ~PythonCompileType(); +public: + static const PythonCompileType* instance(); + + ObjectImp* calc( const Args& parents, const KigDocument& d ) const; + + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + bool isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const; + const ObjectImpType* resultId() const; + + std::vector<ObjectCalcer*> sortArgs( const std::vector<ObjectCalcer*>& args ) const; + Args sortArgs( const Args& args ) const; +}; + +class PythonExecuteType + : public ObjectType +{ + PythonExecuteType(); + ~PythonExecuteType(); +public: + static const PythonExecuteType* instance(); + + ObjectImp* calc( const Args& parents, const KigDocument& d ) const; + + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + bool isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const; + const ObjectImpType* resultId() const; + + std::vector<ObjectCalcer*> sortArgs( const std::vector<ObjectCalcer*>& args ) const; + Args sortArgs( const Args& args ) const; + +// virtual QStringList specialActions() const; +// virtual void executeAction( int i, RealObject* o, KigDocument& d, KigWidget& w, +// NormalMode& m ) const; +}; + +#endif diff --git a/kig/scripting/script-common.cc b/kig/scripting/script-common.cc new file mode 100644 index 00000000..4bd2cbd8 --- /dev/null +++ b/kig/scripting/script-common.cc @@ -0,0 +1,95 @@ +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "script-common.h" + +#include <qstring.h> + +#include <kdebug.h> +#include <klocale.h> + +struct script_prop +{ + const char* fillCodeStatement; + const char* icon; + const char* highlightStyle; +}; + +static const script_prop scripts_properties[] = +{ + { I18N_NOOP( "Now fill in the code:" ), "shellscript", "" }, + { I18N_NOOP( "Now fill in the Python code:" ), "source_py", "Python-Kig" } +}; + +QString ScriptType::fillCodeStatement( ScriptType::Type type ) +{ + return i18n( scripts_properties[type].fillCodeStatement ); +} + +QString ScriptType::templateCode( ScriptType::Type type, std::list<ObjectHolder*> args ) +{ + if ( type == Python ) + { + QString tempcode = QString::fromLatin1( "def calc( " ); + bool firstarg = true; + QString temparg = i18n( "Note to translators: this should be a default " + "name for an argument in a Python function. The " + "default is \"arg%1\" which would become arg1, " + "arg2, etc. Give something which seems " + "appropriate for your language.", "arg%1" ); + + uint id = 1; + for ( std::list<ObjectHolder*>::const_iterator i = args.begin(); i != args.end(); ++i ) + { + if ( !firstarg ) tempcode += ", "; + else firstarg = false; + QString n = ( *i )->name(); + tempcode += n.isEmpty() ? temparg.arg( id ) : n; + id++; + }; + tempcode += + " ):\n" + "\t# Calculate whatever you want to show here, and return it.\n" + "\t# For example, to implement a mid point, you would put\n" + "\t# this code here:\n" + "\t#\treturn Point( ( arg1.coordinate() + arg2.coordinate() ) / 2 )\n" + "\t# Please refer to the manual for more information.\n" + "\n"; + return tempcode; + } + + kdDebug() << "No such script type: " << type << endl; + return ""; +} + +const char* ScriptType::icon( ScriptType::Type type ) +{ + return scripts_properties[type].icon; +} + +QString ScriptType::highlightStyle( ScriptType::Type type ) +{ + return QString( scripts_properties[type].highlightStyle ); +} + +ScriptType::Type ScriptType::intToType( int type ) +{ + if ( type == 1 ) + return Python; + + return Unknown; +} diff --git a/kig/scripting/script-common.h b/kig/scripting/script-common.h new file mode 100644 index 00000000..1c384453 --- /dev/null +++ b/kig/scripting/script-common.h @@ -0,0 +1,61 @@ +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_SCRIPTING_SCRIPT_COMMON_H +#define KIG_SCRIPTING_SCRIPT_COMMON_H + +#include <algorithm> +#include <list> + +#include "../objects/object_holder.h" + +class QString; + +class ScriptType +{ +public: + /** + * This enum represents all the script language types actually in + * Kig. The first type ( Unknown ) can be used if we don't want + * particular tunings for a script language. + */ + enum Type { Unknown = 0, Python = 1 }; + /** + * Returns an i18n'ed statement like 'Now fill in the code:' with + * the name of the script language. + */ + static QString fillCodeStatement( ScriptType::Type type ); + /** + * Returns a template code for a script language. + */ + static QString templateCode( ScriptType::Type type, std::list<ObjectHolder*> args ); + /** + * Returns the icon's name for a script language. + */ + static const char* icon( ScriptType::Type type ); + /** + * Returns the Kate highlight stytle name for a script language. + */ + static QString highlightStyle( ScriptType::Type type ); + /** + * Converts an int to a ScriptType::Type. Useful when reading script + * types from files. + */ + static ScriptType::Type intToType( int type ); +}; + +#endif diff --git a/kig/scripting/script_mode.cc b/kig/scripting/script_mode.cc new file mode 100644 index 00000000..000b99f8 --- /dev/null +++ b/kig/scripting/script_mode.cc @@ -0,0 +1,362 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "script_mode.h" + +#include "newscriptwizard.h" +#include "python_type.h" +#include "python_scripter.h" + +#include "../kig/kig_commands.h" +#include "../kig/kig_part.h" +#include "../kig/kig_view.h" +#include "../misc/calcpaths.h" +#include "../misc/kigpainter.h" +#include "../modes/dragrectmode.h" +#include "../objects/bogus_imp.h" +#include "../objects/object_imp.h" + +#include <qlabel.h> +#include <qpushbutton.h> + +#include <kcursor.h> +#include <kiconloader.h> +#include <kmessagebox.h> + +void ScriptModeBase::dragRect( const QPoint& p, KigWidget& w ) +{ + if ( mwawd != SelectingArgs ) return; + + DragRectMode dm( p, mdoc, w ); + mdoc.runMode( &dm ); + std::vector<ObjectHolder*> ret = dm.ret(); + + KigPainter pter( w.screenInfo(), &w.stillPix, mdoc.document() ); + if ( dm.needClear() ) + { + std::vector<ObjectHolder*> tmp( margs.begin(), margs.begin() ); + pter.drawObjects( tmp, false ); + margs.clear(); + } + + std::copy( ret.begin(), ret.end(), std::inserter( margs, margs.begin() ) ); + pter.drawObjects( ret, true ); + + w.updateCurPix( pter.overlay() ); + w.updateWidget(); +} + +void ScriptModeBase::leftClickedObject( ObjectHolder* o, const QPoint&, + KigWidget& w, bool ) +{ + std::list<ObjectHolder*>::iterator dup_o; + + if ( mwawd != SelectingArgs ) return; + + KigPainter pter( w.screenInfo(), &w.stillPix, mdoc.document() ); + + if ( (dup_o = std::find( margs.begin(), margs.end(), o )) != margs.end() ) + { + margs.erase( dup_o ); + pter.drawObject( o, false ); + } + else + { + margs.push_back( o ); + pter.drawObject( o, true ); + }; + w.updateCurPix( pter.overlay() ); + w.updateWidget(); +} + +void ScriptModeBase::mouseMoved( const std::vector<ObjectHolder*>& os, + const QPoint& pt, KigWidget& w, bool ) +{ + if ( mwawd != SelectingArgs ) return; + + w.updateCurPix(); + if ( os.empty() ) + { + w.setCursor( KCursor::arrowCursor() ); + mdoc.emitStatusBarText( 0 ); + w.updateWidget(); + } + else + { + // the cursor is over an object, show object type next to cursor + // and set statusbar text + + w.setCursor( KCursor::handCursor() ); + QString selectstat = os.front()->selectStatement(); + + // statusbar text + mdoc.emitStatusBarText( selectstat ); + KigPainter p( w.screenInfo(), &w.curPix, mdoc.document() ); + + // set the text next to the arrow cursor + QPoint point = pt; + point.setX(point.x()+15); + + p.drawTextStd( point, selectstat ); + w.updateWidget( p.overlay() ); + } +} + +ScriptModeBase::ScriptModeBase( KigPart& doc ) + : BaseMode( doc ), mwizard( 0 ), mpart( doc ), + mwawd( SelectingArgs ) +{ + mwizard = new NewScriptWizard( doc.widget(), this ); + + doc.redrawScreen(); +} + +ScriptModeBase::~ScriptModeBase() +{ +} + +void ScriptModeBase::killMode() +{ + mdoc.doneMode( this ); +} + +bool ScriptCreationMode::queryCancel() +{ + killMode(); + return true; +} + +void ScriptModeBase::argsPageEntered() +{ + mwawd = SelectingArgs; + mdoc.redrawScreen(); +} + +void ScriptModeBase::enableActions() +{ + KigMode::enableActions(); + // we don't enable any actions.. +} + +void ScriptModeBase::codePageEntered() +{ + if ( mwizard->text().isEmpty() ) + { + // insert template code.. + QString tempcode = ScriptType::templateCode( mtype, margs ); + mwizard->setText( tempcode ); + }; + mwizard->setFinishEnabled( mwizard->mpcode, true ); + mwawd = EnteringCode; + mdoc.redrawScreen(); +} + +void ScriptModeBase::redrawScreen( KigWidget* w ) +{ + std::vector<ObjectHolder*> sel; + if ( mwawd == SelectingArgs ) + sel = std::vector<ObjectHolder*>( margs.begin(), margs.end() ); + w->redrawScreen( sel ); + w->updateScrollBars(); +} + +bool ScriptCreationMode::queryFinish() +{ + std::vector<ObjectCalcer*> args; + + QString script = mwizard->text(); + args.push_back( new ObjectConstCalcer( new StringImp( script ) ) ); + + ObjectTypeCalcer* compiledscript = + new ObjectTypeCalcer( PythonCompileType::instance(), args ); + compiledscript->calc( mdoc.document() ); + + args.clear(); + args.push_back( compiledscript ); + for ( std::list<ObjectHolder*>::iterator i = margs.begin(); + i != margs.end(); ++i ) + args.push_back( ( *i )->calcer() ); + + ObjectTypeCalcer::shared_ptr reto = + new ObjectTypeCalcer( PythonExecuteType::instance(), args ); + reto->calc( mdoc.document() ); + + if ( reto->imp()->inherits( InvalidImp::stype() ) ) + { + PythonScripter* inst = PythonScripter::instance(); + QCString errtrace = inst->lastErrorExceptionTraceback().c_str(); + if ( inst->errorOccurred() ) + { + KMessageBox::detailedSorry( + mwizard, i18n( "The Python interpreter caught an error during the execution of your " + "script. Please fix the script and click the Finish button again." ), + i18n( "The Python Interpreter generated the following error output:\n%1").arg( errtrace ) ); + } + else + { + KMessageBox::sorry( + mwizard, i18n( "There seems to be an error in your script. The Python interpreter " + "reported no errors, but the script does not generate " + "a valid object. Please fix the script, and click the Finish button " + "again." ) ); + } + return false; + } + else + { + mdoc.addObject( new ObjectHolder( reto.get() ) ); + killMode(); + return true; + } +} + +void ScriptModeBase::midClicked( const QPoint&, KigWidget& ) +{ +} + +void ScriptModeBase::rightClicked( const std::vector<ObjectHolder*>&, + const QPoint&, KigWidget& ) +{ +} + +void ScriptModeBase::setScriptType( ScriptType::Type type ) +{ + mtype = type; + mwizard->setType( mtype ); + if ( mtype != ScriptType::Unknown ) + { + KIconLoader* il = mpart.instance()->iconLoader(); + mwizard->setIcon( il->loadIcon( ScriptType::icon( mtype ), KIcon::Small ) ); + } +} + +void ScriptModeBase::addArgs( const std::vector<ObjectHolder*>& obj, KigWidget& w ) +{ + KigPainter pter( w.screenInfo(), &w.stillPix, mdoc.document() ); + + std::copy( obj.begin(), obj.end(), std::inserter( margs, margs.begin() ) ); + pter.drawObjects( obj, true ); + + w.updateCurPix( pter.overlay() ); + w.updateWidget(); +} + +void ScriptModeBase::goToCodePage() +{ + mwizard->next(); +} + +ScriptCreationMode::ScriptCreationMode( KigPart& doc ) + : ScriptModeBase( doc ) +{ + mwizard->show(); +} + +ScriptCreationMode::~ScriptCreationMode() +{ +} + +ScriptEditMode::ScriptEditMode( ObjectTypeCalcer* exec_calc, KigPart& doc ) + : ScriptModeBase( doc ), mexecuted( exec_calc ) +{ + mwawd = EnteringCode; + + mexecargs = mexecuted->parents(); + assert( mexecargs.size() >= 1 ); + + mcompiledargs = mexecargs[0]->parents(); + assert( mcompiledargs.size() == 1 ); + + const ObjectImp* imp = static_cast<ObjectConstCalcer*>( mcompiledargs[0] )->imp(); + assert( dynamic_cast<const StringImp*>( imp ) ); + // save the original script text, in case the user modifies the text + // in the editor and aborts the editing + morigscript = static_cast<const StringImp*>( imp )->data(); + + mwizard->setCaption( i18n( "'Edit' is a verb", "Edit Script" ) ); + mwizard->setText( morigscript ); + mwizard->show(); + mwizard->next(); + mwizard->backButton()->setEnabled( false ); + mwizard->finishButton()->setEnabled( true ); +} + +ScriptEditMode::~ScriptEditMode() +{ +} + +bool ScriptEditMode::queryFinish() +{ + MonitorDataObjects mon( mcompiledargs ); + + static_cast<ObjectConstCalcer*>( mcompiledargs[0] )->switchImp( new StringImp( mwizard->text() ) ); + mexecargs[0]->calc( mpart.document() ); + + mexecuted->calc( mpart.document() ); + + mpart.redrawScreen(); + + KigCommand* comm = new KigCommand( mpart, i18n( "Edit Python Script" ) ); + mon.finish( comm ); + + if ( mexecuted->imp()->inherits( InvalidImp::stype() ) ) + { + PythonScripter* inst = PythonScripter::instance(); + QCString errtrace = inst->lastErrorExceptionTraceback().c_str(); + if ( inst->errorOccurred() ) + { + KMessageBox::detailedSorry( + mpart.widget(), i18n( "The Python interpreter caught an error during the execution of your " + "script. Please fix the script." ), + i18n( "The Python Interpreter generated the following error output:\n%1").arg( errtrace ) ); + } + else + { + KMessageBox::sorry( + mpart.widget(), i18n( "There seems to be an error in your script. The Python interpreter " + "reported no errors, but the script does not generate " + "a valid object. Please fix the script." ) ); + } + delete comm; + return false; + } + + mpart.history()->addCommand( comm ); + mpart.setModified( true ); + + killMode(); + return true; +} + +bool ScriptEditMode::queryCancel() +{ + // reverting the original script text + static_cast<ObjectConstCalcer*>( mcompiledargs[0] )->switchImp( new StringImp( morigscript ) ); + mexecargs[0]->calc( mpart.document() ); + + mexecuted->calc( mpart.document() ); + // paranoic check + assert( !mexecuted->imp()->inherits( InvalidImp::stype() ) ); + + mpart.redrawScreen(); + + // no need to further checks here, as the original script text is ok + + killMode(); + return true; +} + diff --git a/kig/scripting/script_mode.h b/kig/scripting/script_mode.h new file mode 100644 index 00000000..4cbfd737 --- /dev/null +++ b/kig/scripting/script_mode.h @@ -0,0 +1,119 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef KIG_SCRIPTING_SCRIPT_MODE_H +#define KIG_SCRIPTING_SCRIPT_MODE_H + +#include "script-common.h" + +#include "../modes/base_mode.h" + +#include <list> + +class NewScriptWizard; + +/** + * Base mode to interact with a script. + */ +class ScriptModeBase + : public BaseMode +{ +protected: + ScriptModeBase( KigPart& doc ); + +// mp: argument list is implemented as a std::list instead of std::set +// because otherwise the user loses the correct argument ordering (in +// case of more than one argument + std::list<ObjectHolder*> margs; + NewScriptWizard* mwizard; + + KigPart& mpart; + + enum WAWD { SelectingArgs, EnteringCode }; + WAWD mwawd; + +private: + ScriptType::Type mtype; + +public: + virtual ~ScriptModeBase(); + + void dragRect( const QPoint& p, KigWidget& w ); +// void dragObject( const Objects& os, const QPoint& pointClickedOn, KigWidget& w, bool ctrlOrShiftDown ); + void leftClickedObject( ObjectHolder* o, const QPoint& p, + KigWidget& w, bool actrlOrShiftDown ); + void mouseMoved( const std::vector<ObjectHolder*>& os, const QPoint& p, + KigWidget& w, bool shiftpressed ); + void midClicked( const QPoint&, KigWidget& ); + void rightClicked( const std::vector<ObjectHolder*>&, const QPoint&, KigWidget& ); + + void argsPageEntered(); + void codePageEntered(); + + virtual bool queryFinish() = 0; + virtual bool queryCancel() = 0; + + void redrawScreen( KigWidget* w ); + + void killMode(); + + void enableActions(); + + void setScriptType( ScriptType::Type type ); + + void addArgs( const std::vector<ObjectHolder*>& obj, KigWidget& w ); + + void goToCodePage(); + +}; + +/** + * Script mode to create a script. + */ +class ScriptCreationMode + : public ScriptModeBase +{ +public: + ScriptCreationMode( KigPart& doc ); + virtual ~ScriptCreationMode(); + + virtual bool queryFinish(); + virtual bool queryCancel(); +}; + +/** + * Script mode to edit an already-built script. + */ +class ScriptEditMode + : public ScriptModeBase +{ +private: + ObjectTypeCalcer* mexecuted; + std::vector<ObjectCalcer*> mexecargs; + std::vector<ObjectCalcer*> mcompiledargs; + + QString morigscript; + +public: + ScriptEditMode( ObjectTypeCalcer* exec_calc, KigPart& doc ); + virtual ~ScriptEditMode(); + + virtual bool queryFinish(); + virtual bool queryCancel(); +}; + +#endif |