ditaa-0.9/0000775000175000017500000000000011361663105010574 5ustar neoneoditaa-0.9/build/0000755000175000017500000000000011277077344011703 5ustar neoneoditaa-0.9/build/release.xml0000644000175000017500000001001511303054676014033 0ustar neoneo Creates the ditaa binary release files. ditaa-0.9/build/ejp-filter0000644000175000017500000000006611277077344013671 0ustar neoneoi org.stathissideris.ascii2image.text. e java. e sun. ditaa-0.9/COPYING0000644000175000017500000004313111277077356011644 0ustar neoneo GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 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. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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) year 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. , 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. ditaa-0.9/tests/0000755000175000017500000000000011277077344011746 5ustar neoneoditaa-0.9/tests/text/0000755000175000017500000000000011277502002012712 5ustar neoneoditaa-0.9/tests/text/bug14.txt0000644000175000017500000000022511277077344014414 0ustar neoneo+--+--------+ | | Test | | | +----- | | /---+ | | | | | |cPNK| | +--+----+---/ o Bullet point 1 o Point 2 ditaa-0.9/tests/text/simple_square01.txt0000644000175000017500000000005011277077344016500 0ustar neoneo+-----+ | | | | | | +-----+ ditaa-0.9/tests/text/bug11.txt0000644000175000017500000000001011277077344014401 0ustar neoneo--- ok!ditaa-0.9/tests/text/color_codes.txt0000644000175000017500000000034611277077344015771 0ustar neoneoColor codes /-------------+-------------\ |cRED RED |cBLU BLU | +-------------+-------------+ |cGRE GRE |cPNK PNK | +-------------+-------------+ |cBLK BLK |cYEL YEL | \-------------+-------------/ ditaa-0.9/tests/text/dak_orgstruktur_vs_be.ditaa.OutOfMemoryError.edit.txt0000644000175000017500000000021411277503252025333 0ustar neoneo/-------------------\ | +-----+ /----+ | | | | | | | +-->| | \---++---------+----/ | +-+-+ | | +---+ ditaa-0.9/tests/text/bug7.txt0000644000175000017500000000033011277077344014333 0ustar neoneo /-\ | | | | | | | | /-----------/ | \-----\ | | Bug 7 | |(fixed)| | | | \-----\ \-------------/ ditaa-0.9/tests/text/art7.txt0000644000175000017500000000015611277077344014352 0ustar neoneo +-+-+ +---+ | | | | | | | | +---+ | | | +-+-+ +--+--> | | | | | | ditaa-0.9/tests/text/dak_orgstruktur_vs_be.ditaa.OutOfMemoryError.3.txt0000644000175000017500000000100011277407340024543 0ustar neoneo /-----------------------\ |c33F +-----+ +-------+ |SZ |c85F | | c088 | | |KB0 +-->| KB1 | \----++-----+---+-------/ | | | | | | /-----+--------\ | | | | /---++----\ /----++---\ |c33F|c85F| |c85F|c33F| |SSt |KB0 | |KB0 |SSt | | +----+ +----+ | \---------/ \---------/ ditaa-0.9/tests/text/ditaa_bug2.txt0000644000175000017500000000114211277077344015472 0ustar neoneo Front-end : Back-end | Financiele | | Instellingen /----------+ | /-- ------\ : | Simyo | | | CDRator | | | Website |<---+--->\---------/ | \----------/ : ^ | | | | | v | /----------\ | /---------\ | | Simone | | |cYEL | | /----------\ | (IVR) |<---+--->| SMO :<---+--->| Interpay | \----------/ : | | : \----------/ | \---------/ | | | ditaa-0.9/tests/text/simple_U01.txt0000644000175000017500000000020511277077344015406 0ustar neoneo+-----+ +-----+ | | | | | | | | | +----+ | | | | | +----------------+ ditaa-0.9/tests/text/art8.txt0000644000175000017500000000013611277077344014351 0ustar neoneo AB / \ / \ / * BC / \ / \ / * ditaa-0.9/tests/text/art_text.txt0000644000175000017500000000126011277077344015324 0ustar neoneo/--------------------------\ | the the | | horizontal horizontal | | alignment alignment | | of of | | strings strings | | is is | | retained retained | | | | | | hhh | | MMM | | iii | | the example is clear| | | | o Bullet | | o points | | o are | | o recognised | | * and | | * handled | | * nicely c9D9| \--------------------------/ditaa-0.9/tests/text/dak_orgstruktur_vs_be.ditaa.OutOfMemoryError.4.txt0000644000175000017500000000100011277407340024544 0ustar neoneo /-----------------------\ |c33F +-----+ +-------+ |SZ |c85F +-->| c088 | | |KB0 | | KB1 | \----++-----+---+-------/ | | | | | | /-----+--------\ | | | | /---++----\ /----++---\ |c33F|c85F| |c85F|c33F| |SSt |KB0 | |KB0 |SSt | | +----+ +----+ | \---------/ \---------/ ditaa-0.9/tests/text/art20.txt0000644000175000017500000000153511303043544014410 0ustar neoneonew shapes: +-----+ +=----+ | {c} | | {c} | |test | | | +-----+ +-----+ +-----+ +=----+ | {mo}| | {mo}| |edge | | text| +-----+ +-----+ +-----+ +=----+ | {tr}| | {tr}| | | | | +-----+ +-----+ +----------------+ | {mo} | |edge | +----------------+ +-----+ +-------+ | {mo}| | {mo} | | | |edge | | | +-------+ | | | | | | | | | | |edge | +-----+ coffee thingy: +--*--+ --+{mo} +-\ | | | | | | +-----+ |{tr} | | | | | +-----+ ellipses +----------------+ | {o} | |edge | +----------------+ +-----+ +-------+ | {o} | | {o} | | | |edge | | | +-------+ | | | | | | | | | | |edge | +-----+ ditaa-0.9/tests/text/art3.txt0000644000175000017500000000021311277077344014340 0ustar neoneo +--+ +-----+-->| | | | +--+ | | | | +-+-+ +-+-+ | | | | | | | | +---+ +---+ ditaa-0.9/tests/text/art11.txt0000644000175000017500000000023611277077344014424 0ustar neoneo +-+ ++ | | <--++---+---+-+ | | | | | | +---+---+ | | | | | | ++---+---++ ++ ++ ditaa-0.9/tests/text/art10.txt0000644000175000017500000000110311277077344014415 0ustar neoneo+-----+ |{d} | +======\ | | --->: HELL : |cRED |{d} \======+ +-----+ +----+ +-----+ +-----+ |{d} |---->|{s} | |{io} | | | /->| | | I/O | +----+ | |cGRE | | | | +-----+ +-----+ : +-------+------+ +------+ |{d} | | | | | | cPNK | | | | | | BIG file | | {s}| | | +------+ | | | {s}| | cBLU | | | +--------------+ +------+ Documents go to hell, some of them are stored in the green database (not in the pink one)ditaa-0.9/tests/text/art5.txt0000644000175000017500000000024411277077344014346 0ustar neoneo+--+--------+ | | | | | | | | /---+ | | | | | | | | +--+----+---/ ^ | This square makes a differenceditaa-0.9/tests/text/art4.txt0000644000175000017500000000001511277077344014341 0ustar neoneo+-+ | | +-+ditaa-0.9/tests/text/simple_S01.txt0000644000175000017500000000026711277077344015414 0ustar neoneo+-----+ +-----+ | | | | | | | | | | | | +-----+ +----- +-----+ | | | | | | +-----+ditaa-0.9/tests/text/art6.txt0000644000175000017500000000016011277077344014344 0ustar neoneo/----\/----\ | || | | || | | |\----/ | |/----\ | || | | || | \----/\----/ ditaa-0.9/tests/text/bug6.txt0000644000175000017500000000006511277077344014337 0ustar neoneo+-+-+ | | | | | | | | | +-+-+ Bug6 (now ok)ditaa-0.9/tests/text/bug5.txt0000644000175000017500000000051611277077344014337 0ustar neoneo +-------------+ | | | | | | | +-------+ | | | | | | | | | +-------+-----+ | | | | | | | +-------------+ (now ok!) ditaa-0.9/tests/text/art15.txt0000644000175000017500000000005211277077344014424 0ustar neoneo+---+ | | +---+---+ | | +---+ ditaa-0.9/tests/text/art2_5.txt0000644000175000017500000000022511277077344014566 0ustar neoneo+--+--------+ | | Test | | | |----- | | /---+ | | | | | |cPNK| | +--+----+---/ o Bullet point 1 o Point 2 ditaa-0.9/tests/text/art3_5.txt0000644000175000017500000000021611277077344014567 0ustar neoneo +--+ +-----+--> | | | | +--+ | | | | +---+ +---+ | | | | | | | | +---+ +---+ ditaa-0.9/tests/text/dak_orgstruktur_vs_be.ditaa.OutOfMemoryError.2.txt0000644000175000017500000000100011277407340024542 0ustar neoneo /-----------------------\ |c33F +-----+ +---+---+ |SZ |c85F | | c088 | | |KB0 +-->| KB1 | \----++---------+-------/ | | | | | | /-----+--------\ | | | | /---++----\ /----++---\ |c33F|c85F| |c85F|c33F| |SSt |KB0 | |KB0 |SSt | | +----+ +----+ | \---------/ \---------/ ditaa-0.9/tests/text/bug3.txt0000644000175000017500000000034411277077344014334 0ustar neoneo/----------\ | White! | | /----\ | | | | | | | | | | \----/ | | cBLU | \----------/ /----------\/----------\ | || | | || | \----------/\----------/ (now ok) ditaa-0.9/tests/text/art13.txt0000644000175000017500000000006211277077344014423 0ustar neoneo /-- +-- | | | --+ +-- --/ \--ditaa-0.9/tests/text/art16.txt0000644000175000017500000000027311277077344014432 0ustar neoneo+-----+-------+ | | | | | | | | | | d | | | | | +-----+ f | | | | | e | | | | | +-----+-------+ ditaa-0.9/tests/text/corner_case02.txt0000644000175000017500000000017711277077344016125 0ustar neoneo ++++++++++++++ +++++++++++++++ +++++++++++++++ +++++ +++++++++ +++++++++++++++ ++++++++++ ++++ +++++++++++++++ ++++++++++++++ ditaa-0.9/tests/text/art14.txt0000644000175000017500000000012011277077344014417 0ustar neoneo 1 2 3 *--*--* | | 8* *4 | | *--*--* 7 6 5 ditaa-0.9/tests/text/garbage.txt0000644000175000017500000000010611277077344015060 0ustar neoneo//-\|+\/\/\\ \|/\\=/=\/\: | |\ | | |\ *----/32)_+# _|+|_=_AEeee ditaa-0.9/tests/text/ditaa_bug.txt0000644000175000017500000000442611277077344015420 0ustar neoneo/-----------\ | 3D engine | \-----+-----/ | /---------------------------\ \----------+----->| graphics | | +---------------------------+ | | low level opengl handling | | | geometry | | | animation | | | shaders | | | scene | | | textures | /-----------------\ | | ... lots of stuff ... | /--->| X Window System | | \---------------------------/ | \-----------------/ | /---------------------------\ | /-------\ +----->| window system abstraction +---+--->| Win32 | | +---------------------------+ | \-------/ | | window / GL context init | | /------------\ | | event handling | +--->| OSX Carbon | | \---------------------------/ | \------------/ | /--------------\ | /-----------------\ +----->| math library | \--->| PlayStation 3 ? | | +--------------+ \-----------------/ | | vectors | | | matrices | | | quaternions | | | etc... | | \--------------/ | /---------------------\ +----->| simulation | | +---------------------+ | | particle systems | | | rigid bodies | | | spring/mass systems | | \---------------------/ | /---------------------\ \----->| common code | +---------------------+ | data structures | | error logging | | config file parsers | | ... whatever ... | \---------------------/ ditaa-0.9/tests/text/dak_orgstruktur_vs_be.ditaa.OutOfMemoryError.txt0000644000175000017500000000100011277407340024402 0ustar neoneo /-----------------------\ |c33F +-----+ /---+---+ |SZ |c85F | | c088 | | |KB0 +-->| KB1 | \----++---------+-------/ | | | | | | /-----+--------\ | | | | /---++----\ /----++---\ |c33F|c85F| |c85F|c33F| |SSt |KB0 | |KB0 |SSt | | +----+ +----+ | \---------/ \---------/ ditaa-0.9/tests/text/art19.txt0000644000175000017500000000000711277077344014430 0ustar neoneo| +- | ditaa-0.9/tests/text/bug1.txt0000644000175000017500000000006011277077344014325 0ustar neoneoBug1 fixed ^ | | \--\ | | V ditaa-0.9/tests/text/art21.txt0000644000175000017500000000001711277117236014415 0ustar neoneo+-+ | | \-+ ditaa-0.9/tests/text/bug9.txt0000644000175000017500000000026011277077344014337 0ustar neoneo Cell | +-----+ | | A |C B * | *--*----------* | | | | +-----+ | | ----*-------* ditaa-0.9/tests/text/bug15.txt0000644000175000017500000000007411277077344014417 0ustar neoneo/--\/--\ | || | \--/\--/ /--\/--\ | || | \--/\--/ ditaa-0.9/tests/text/art17.txt0000644000175000017500000000043011277077344014426 0ustar neoneo/---------------------------\ | fffew | +---------------------------+ | ... lots of stuff ... | \---------------------------/ /---------------------------\ | | window system abstraction +---+ | ... lots of stuff ... | \---------------------------/ ditaa-0.9/tests/text/bug12.txt0000644000175000017500000000007511277077344014415 0ustar neoneo | +-+-+ -| | |- +-+-+ | | +---+ | | |ditaa-0.9/tests/text/bug13.txt0000644000175000017500000000000711277077344014411 0ustar neoneo- -- ditaa-0.9/tests/text/art2.txt0000644000175000017500000000137411277077344014350 0ustar neoneo+--+--------+ | | Test | | | |----- | | /---+ | | | | | |cPNK| | +--+----+---/ o Bullet point 1 o Point 2 +-----+ --+-- |cGRE | | | | | | | \---- +-----+ |MMMMMMM| V ^ | | | | | <--+--> | | v | aligned else + + + | Horo_aligned | | strings | - ---\ -- | --- V ---- /----------\ | White! | | /----\ | | | | | | | | | | \----/ | | cBLU | \----------/ /----------\/----------\ | || | | || | \----------/\----------/ ----------- +-----+ | | +-+ | |-->|Z| | | +-+ +-----+ ditaa-0.9/tests/text/art12.txt0000644000175000017500000000021411277077344014421 0ustar neoneo ++ ++ ++---+---++--> | | | | | | +---+---+ | | | | | | ++---+---++ ++ ++ditaa-0.9/tests/text/dak_orgstruktur_vs_be.ditaa.txt0000644000175000017500000000100011277407340021144 0ustar neoneo /-----------------------\ |c33F +-----+ +-------+ |SZ |c85F +-->| c088 | | |KB0 | | KB1 | \----++-----+---+-------/ | | | | | | /-----+--------\ | | | | /---++----\ /----++---\ |c33F|c85F| |c85F|c33F| |SSt |KB0 | |KB0 |SSt | | +----+ +----+ | \---------/ \---------/ ditaa-0.9/tests/text/bug10.txt0000644000175000017500000000006111277077344014406 0ustar neoneoBUG - -- --- ---- ----- +-+ | | +-+ ditaa-0.9/tests/text/bug9_5.txt0000644000175000017500000000016111277077344014563 0ustar neoneo Cell +-----+ | A | | B C *--*--+------------+-----* | | | +-----+ditaa-0.9/tests/text/bug4.txt0000644000175000017500000000010711277077344014332 0ustar neoneo/------\ | | | BUG4 |--- | | \------/ --------- fixedditaa-0.9/tests/text/corner_case01.txt0000644000175000017500000000020011277077344016107 0ustar neoneo+++++++++++++++ +++++++++++++++ +++++++++++++++ +++++++++++++++ +++++++++++++++ +++++++++++++++ +++++++++++++++ +++++++++++++++ ditaa-0.9/tests/text/bug2.txt0000644000175000017500000000022011277077344014324 0ustar neoneoBug 2 fixed | | +-+-+ +---+ | | | | |--- ---| | | +---+ | | | | +-+-+ | | | | |ditaa-0.9/tests/text/art18.txt0000644000175000017500000000063211277077344014433 0ustar neoneo /---------------------------\ -------=--+----->| this is the triena bug | | \---------------------------/ | /---------------------------\ +----->| window system abstraction | | \---------------------------/ | /--------------\ +----->| math library | \--------------/ ditaa-0.9/tests/text/logo.txt0000644000175000017500000000055611277077344014441 0ustar neoneo/-------\ /------\ /------\ /-\ /-\ /-\ | /---\ | | /----/ | /----/ | | | | | | | | | | | | | | | | | | | | | | /-/ | | \----\ | | | | | | | | | | \-\ | \----\ | | | | | | | --> | | | | | | | | | | | | | | | | | | | | /----/ | | \----\ | | | | | | \-/ \-/ \------/ \------/ \-/ \-/ \-/ ditaa-0.9/tests/text/art1.txt0000644000175000017500000000324311277077344014344 0ustar neoneo+-----------+ +-----+-----+ +-----+-----+ | | | | | | | | | | | | | | d | | | | | | | | | | | a | -> | b X c | -> +--Y--+ f | -> ... | | | | | | | | | | | | | | e | | | | | | | | | | +-----------+ +-----+-----+ +-----+-----+ +-----+ /---\ | | | f | +--------------+ +--+ \---/ | | | | | | | | | | | | | | +-------+ +--+ -+- | | | | | +--+ +-------+ | | | | | | | | | | | | | +--+ +-------------+ ^ | | | +------+ \-\ testing | ascii2image --+ A B +-------------+ | | | | | E | F | +-----+-------+ | | | | | | | | | | | | +-------+-----+ | D | C | | | | | +-------------+ H G AB / \ / \ / * BC / \ / \ / * ditaa-0.9/tests/text/bug8.txt0000644000175000017500000000036411277077344014343 0ustar neoneo /\ || || || || /-----------/| \-----\ | |Bug 8 | |(ok!) | | | | \-----\ |/-----------/ || || || \/ ditaa-0.9/HISTORY0000644000175000017500000000070011303052710011641 0ustar neoneoVersion 0.9 * New shapes: * Ellipse {o} * Manual operation {mo} * Decision {c} ("Choice") * Trapezoid {tr} * Eliminated memory leak * Further parsing optimisations (pre-compiled regexes) Version 0.8 * Parsing optimisation (~90% faster) * Fixed line separation bugs * Fixed open shape rendering bugs (missing lines) * Generally more reliable * Tidying-up of command-line options (now using Apache commons-cli) * Streamlined release process ditaa-0.9/src/0000755000175000017500000000000011361663105011361 5ustar neoneoditaa-0.9/src/org/0000755000175000017500000000000011277077354012163 5ustar neoneoditaa-0.9/src/org/stathissideris/0000755000175000017500000000000011277077354015225 5ustar neoneoditaa-0.9/src/org/stathissideris/ascii2image/0000755000175000017500000000000011277077356017404 5ustar neoneoditaa-0.9/src/org/stathissideris/ascii2image/text/0000755000175000017500000000000011277501420020350 5ustar neoneoditaa-0.9/src/org/stathissideris/ascii2image/text/GridPatternGroup.java0000644000175000017500000002742711277120326024471 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.text; import java.util.ArrayList; import java.util.Iterator; /** * * @author Efstathios Sideris */ public class GridPatternGroup extends ArrayList { public boolean areAllMatchedBy(TextGrid grid){ Iterator it = iterator(); while (it.hasNext()) { GridPattern pattern = it.next(); if(!pattern.isMatchedBy(grid)) return false; } return true; } public boolean isAnyMatchedBy(TextGrid grid){ Iterator it = iterator(); while (it.hasNext()) { GridPattern pattern = it.next(); if(pattern.isMatchedBy(grid)) return true; } return false; } public void add(GridPattern... patterns) { for(GridPattern p : patterns) add(p); } //TODO: define criteria for on-line type? public static final GridPatternGroup cornerCriteria = new GridPatternGroup(); public static final GridPatternGroup normalCornerCriteria = new GridPatternGroup(); public static final GridPatternGroup roundCornerCriteria = new GridPatternGroup(); public static final GridPatternGroup corner1Criteria = new GridPatternGroup(); public static final GridPatternGroup corner2Criteria = new GridPatternGroup(); public static final GridPatternGroup corner3Criteria = new GridPatternGroup(); public static final GridPatternGroup corner4Criteria = new GridPatternGroup(); public static final GridPatternGroup normalCorner1Criteria = new GridPatternGroup(); public static final GridPatternGroup normalCorner2Criteria = new GridPatternGroup(); public static final GridPatternGroup normalCorner3Criteria = new GridPatternGroup(); public static final GridPatternGroup normalCorner4Criteria = new GridPatternGroup(); public static final GridPatternGroup roundCorner1Criteria = new GridPatternGroup(); public static final GridPatternGroup roundCorner2Criteria = new GridPatternGroup(); public static final GridPatternGroup roundCorner3Criteria = new GridPatternGroup(); public static final GridPatternGroup roundCorner4Criteria = new GridPatternGroup(); public static final GridPatternGroup intersectionCriteria = new GridPatternGroup(); public static final GridPatternGroup TCriteria = new GridPatternGroup(); public static final GridPatternGroup inverseTCriteria = new GridPatternGroup(); public static final GridPatternGroup KCriteria = new GridPatternGroup(); public static final GridPatternGroup inverseKCriteria = new GridPatternGroup(); public static final GridPatternGroup crossCriteria = new GridPatternGroup(); public static final GridPatternGroup stubCriteria = new GridPatternGroup(); public static final GridPatternGroup verticalLinesEndCriteria = new GridPatternGroup(); public static final GridPatternGroup horizontalLinesEndCriteria = new GridPatternGroup(); public static final GridPatternGroup linesEndCriteria = new GridPatternGroup(); public static final GridPatternGroup crossOnLineCriteria = new GridPatternGroup(); public static final GridPatternGroup horizontalCrossOnLineCriteria = new GridPatternGroup(); public static final GridPatternGroup verticalCrossOnLineCriteria = new GridPatternGroup(); public static final GridPatternGroup starOnLineCriteria = new GridPatternGroup(); public static final GridPatternGroup horizontalStarOnLineCriteria = new GridPatternGroup(); public static final GridPatternGroup verticalStarOnLineCriteria = new GridPatternGroup(); public static final GridPatternGroup loneDiagonalCriteria = new GridPatternGroup(); static { GridPattern crossPattern1 = new GridPattern( ".6.", "4+8", ".2." ); crossCriteria.add(crossPattern1); GridPattern KPattern1 = new GridPattern( ".6.", "%4+8", ".2." ); KCriteria.add(KPattern1); GridPattern inverseKPattern1 = new GridPattern( ".6.", "4+%8", ".2." ); inverseKCriteria.add(inverseKPattern1); GridPattern TPattern1 = new GridPattern( ".%6.", "4+8", ".2." ); TCriteria.add(TPattern1); GridPattern inverseTPattern1 = new GridPattern( ".6.", "4+8", ".%2." ); inverseTCriteria.add(inverseTPattern1); // ****** normal corners ******* GridPattern normalCorner1Pattern1 = new GridPattern( ".[.", "~+(", ".^." ); normalCorner1Criteria.add(normalCorner1Pattern1); GridPattern normalCorner2Pattern1 = new GridPattern( ".[.", "(+~", ".^." ); normalCorner2Criteria.add(normalCorner2Pattern1); GridPattern normalCorner3Pattern1 = new GridPattern( ".^.", "(+~", ".[." ); normalCorner3Criteria.add(normalCorner3Pattern1); GridPattern normalCorner4Pattern1 = new GridPattern( ".^.", "~+(", ".[." ); normalCorner4Criteria.add(normalCorner4Pattern1); // ******* round corners ******* GridPattern roundCorner1Pattern1 = new GridPattern( ".[.", "~/4", ".2." ); roundCorner1Criteria.add(roundCorner1Pattern1); GridPattern roundCorner2Pattern1 = new GridPattern( ".[.", "4\\~", ".2." ); roundCorner2Criteria.add(roundCorner2Pattern1); GridPattern roundCorner3Pattern1 = new GridPattern( ".6.", "4/~", ".[." ); roundCorner3Criteria.add(roundCorner3Pattern1); GridPattern roundCorner4Pattern1 = new GridPattern( ".6.", "~\\8", ".[." ); roundCorner4Criteria.add(roundCorner4Pattern1); //stubs GridPattern stubPattern1 = new GridPattern( "!^!", "!+!", ".!." ); stubCriteria.add(stubPattern1); GridPattern stubPattern2 = new GridPattern( "!^!", "!+!", ".-." ); stubCriteria.add(stubPattern2); GridPattern stubPattern3 = new GridPattern( "!!.", "(+!", "!!." ); stubCriteria.add(stubPattern3); GridPattern stubPattern4 = new GridPattern( "!!.", "(+|", "!!." ); stubCriteria.add(stubPattern4); GridPattern stubPattern5 = new GridPattern( ".!.", "!+!", "!^!" ); stubCriteria.add(stubPattern5); GridPattern stubPattern6 = new GridPattern( ".-.", "!+!", "!^!" ); stubCriteria.add(stubPattern6); GridPattern stubPattern7 = new GridPattern( ".!!", "!+(", ".!!" ); stubCriteria.add(stubPattern7); GridPattern stubPattern8 = new GridPattern( ".!!", "|+(", ".!!" ); stubCriteria.add(stubPattern8); // ****** ends of lines ****** GridPattern verticalLinesEndPattern1 = new GridPattern( ".^.", ".|.", ".!." ); verticalLinesEndCriteria.add(verticalLinesEndPattern1); GridPattern verticalLinesEndPattern2 = new GridPattern( ".^.", ".|.", ".-." ); verticalLinesEndCriteria.add(verticalLinesEndPattern2); GridPattern horizontalLinesEndPattern3 = new GridPattern( "...", "(-!", "..." ); horizontalLinesEndCriteria.add(horizontalLinesEndPattern3); GridPattern horizontalLinesEndPattern4 = new GridPattern( "...", "(-|", "..." ); horizontalLinesEndCriteria.add(horizontalLinesEndPattern4); GridPattern verticalLinesEndPattern5 = new GridPattern( ".!.", ".|.", ".^." ); verticalLinesEndCriteria.add(verticalLinesEndPattern5); GridPattern verticalLinesEndPattern6 = new GridPattern( ".-.", ".|.", ".^." ); verticalLinesEndCriteria.add(verticalLinesEndPattern6); GridPattern horizontalLinesEndPattern7 = new GridPattern( "...", "!-(", "..." ); horizontalLinesEndCriteria.add(horizontalLinesEndPattern7); GridPattern horizontalLinesEndPattern8 = new GridPattern( "...", "|-(", "..." ); horizontalLinesEndCriteria.add(horizontalLinesEndPattern8); // ****** others ******* GridPattern horizontalCrossOnLinePattern1 = new GridPattern( "...", "(+(", "..." ); horizontalCrossOnLineCriteria.add(horizontalCrossOnLinePattern1); GridPattern verticalCrossOnLinePattern1 = new GridPattern( ".^.", ".+.", ".^." ); verticalCrossOnLineCriteria.add(verticalCrossOnLinePattern1); GridPattern horizontalStarOnLinePattern1 = new GridPattern( "...", "(*(", "..." ); horizontalStarOnLineCriteria.add(horizontalStarOnLinePattern1); GridPattern horizontalStarOnLinePattern2 = new GridPattern( "...", "!*(", "..." ); horizontalStarOnLineCriteria.add(horizontalStarOnLinePattern2); GridPattern horizontalStarOnLinePattern3 = new GridPattern( "...", "(*!", "..." ); horizontalStarOnLineCriteria.add(horizontalStarOnLinePattern3); GridPattern verticalStarOnLinePattern1 = new GridPattern( ".^.", ".*.", ".^." ); verticalStarOnLineCriteria.add(verticalStarOnLinePattern1); GridPattern verticalStarOnLinePattern2 = new GridPattern( ".!.", ".*.", ".^." ); verticalStarOnLineCriteria.add(verticalStarOnLinePattern2); GridPattern verticalStarOnLinePattern3 = new GridPattern( ".^.", ".*.", ".!." ); verticalStarOnLineCriteria.add(verticalStarOnLinePattern3); GridPattern loneDiagonalPattern1 = new GridPattern( ".%6%7", "%4/%8", "%3%2." ); loneDiagonalCriteria.add(loneDiagonalPattern1); GridPattern loneDiagonalPattern2 = new GridPattern( "%1%6.", "%4\\%8", ".%2%5" ); loneDiagonalCriteria.add(loneDiagonalPattern2); //groups intersectionCriteria.addAll(crossCriteria); intersectionCriteria.addAll(KCriteria); intersectionCriteria.addAll(TCriteria); intersectionCriteria.addAll(inverseKCriteria); intersectionCriteria.addAll(inverseTCriteria); normalCornerCriteria.addAll(normalCorner1Criteria); normalCornerCriteria.addAll(normalCorner2Criteria); normalCornerCriteria.addAll(normalCorner3Criteria); normalCornerCriteria.addAll(normalCorner4Criteria); roundCornerCriteria.addAll(roundCorner1Criteria); roundCornerCriteria.addAll(roundCorner2Criteria); roundCornerCriteria.addAll(roundCorner3Criteria); roundCornerCriteria.addAll(roundCorner4Criteria); corner1Criteria.addAll(normalCorner1Criteria); corner1Criteria.addAll(roundCorner1Criteria); corner2Criteria.addAll(normalCorner2Criteria); corner2Criteria.addAll(roundCorner2Criteria); corner3Criteria.addAll(normalCorner3Criteria); corner3Criteria.addAll(roundCorner3Criteria); corner4Criteria.addAll(normalCorner4Criteria); corner4Criteria.addAll(roundCorner4Criteria); cornerCriteria.addAll(normalCornerCriteria); cornerCriteria.addAll(roundCornerCriteria); crossOnLineCriteria.addAll(horizontalCrossOnLineCriteria); crossOnLineCriteria.addAll(verticalCrossOnLineCriteria); starOnLineCriteria.addAll(horizontalStarOnLineCriteria); starOnLineCriteria.addAll(verticalStarOnLineCriteria); linesEndCriteria.addAll(horizontalLinesEndCriteria); linesEndCriteria.addAll(verticalLinesEndCriteria); linesEndCriteria.addAll(stubCriteria); } } ditaa-0.9/src/org/stathissideris/ascii2image/text/AbstractCell.java0000644000175000017500000000612711277077356023604 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.text; /** * * @author Efstathios Sideris */ public class AbstractCell { public int rows[][] = new int[3][3]; { for(int y = 0; y < 3; y++) for(int x = 0; x < 3; x++) rows[x][y] = 0; } static AbstractCell makeHorizontalLine(){ AbstractCell result = new AbstractCell(); result.rows[0][1] = 1; result.rows[1][1] = 1; result.rows[2][1] = 1; return result; } static AbstractCell makeVerticalLine(){ AbstractCell result = new AbstractCell(); result.rows[1][0] = 1; result.rows[1][1] = 1; result.rows[1][2] = 1; return result; } static AbstractCell makeCorner1(){ AbstractCell result = new AbstractCell(); result.rows[1][1] = 1; result.rows[1][2] = 1; result.rows[2][1] = 1; return result; } static AbstractCell makeCorner2(){ AbstractCell result = new AbstractCell(); result.rows[0][1] = 1; result.rows[1][1] = 1; result.rows[1][2] = 1; return result; } static AbstractCell makeCorner3(){ AbstractCell result = new AbstractCell(); result.rows[0][1] = 1; result.rows[1][1] = 1; result.rows[1][0] = 1; return result; } static AbstractCell makeCorner4(){ AbstractCell result = new AbstractCell(); result.rows[2][1] = 1; result.rows[1][1] = 1; result.rows[1][0] = 1; return result; } static AbstractCell makeT(){ AbstractCell result = AbstractCell.makeHorizontalLine(); result.rows[1][2] = 1; return result; } static AbstractCell makeInverseT(){ AbstractCell result = AbstractCell.makeHorizontalLine(); result.rows[1][0] = 1; return result; } static AbstractCell makeK(){ AbstractCell result = AbstractCell.makeVerticalLine(); result.rows[2][1] = 1; return result; } static AbstractCell makeInverseK(){ AbstractCell result = AbstractCell.makeVerticalLine(); result.rows[0][1] = 1; return result; } static AbstractCell makeCross(){ AbstractCell result = AbstractCell.makeVerticalLine(); result.rows[0][1] = 1; result.rows[2][1] = 1; return result; } static AbstractCell makeStar(){ AbstractCell result = AbstractCell.makeVerticalLine(); for(int y = 0; y < 3; y++) for(int x = 0; x < 3; x++) result.rows[x][y] = 1; return result; } } ditaa-0.9/src/org/stathissideris/ascii2image/text/StringUtils.java0000644000175000017500000001142711277077356023527 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.text; /** * @author sideris * * To change the template for this generated type comment go to * Window>Preferences>Java>Code Generation>Code and Comments */ public class StringUtils { /** * The indexOf idiom * * @param big * @param fragment * @return */ public static boolean contains(String big, String fragment){ return (big.indexOf(fragment) != -1); } public static String repeatString(String string, int repeats){ if(repeats == 0) return ""; String buffer = ""; for(int i=0; i < repeats; i++){ buffer += string; } return buffer; } /*public static String repeatString(String string, int repeats){ if(repeats == 0) return ""; StringBuffer buffer = new StringBuffer(""); for(int i=0; i < repeats; i++){ buffer.append(string); } return buffer.toString(); }*/ public static boolean isBlank(String s){ return (s.length() == 0 || s.matches("^\\s*$")); } /** * * Converts the first character of string into a capital letter * * @param string * @return */ public static String firstToUpper(String string){ return string.substring(0,1).toUpperCase()+string.substring(1); } public static String insertSpaceAtCaps(String string){ int uppers = 0; //first we count for(int i=0; i < string.length(); i++){ if(Character.isUpperCase(string.charAt(i))) uppers++; } int[] indexes = null; if(Character.isUpperCase(string.charAt(0))){ indexes = new int[uppers]; } else { indexes = new int[++uppers]; } indexes[0] = 0; int k = 1; //then we find the indexes (we have ckecked the first char already) for(int j =1; j < string.length(); j++){ if(Character.isUpperCase(string.charAt(j))) indexes[k++] = j; } StringBuffer buffer = new StringBuffer(""); //and finally we breakup the String for(int i =0; i < indexes.length; i++){ if(i+1 < indexes.length){ buffer.append(string.substring(indexes[i], indexes[i+1])); buffer.append(" "); } else { buffer.append(string.substring(indexes[i])); } } return buffer.toString(); } public static boolean isOneOf(char c, char[] group){ for(int i = 0; i < group.length; i++) if(c == group[i]) return true; return false; } public static boolean isOneOf(String str, String[] group){ for(int i = 0; i < group.length; i++) if(str.equals(group[i])) return true; return false; } public static String getPath(String fullPath){ if(fullPath.lastIndexOf("\\") != -1) return fullPath.substring(0, fullPath.lastIndexOf("\\")); else return ""; } public static String getBaseFilename(String fullPath){ if(fullPath.lastIndexOf(".") != -1 && fullPath.lastIndexOf("\\") != -1) return fullPath.substring(fullPath.lastIndexOf("\\") + 1, fullPath.lastIndexOf(".")); else return fullPath; } public static String getExtension(String fullPath){ if(fullPath.lastIndexOf(".") != -1) return fullPath.substring(fullPath.lastIndexOf(".") + 1); else return ""; } public static void main(String[] args){ System.out.println("1 "+StringUtils.firstToUpper("testing")); System.out.println("2 "+StringUtils.firstToUpper(" testing")); System.out.println("3 "+StringUtils.firstToUpper("_testing")); System.out.println("4 "+StringUtils.firstToUpper("Testing")); System.out.println("5 "+StringUtils.firstToUpper("ttesting")); String path = "C:\\Files\\test.txt"; System.out.println(path); System.out.println(StringUtils.getPath(path)); System.out.println(StringUtils.getBaseFilename(path)); System.out.println(StringUtils.getExtension(path)); path = "test.txt"; System.out.println(path); System.out.println(StringUtils.getPath(path)); System.out.println(StringUtils.getBaseFilename(path)); System.out.println(StringUtils.getExtension(path)); path = "test"; System.out.println(path); System.out.println("path: "+StringUtils.getPath(path)); System.out.println("base: "+StringUtils.getBaseFilename(path)); System.out.println(" ext: "+StringUtils.getExtension(path)); } } ditaa-0.9/src/org/stathissideris/ascii2image/text/GridPattern.java0000644000175000017500000002224311277501420023441 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.text; import java.util.ArrayList; import java.util.Iterator; import java.util.regex.Pattern; /** * This is a TextGrid (usually 3x3) that contains the equivalent of a * 2D reqular expression (which uses custom syntax to make things more * visual, but standard syntax is also possible). * * The custom syntax is: * . means anything * b means any boundary (any of - = / \ + | :) * ! means not boundary (none of - = / \ + | :) * - means - or = * | means | or : * [ means not | nor : * ~ means not - nor = * ^ means a boundary but not - nor = * ( means a boundary but not | nor : * s means a straight boundary (one of - = + | :) * S means not a straight boundary (none of - = + | :) * * 1 means a cell that has entry point 1 * 2 means a cell that has entry point 2 * 3 means a cell that has entry point 3 etc. up to number 8 * * %1 means a cell that does not have entry point 1 etc. * * See below for an explanation of entry points * * +, \, / and the space are literal (as is any other character) * * * Entry points * *
 * 1   2   3
 *  *--*--*
 *  |     |
 * 8*     *4
 *  |     |
 *  *--*--*
 * 7   6   5
 * 
* * We number the entry points for each cell as in the diagram * above. If a cell is occupied by a character, we define as * entry points the points of the above diagram that the character * can touch with the end of its lines. For example - has * entry points 8 and 4, | and : have entry points 2 and 6, * / has 3 and 7, \ has 1 and 5, + has 2, 6, 8 and 4 etc. * * * @author Efstathios Sideris */ public class GridPattern extends TextGrid { private ArrayList regExps = new ArrayList(); //TODO optimise: store as PatternS private boolean regExpsAreValid = false; private static final boolean DEBUG = false; private boolean usesStandardSyntax = false; public GridPattern(){ super(3, 3); } public GridPattern(String row1, String row2, String row3){ super(Math.max(Math.max(row1.length(), row2.length()), row3.length()), 3); setTo(row1, row2, row3); regExpsAreValid = false; } public boolean usesStandardSyntax() { return usesStandardSyntax; } public void setUsesStandardSyntax(boolean b) { usesStandardSyntax = b; regExpsAreValid = false; } public boolean isMatchedBy(TextGrid grid){ /*if(grid.getHeight() != this.getHeight() || grid.getWidth() != this.getWidth()) return false;*/ if(!regExpsAreValid) prepareRegExps(); for(int i = 0; i < grid.getHeight(); i++) { String row = grid.getRow(i).toString(); Pattern regexp = regExps.get(i); if(!regexp.matcher(row).matches()) { if(DEBUG) System.out.println(row+" does not match "+regexp); return false; } } return true; } private void prepareRegExps(){ regExpsAreValid = true; regExps.clear(); if (DEBUG) System.out.println("Trying to match:"); if(!usesStandardSyntax){ Iterator it = getRows().iterator(); while (it.hasNext()) { String row = it.next().toString(); regExps.add(Pattern.compile(makeRegExp(row))); if(DEBUG) System.out.println(row+" becomes "+makeRegExp(row)); } } else { Iterator it = getRows().iterator(); while (it.hasNext()) { String row = it.next().toString(); regExps.add(Pattern.compile(row)); } } } private String makeRegExp(String pattern){ StringBuilder result = new StringBuilder(); int tokensHandled = 0; for(int i = 0; i < pattern.length() && tokensHandled < 3; i++){ char c = pattern.charAt(i); if(c == '[') { result.append("[^|:]"); } else if(c == '|') { result.append("[|:]"); } else if(c == '-') { result.append("-"); } else if(c == '!') { result.append("[^-=\\/\\\\+|:]"); } else if(c == 'b') { result.append("[-=\\/\\\\+|:]"); } else if(c == '^') { result.append("[\\/\\\\+|:]"); } else if(c == '(') { result.append("[-=\\/\\\\+]"); } else if(c == '~') { result.append("."); } else if(c == '+') { result.append("\\+"); } else if(c == '\\') { result.append("\\\\"); } else if(c == 's') { result.append("[-=+|:]"); } else if(c == 'S') { result.append("[\\/\\\\]"); } else if(c == '*') { result.append("\\*"); //entry points } else if(c == '1') { result.append("[\\\\]"); } else if(c == '2') { result.append("[|:+\\/\\\\]"); } else if(c == '3') { result.append("[\\/]"); } else if(c == '4') { result.append("[-=+\\/\\\\]"); } else if(c == '5') { result.append("[\\\\]"); } else if(c == '6') { result.append("[|:+\\/\\\\]"); } else if(c == '7') { result.append("[\\/]"); } else if(c == '8') { result.append("[-=+\\/\\\\]"); //entry point negations } else if(c == '%') { if(i+1 > pattern.length()){ throw new RuntimeException("Invalid pattern, found % at the end"); } c = pattern.charAt(++i); if(c == '1') { result.append("[^\\\\]"); } else if(c == '2') { result.append("[^|:+\\/\\\\]"); } else if(c == '3') { result.append("[^\\/]"); } else if(c == '4') { result.append("[^-=+\\/\\\\]"); } else if(c == '5') { result.append("[^\\\\]"); } else if(c == '6') { result.append("[^|:+\\/\\\\]"); } else if(c == '7') { result.append("[^\\/]"); } else if(c == '8') { result.append("[^-=+\\/\\\\]"); } } else result.append(String.valueOf(c)); tokensHandled++; } return result.toString(); } public void setTo(String row1, String row2, String row3){ if(getHeight() != 3) throw new RuntimeException("This method can only be called for GridPatternS with height 3"); regExpsAreValid = false; writeStringTo(0, 0, row1); writeStringTo(0, 1, row2); writeStringTo(0, 2, row3); //don't use setRow() here! } public static void main(String[] args) { TextGrid grid = new TextGrid(3, 3); // grid.setRow(0, " "); // grid.setRow(1, "-\\ "); // grid.setRow(2, " | "); // // if(GridPatternGroup.corner2Criteria.isAnyMatchedBy(grid)){ // System.out.println("Grid is corner 2"); // } else { // System.out.println("Grid is not corner 2"); // } // // if(grid.isCorner2(grid.new Cell(1,1))){ // System.out.println("Grid is corner 2"); // } else { // System.out.println("Grid is not corner 2"); // } // // // grid.setRow(0, "-+ "); // grid.setRow(1, " | "); // grid.setRow(2, "-+ "); // // if(GridPatternGroup.cornerCriteria.isAnyMatchedBy(grid)){ // System.out.println("Grid is corner"); // } else { // System.out.println("Grid is not corner"); // } // // if(grid.isCorner(grid.new Cell(1,1))){ // System.out.println("Grid is corner"); // } else { // System.out.println("Grid is not corner"); // } grid.setRow(0, "---"); grid.setRow(1, " / "); grid.setRow(2, "---"); grid.printDebug(); if(GridPatternGroup.loneDiagonalCriteria.isAnyMatchedBy(grid)){ System.out.println("Grid is lone diagonal"); } else { System.out.println("Grid is not lone diagonal"); } grid.setRow(0, "--/"); grid.setRow(1, " / "); grid.setRow(2, "---"); grid.printDebug(); if(GridPatternGroup.loneDiagonalCriteria.isAnyMatchedBy(grid)){ System.out.println("Grid is lone diagonal"); } else { System.out.println("Grid is not lone diagonal"); } grid.setRow(0, "-- "); grid.setRow(1, " \\ "); grid.setRow(2, "---"); grid.printDebug(); if(GridPatternGroup.loneDiagonalCriteria.isAnyMatchedBy(grid)){ System.out.println("Grid is lone diagonal"); } else { System.out.println("Grid is not lone diagonal"); } grid.setRow(0, "-- "); grid.setRow(1, " \\ "); grid.setRow(2, "--\\"); grid.printDebug(); if(GridPatternGroup.loneDiagonalCriteria.isAnyMatchedBy(grid)){ System.out.println("Grid is lone diagonal"); } else { System.out.println("Grid is not lone diagonal"); } grid.setRow(0, " "); grid.setRow(1, "-\\/"); grid.setRow(2, " ||"); grid.printDebug(); if(GridPatternGroup.loneDiagonalCriteria.isAnyMatchedBy(grid)){ System.out.println("Grid is lone diagonal"); } else { System.out.println("Grid is not lone diagonal"); } } } ditaa-0.9/src/org/stathissideris/ascii2image/text/CellSet.java0000644000175000017500000004400511277077356022571 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.text; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.Set; /** * * @author Efstathios Sideris */ public class CellSet implements Iterable { private static final boolean DEBUG = false; private static final boolean VERBOSE_DEBUG = false; public static final int TYPE_CLOSED = 0; public static final int TYPE_OPEN = 1; public static final int TYPE_MIXED = 2; public static final int TYPE_HAS_CLOSED_AREA = 3; public static final int TYPE_UNDETERMINED = 4; Set internalSet = new HashSet(); private int type = TYPE_UNDETERMINED; private boolean typeIsValid = false; private static final Object FAKE = new Object(); public CellSet(){ } public CellSet(CellSet other){ addAll(other); } public Iterator iterator(){ return internalSet.iterator(); } public Object add(TextGrid.Cell cell){ return internalSet.add(cell); } public void addAll(CellSet set){ internalSet.addAll(set.internalSet); } void clear(){ internalSet.clear(); } public int size() { return internalSet.size(); } public TextGrid.Cell getFirst(){ //return internalSet.get(0); return (TextGrid.Cell) internalSet.iterator().next(); } public void printAsGrid(){ TextGrid grid = new TextGrid(getMaxX()+2, getMaxY()+2); grid.fillCellsWith(this, '*'); grid.printDebug(); } public void printDebug(){ Iterator it = iterator(); while(it.hasNext()){ TextGrid.Cell cell = it.next(); System.out.print(cell); if(it.hasNext()) System.out.print(" "); } System.out.println(); } public String getCellsAsString(){ StringBuffer str = new StringBuffer(); Iterator it = iterator(); while(it.hasNext()){ str.append(it.next().toString()); if(it.hasNext()) str.append("/"); } return str.toString(); } public String toString(){ TextGrid grid = new TextGrid(getMaxX()+2, getMaxY()+2); grid.fillCellsWith(this, '*'); return grid.getDebugString(); } /** * Deep copy * * @param set * @return */ public static CellSet copyCellSet(CellSet set) { TextGrid grid = new TextGrid(); CellSet newSet = new CellSet(); Iterator it = set.iterator(); while (it.hasNext()) { TextGrid.Cell cell = (TextGrid.Cell) it.next(); TextGrid.Cell newCell = grid.new Cell(cell); newSet.add(newCell); } return newSet; } /*public BoundarySet(BoundarySet set) { Iterator it = set.iterator(); while(it.hasNext()){ TextGrid.Cell cell = (TextGrid.Cell) it.next(); add(new TextGrid.Cell(cell)); } }*/ public int getType(TextGrid grid) { if(typeIsValid) return type; typeIsValid = true; if(size() == 1) { type = TYPE_OPEN; return TYPE_OPEN; } int typeTrace = getTypeAccordingToTraceMethod(grid); if(DEBUG){ System.out.println("trace: "+typeTrace); } if(typeTrace == TYPE_OPEN) { type = TYPE_OPEN; return TYPE_OPEN; } if(typeTrace == TYPE_CLOSED) { type = TYPE_CLOSED; return TYPE_CLOSED; } if(typeTrace == TYPE_UNDETERMINED) { int typeFill = getTypeAccordingToFillMethod(grid); if(typeFill == TYPE_HAS_CLOSED_AREA){ type = TYPE_MIXED; return TYPE_MIXED; } else if(typeFill == TYPE_OPEN){ type = TYPE_OPEN; return TYPE_OPEN; } } //in the case that both return undetermined: type = TYPE_UNDETERMINED; return TYPE_UNDETERMINED; } private int getTypeAccordingToTraceMethod(TextGrid grid) { if(size() < 2) return TYPE_OPEN; TextGrid workGrid = TextGrid.makeSameSizeAs(grid); grid.copyCellsTo(this, workGrid); //start with a line end if it exists or with a "random" cell if not TextGrid.Cell start = null; for(TextGrid.Cell cell : this) if(workGrid.isLinesEnd(cell)) start = cell; if(start == null) start = (TextGrid.Cell) getFirst(); if (DEBUG) System.out.println("Tracing:\nStarting at "+start+" ("+grid.getCellTypeAsString(start)+")"); TextGrid.Cell previous = start; TextGrid.Cell cell = null; CellSet nextCells = workGrid.followCell(previous); if(nextCells.size() == 0) return TYPE_OPEN; cell = (TextGrid.Cell) nextCells.getFirst(); if (DEBUG) System.out.println("\tat cell "+cell+" ("+grid.getCellTypeAsString(cell)+")"); while(!cell.equals(start)){ nextCells = workGrid.followCell(cell, previous); if(nextCells.size() == 0) { if (DEBUG) System.out.println("-> Found dead-end, shape is open"); return TYPE_OPEN; } if(nextCells.size() == 1) { previous = cell; cell = (TextGrid.Cell) nextCells.getFirst(); if (DEBUG) System.out.println("\tat cell "+cell+" ("+grid.getCellTypeAsString(cell)+")"); } else if(nextCells.size() > 1) { if (DEBUG) System.out.println("-> Found intersection at cell "+cell); return TYPE_UNDETERMINED; } } if (DEBUG) System.out.println("-> Arrived back to start, shape is closed"); return TYPE_CLOSED; // boolean hasMoved = false; // // CellSet workSet; // workSet = new CellSet(this); // // TextGrid.Cell start = (TextGrid.Cell) get(0); // // workSet.remove(start); // TextGrid.Cell cell = workSet.findCellNextTo(start); // // while(true && cell != null){ // // hasMoved = true; // workSet.remove(cell); // // CellSet setOfNeighbours = workSet.findCellsNextTo(cell); // // if(setOfNeighbours.isEmpty()) break; // // TextGrid.Cell c = null; // if(setOfNeighbours.size() == 1) c = (TextGrid.Cell) setOfNeighbours.get(0); // if(setOfNeighbours.size() > 1) return TYPE_UNDETERMINED; // if(c == null) break; // else cell = c; // } // if(cell != null && start.isNextTo(cell) && hasMoved) return TYPE_CLOSED; // else return TYPE_OPEN; } private int getTypeAccordingToFillMethod(TextGrid grid){ if(size() == 0) return TYPE_OPEN; CellSet tempSet = copyCellSet(this); tempSet.translate( -this.getMinX() + 1, -this.getMinY() + 1); TextGrid subGrid = grid.getSubGrid(getMinX() - 1, getMinY() - 1, getWidth() + 3, getHeight() + 3); AbstractionGrid abstraction = new AbstractionGrid(subGrid, tempSet); TextGrid temp = abstraction.getCopyOfInternalBuffer(); TextGrid.Cell cell1 = null; TextGrid.Cell cell2 = null; int width = temp.getWidth(); int height = temp.getHeight(); TextGrid.Cell fillCell = null; for(int y = 0; y < height; y++){ for(int x = 0; x < width; x++){ TextGrid.Cell cCell = temp.new Cell(x, y); if(temp.isBlank(cCell)){ fillCell = cCell; break; } } } if(fillCell == null){ System.err.println("Unexpected error: fill method cannot fill anywhere"); return TYPE_UNDETERMINED; } temp.fillContinuousArea(fillCell, '*'); if(VERBOSE_DEBUG) {System.out.println("Buffer after filling:"); temp.printDebug();} if(temp.hasBlankCells()) return TYPE_HAS_CLOSED_AREA; else return TYPE_OPEN; } public void translate(int dx, int dy){ typeIsValid = false; Iterator it = iterator(); while(it.hasNext()){ TextGrid.Cell cCell = (TextGrid.Cell) it.next(); cCell.x += dx; cCell.y += dy; } } public TextGrid.Cell find(TextGrid.Cell cell){ Iterator it = iterator(); while(it.hasNext()){ TextGrid.Cell cCell = it.next(); if(cCell.equals(cell)) return cCell; } return null; } public boolean contains(TextGrid.Cell cell){ if(cell == null) return false; return internalSet.contains(cell); } // public boolean contains(TextGrid.Cell cell){ // Iterator it = iterator(); // while(it.hasNext()){ // TextGrid.Cell cCell = it.next(); // if(cCell.equals(cell)) return true; // } // return false; // } public void addSet(CellSet set){ typeIsValid = false; this.addAll(set); } public boolean hasCommonCells(CellSet otherSet){ Iterator it = iterator(); while(it.hasNext()){ TextGrid.Cell cell = it.next(); if(otherSet.contains(cell)) return true; } return false; } public TextGrid.Cell find(int x, int y){ Iterator it = iterator(); while(it.hasNext()){ TextGrid.Cell cCell = (TextGrid.Cell) it.next(); if(cCell.x == x && cCell.y == y) return cCell; } return null; } public CellSet getFilledEquivalent(TextGrid textGrid){ if(this.getType(textGrid) == CellSet.TYPE_OPEN) return new CellSet(this); TextGrid grid = new TextGrid(getMaxX()+2, getMaxY()+2); grid.fillCellsWith(this, '*'); //find a cell that has a blank both on the east and the west TextGrid.Cell cell = null; boolean finished = false; for(int y = 0; y < grid.getHeight() && !finished; y++){ for(int x = 0; x < grid.getWidth() && !finished; x++){ cell = grid.new Cell(x, y); if(!grid.isBlank(cell) && grid.isBlank(cell.getEast()) && grid.isBlank(cell.getWest())){ finished = true; } } } if(cell != null){ cell = cell.getEast(); if(grid.isOutOfBounds(cell)) return new CellSet(this); grid.fillContinuousArea(cell, '*'); return grid.getAllNonBlank(); } System.err.println("Unexpected error, cannot find the filled equivalent of CellSet"); return null; } /** * Returns the first cell that is found to be next to cell. * * @param cell * @return */ public TextGrid.Cell findCellNextTo(TextGrid.Cell cell){ Iterator it = iterator(); while(it.hasNext()){ TextGrid.Cell cCell = (TextGrid.Cell) it.next(); if(cCell.isNextTo(cell)) return cCell; } return null; } /** * Returns all the cells that are found to be next to cell. * * @param cell * @return */ public CellSet findCellsNextTo(TextGrid.Cell cell){ if(cell == null) throw new IllegalArgumentException("cell cannot be null"); CellSet set = new CellSet(); Iterator it = iterator(); while(it.hasNext()){ TextGrid.Cell cCell = (TextGrid.Cell) it.next(); if(cCell.isNextTo(cell)) set.add(cCell); } return set; } public void appendSet(CellSet set){ typeIsValid = false; Iterator it = set.iterator(); while(it.hasNext()){ TextGrid.Cell cell = (TextGrid.Cell) it.next(); if(find(cell) == null) add(cell); } } public void subtractSet(CellSet set){ typeIsValid = false; Iterator it = set.iterator(); while(it.hasNext()){ TextGrid.Cell cell = (TextGrid.Cell) it.next(); TextGrid.Cell thisCell = find(cell); if(thisCell != null) remove(thisCell); } } public int getWidth(){ return getMaxX() - getMinX(); } public int getHeight(){ return getMaxY() - getMinY(); } public int getMaxX(){ int result = 0; Iterator it = iterator(); while(it.hasNext()){ TextGrid.Cell cell = (TextGrid.Cell) it.next(); if(cell.x > result) result = cell.x; } return result; } public int getMinX(){ int result = Integer.MAX_VALUE; Iterator it = iterator(); while(it.hasNext()){ TextGrid.Cell cell = (TextGrid.Cell) it.next(); if(cell.x < result) result = cell.x; } return result; } public int getMaxY(){ int result = 0; Iterator it = iterator(); while(it.hasNext()){ TextGrid.Cell cell = (TextGrid.Cell) it.next(); if(cell.y > result) result = cell.y; } return result; } public int getMinY(){ int result = Integer.MAX_VALUE; Iterator it = iterator(); while(it.hasNext()){ TextGrid.Cell cell = (TextGrid.Cell) it.next(); if(cell.y < result) result = cell.y; } return result; } public Object remove(TextGrid.Cell cell){ typeIsValid = false; cell = find(cell); if(cell != null) return internalSet.remove(cell); else return null; } public boolean equals(Object o){ CellSet otherSet = (CellSet) o; return internalSet.equals(otherSet.internalSet); } public static ArrayList removeDuplicateSets(ArrayList list) { ArrayList uniqueSets = new ArrayList(); Iterator it = list.iterator(); while(it.hasNext()){ CellSet set = (CellSet) it.next(); boolean isOriginal = true; Iterator uniquesIt = uniqueSets.iterator(); while(uniquesIt.hasNext()){ CellSet uniqueSet = (CellSet) uniquesIt.next(); if(set.equals(uniqueSet)){ isOriginal = false; } } if(isOriginal) uniqueSets.add(set); } return uniqueSets; } /** * Takes into account character info from the grid * * @return ArrayList of distinct BoundarySetS */ public ArrayList breakIntoDistinctBoundaries(TextGrid grid){ ArrayList result; AbstractionGrid temp = new AbstractionGrid(grid, this); result = temp.getDistinctShapes(); return result; } /** * * @return ArrayList of distinct BoundarySetS */ public ArrayList breakIntoDistinctBoundaries(){ ArrayList result = new ArrayList(); //CellSet tempSet = copyCellSet(this); //tempSet.translate( - this.getMinX() + 1, - this.getMinY() + 1); // TextGrid boundaryGrid = new TextGrid(tempSet.getMaxX()+2, tempSet.getMaxY()+2); // boundaryGrid.fillCellsWith(tempSet, '*'); TextGrid boundaryGrid = new TextGrid(getMaxX()+2, getMaxY()+2); boundaryGrid.fillCellsWith(this, '*'); Iterator it = iterator(); while(it.hasNext()){ TextGrid.Cell cell = (TextGrid.Cell) it.next(); if(boundaryGrid.isBlank(cell.x, cell.y)) continue; CellSet boundarySet = boundaryGrid.fillContinuousArea(cell.x, cell.y, ' '); //boundarySet.translate( this.getMinX() - 1, this.getMinY() - 1); result.add(boundarySet); } return result; } /** * * Breaks that: *
	 *  +-----+
	 *  |     |
	 *  +  ---+-------------------
	 *  |     |
	 *  +-----+
	 * 
* * into the following 3: * *
	 *  +-----+
	 *  |     |
	 *  +     +
	 *  |     |
	 *  +-----+
	 * 
	 *     ---
	 *         -------------------
	 * 
* * @param grid * @return a list of boundaries that are either open or closed but not mixed * and they are equivalent to the this */ public ArrayList breakTrulyMixedBoundaries(TextGrid grid){ ArrayList result = new ArrayList(); CellSet visitedEnds = new CellSet(); TextGrid workGrid = TextGrid.makeSameSizeAs(grid); grid.copyCellsTo(this, workGrid); if (DEBUG){ System.out.println("Breaking truly mixed boundaries below:"); workGrid.printDebug(); } Iterator it = iterator(); while(it.hasNext()){ TextGrid.Cell start = (TextGrid.Cell) it.next(); if(workGrid.isLinesEnd(start) && !visitedEnds.contains(start)){ if (DEBUG) System.out.println("Starting new subshape:"); CellSet set = new CellSet(); set.add(start); if(DEBUG) System.out.println("Added boundary "+start); TextGrid.Cell previous = start; TextGrid.Cell cell = null; CellSet nextCells = workGrid.followCell(previous); if(nextCells.size() == 0) throw new IllegalArgumentException("This shape is either open but multipart or has only one cell, and cannot be processed by this method"); cell = (TextGrid.Cell) nextCells.getFirst(); set.add(cell); if(DEBUG) System.out.println("Added boundary "+cell); boolean finished = false; if(workGrid.isLinesEnd(cell)){ visitedEnds.add(cell); finished = true; } while(!finished){ nextCells = workGrid.followCell(cell, previous); if(nextCells.size() == 1) { set.add(cell); if(DEBUG) System.out.println("Added boundary " + cell); previous = cell; cell = (TextGrid.Cell) nextCells.getFirst(); //if(!cell.equals(start) && grid.isPointCell(cell)) // s.addToPoints(makePointForCell(cell, workGrid, cellWidth, cellHeight, allRound)); if(workGrid.isLinesEnd(cell)){ visitedEnds.add(cell); finished = true; } } else if(nextCells.size() > 1) { finished = true; } } result.add(set); } } //substract all boundary sets from this CellSet CellSet whatsLeft = new CellSet(this); it = result.iterator(); while (it.hasNext()) { CellSet set = (CellSet) it.next(); whatsLeft.subtractSet(set); if(DEBUG) set.printAsGrid(); } result.add(whatsLeft); if(DEBUG) whatsLeft.printAsGrid(); return result; } public TextGrid makeIntoGrid(){ TextGrid grid = new TextGrid(getMaxX()+2, getMaxY()+2); grid.fillCellsWith(this, '*'); return grid; } public CellSet makeScaledOneThirdEquivalent(){ TextGrid gridBig = this.makeIntoGrid(); gridBig.fillCellsWith(this, '*'); if (VERBOSE_DEBUG){ System.out.println("---> making ScaledOneThirdEquivalent of:"); gridBig.printDebug(); } TextGrid gridSmall = new TextGrid((getMaxX() + 2) / 3, (getMaxY() + 2) / 3); for(int y = 0; y < gridBig.getHeight(); y++){ for(int x = 0; x < gridBig.getWidth(); x++){ TextGrid.Cell cell = gridBig.new Cell(x, y); if(!gridBig.isBlank(cell)) gridSmall.set(x/3, y/3, '*'); } } if (VERBOSE_DEBUG){ System.out.println("---> made into grid:"); gridSmall.printDebug(); } return gridSmall.getAllNonBlank(); } } ditaa-0.9/src/org/stathissideris/ascii2image/text/AbstractionGrid.java0000644000175000017500000001265311277077356024321 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.text; import java.util.ArrayList; import java.util.Iterator; /** * * @author Efstathios Sideris */ public class AbstractionGrid { private static final boolean DEBUG = false; private TextGrid grid; /** * Makes an AbstractionGrid using internalGrid as * its internal buffer * * @param internalGrid * @return */ public static AbstractionGrid makeUsingBuffer(TextGrid internalGrid){ if(internalGrid.getWidth() % 3 != 0 || internalGrid.getHeight() % 3 != 0) throw new IllegalArgumentException("Passed TextGrid must have dimensions that are divisible by 3."); AbstractionGrid result = new AbstractionGrid(internalGrid.getWidth() / 3, internalGrid.getHeight() / 3); result.setInternalBuffer(internalGrid); return result; } /** * Makes an AbstractionGrid using the cellSet * of textGrid. * * @param textGrid * @param cellSet */ public AbstractionGrid(TextGrid textGrid, CellSet cellSet){ this(textGrid.getWidth(), textGrid.getHeight()); /*this(cellSet.getWidth(), cellSet.getHeight()); cellSet = new CellSet(cellSet); cellSet.translate( - cellSet.getMinX(), - cellSet.getMinY());*/ if(DEBUG){ System.out.println("Making AbstractionGrid using buffer:"); textGrid.printDebug(); System.out.println("...and the following CellSet:"); cellSet.printAsGrid(); } Iterator it = cellSet.iterator(); while(it.hasNext()){ TextGrid.Cell cell = (TextGrid.Cell) it.next(); if(textGrid.isBlank(cell)) continue; if(textGrid.isCross(cell)){ set(cell.x, cell.y, AbstractCell.makeCross()); } else if(textGrid.isT(cell)){ set(cell.x, cell.y, AbstractCell.makeT()); } else if(textGrid.isK(cell)){ set(cell.x, cell.y, AbstractCell.makeK()); } else if(textGrid.isInverseT(cell)){ set(cell.x, cell.y, AbstractCell.makeInverseT()); } else if(textGrid.isInverseK(cell)){ set(cell.x, cell.y, AbstractCell.makeInverseK()); } else if(textGrid.isCorner1(cell)){ set(cell.x, cell.y, AbstractCell.makeCorner1()); } else if(textGrid.isCorner2(cell)){ set(cell.x, cell.y, AbstractCell.makeCorner2()); } else if(textGrid.isCorner3(cell)){ set(cell.x, cell.y, AbstractCell.makeCorner3()); } else if(textGrid.isCorner4(cell)){ set(cell.x, cell.y, AbstractCell.makeCorner4()); } else if(textGrid.isHorizontalLine(cell)){ set(cell.x, cell.y, AbstractCell.makeHorizontalLine()); } else if(textGrid.isVerticalLine(cell)){ set(cell.x, cell.y, AbstractCell.makeVerticalLine()); } else if(textGrid.isCrossOnLine(cell)){ set(cell.x, cell.y, AbstractCell.makeCross()); } else if(textGrid.isStarOnLine(cell)){ set(cell.x, cell.y, AbstractCell.makeStar()); } } if(DEBUG){ System.out.println("...the resulting AbstractionGrid is:"); grid.printDebug(); } } private AbstractionGrid(int width, int height){ grid = new TextGrid(width*3, height*3); } public TextGrid getCopyOfInternalBuffer(){ return new TextGrid(grid); } private void setInternalBuffer(TextGrid grid){ this.grid = grid; } public int getWidth(){ return grid.getWidth() / 3; } public int getHeight(){ return grid.getHeight() / 3; } public TextGrid getAsTextGrid(){ TextGrid result = new TextGrid(getWidth(), getHeight()); for(int y = 0; y < grid.getHeight(); y++){ for(int x = 0; x < grid.getWidth(); x++){ TextGrid.Cell cell = grid.new Cell(x, y); if(!grid.isBlank(cell)) result.set(x/3, y/3, '*'); } } if (DEBUG){ System.out.println("Getting AbstractionGrid as textGrid.\nAbstractionGrid:"); grid.printDebug(); System.out.println("...as text grid:"); result.printDebug(); } return result; } public ArrayList getDistinctShapes(){ ArrayList result = new ArrayList(); CellSet nonBlank = grid.getAllNonBlank(); ArrayList distinct = nonBlank.breakIntoDistinctBoundaries(); Iterator it = distinct.iterator(); while (it.hasNext()) { CellSet set = (CellSet) it.next(); AbstractionGrid temp = new AbstractionGrid(this.getWidth(), this.getHeight()); temp.fillCells(set); result.add(temp.getAsTextGrid().getAllNonBlank()); } return result; } protected void fillCells(CellSet cells){ grid.fillCellsWith(cells, '*'); } public void set(int xPos, int yPos, AbstractCell cell){ xPos *= 3; yPos *= 3; for(int y = 0; y < 3; y++){ for(int x = 0; x < 3; x++){ if(cell.rows[x][y] == 1){ grid.set(xPos + x, yPos + y, '*'); } } } } } ditaa-0.9/src/org/stathissideris/ascii2image/text/TextGrid.java0000644000175000017500000014045511303043320022744 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.text; import java.awt.Color; import java.io.*; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.stathissideris.ascii2image.core.FileUtils; import org.stathissideris.ascii2image.core.ProcessingOptions; import org.stathissideris.ascii2image.graphics.CustomShapeDefinition; /** * * @author Efstathios Sideris */ public class TextGrid { private static final boolean DEBUG = false; private ArrayList rows; private static char[] boundaries = {'/', '\\', '|', '-', '*', '=', ':'}; private static char[] undisputableBoundaries = {'|', '-', '*', '=', ':'}; private static char[] horizontalLines = {'-', '='}; private static char[] verticalLines = {'|', ':'}; private static char[] arrowHeads = {'<', '>', '^', 'v', 'V'}; private static char[] cornerChars = {'\\', '/', '+'}; private static char[] pointMarkers = {'*'}; private static char[] dashedLines = {':', '~', '='}; private static char[] entryPoints1 = {'\\'}; private static char[] entryPoints2 = {'|', ':', '+', '\\', '/'}; private static char[] entryPoints3 = {'/'}; private static char[] entryPoints4 = {'-', '=', '+', '\\', '/'}; private static char[] entryPoints5 = {'\\'}; private static char[] entryPoints6 = {'|', ':', '+', '\\', '/'}; private static char[] entryPoints7 = {'/'}; private static char[] entryPoints8 = {'-', '=', '+', '\\', '/'}; private static HashMap humanColorCodes = new HashMap(); static { humanColorCodes.put("GRE", "9D9"); humanColorCodes.put("BLU", "55B"); humanColorCodes.put("PNK", "FAA"); humanColorCodes.put("RED", "E32"); humanColorCodes.put("YEL", "FF3"); humanColorCodes.put("BLK", "000"); } private static HashSet markupTags = new HashSet(); static { markupTags.add("d"); markupTags.add("s"); markupTags.add("io"); markupTags.add("c"); markupTags.add("mo"); markupTags.add("tr"); markupTags.add("o"); } public void addToMarkupTags(Collection tags){ markupTags.addAll(tags); } public static void main(String[] args) throws Exception { TextGrid grid = new TextGrid(); grid.loadFrom("tests/text/art10.txt"); grid.writeStringTo(grid.new Cell(28, 1), "testing"); grid.findMarkupTags(); grid.printDebug(); //System.out.println(grid.fillContinuousArea(0, 0, '-').size()+" cells filled"); //grid.fillContinuousArea(4, 4, '-'); //grid.getSubGrid(1,1,3,3).printDebug(); //grid.printDebug(); } public TextGrid(){ rows = new ArrayList(); } public TextGrid(int width, int height){ String space = StringUtils.repeatString(" ", width); rows = new ArrayList(); for(int i = 0; i < height; i++) rows.add(new StringBuffer(space)); } public static TextGrid makeSameSizeAs(TextGrid grid){ return new TextGrid(grid.getWidth(), grid.getHeight()); } public TextGrid(TextGrid otherGrid){ rows = new ArrayList(); for(StringBuffer row : otherGrid.getRows()) { rows.add(new StringBuffer(row)); } } public void clear(){ String blank = StringUtils.repeatString(" ", getWidth()); int height = getHeight(); rows.clear(); for(int i = 0; i < height; i++) rows.add(new StringBuffer(blank)); } // duplicated code due to lots of hits to this function public char get(int x, int y){ if(x > getWidth() - 1 || y > getHeight() - 1 || x < 0 || y < 0) return 0; return rows.get(y).charAt(x); } //duplicated code due to lots of hits to this function public char get(Cell cell){ if(cell.x > getWidth() - 1 || cell.y > getHeight() - 1 || cell.x < 0 || cell.y < 0) return 0; return rows.get(cell.y).charAt(cell.x); } public StringBuffer getRow(int y){ return rows.get(y); } public TextGrid getSubGrid(int x, int y, int width, int height){ TextGrid grid = new TextGrid(width, height); for(int i = 0; i < height; i++){ grid.setRow(i, new StringBuffer(getRow(y + i).subSequence(x, x + width))); } return grid; } public TextGrid getTestingSubGrid(Cell cell){ return getSubGrid(cell.x - 1, cell.y - 1, 3, 3); } public String getStringAt(int x, int y, int length){ return getStringAt(new Cell(x, y), length); } public String getStringAt(Cell cell, int length){ int x = cell.x; int y = cell.y; if(x > getWidth() - 1 || y > getHeight() - 1 || x < 0 || y < 0) return null; return rows.get(y).substring(x, x + length); } public char getNorthOf(int x, int y){ return get(x, y - 1); } public char getSouthOf(int x, int y){ return get(x, y + 1); } public char getEastOf(int x, int y){ return get(x + 1, y); } public char getWestOf(int x, int y){ return get(x - 1, y); } public char getNorthOf(Cell cell){ return getNorthOf(cell.x, cell.y); } public char getSouthOf(Cell cell){ return getSouthOf(cell.x, cell.y); } public char getEastOf(Cell cell){ return getEastOf(cell.x, cell.y); } public char getWestOf(Cell cell){ return getWestOf(cell.x, cell.y); } public void writeStringTo(int x, int y, String str){ writeStringTo(new Cell(x, y), str); } public void writeStringTo(Cell cell, String str){ if(isOutOfBounds(cell)) return; rows.get(cell.y).replace(cell.x, cell.x + str.length(), str); } public void set(Cell cell, char c){ set(cell.x, cell.y, c); } public void set(int x, int y, char c){ if(x > getWidth() - 1 || y > getHeight() - 1) return; StringBuffer row = rows.get(y); row.setCharAt(x, c); } public void setRow(int y, String row){ if(y > getHeight() || row.length() != getWidth()) throw new IllegalArgumentException("setRow out of bounds or string wrong size"); rows.set(y, new StringBuffer(row)); } public void setRow(int y, StringBuffer row){ if(y > getHeight() || row.length() != getWidth()) throw new IllegalArgumentException("setRow out of bounds or string wrong size"); rows.set(y, row); } public int getWidth(){ if(rows.size() == 0) return 0; //empty buffer return rows.get(0).length(); } public int getHeight(){ return rows.size(); } public void printDebug(){ Iterator it = rows.iterator(); int i = 0; System.out.println( " " +StringUtils.repeatString("0123456789", (int) Math.floor(getWidth()/10)+1)); while(it.hasNext()){ String row = it.next().toString(); String index = new Integer(i).toString(); if(i < 10) index = " "+index; System.out.println(index+" ("+row+")"); i++; } } public String getDebugString(){ StringBuffer buffer = new StringBuffer(); Iterator it = rows.iterator(); int i = 0; buffer.append( " " +StringUtils.repeatString("0123456789", (int) Math.floor(getWidth()/10)+1)+"\n"); while(it.hasNext()){ String row = it.next().toString(); String index = new Integer(i).toString(); if(i < 10) index = " "+index; row = row.replaceAll("\n", "\\\\n"); row = row.replaceAll("\r", "\\\\r"); buffer.append(index+" ("+row+")\n"); i++; } return buffer.toString(); } public String toString(){ return getDebugString(); } /** * Adds grid to this. Space characters in this grid * are replaced with the corresponding contents of * grid, otherwise the contents are unchanged. * * @param grid * @return false if the grids are of different size */ public boolean add(TextGrid grid){ if(getWidth() != grid.getWidth() || getHeight() != grid.getHeight()) return false; int width = getWidth(); int height = getHeight(); for(int yi = 0; yi < height; yi++){ for(int xi = 0; xi < width; xi++){ if(get(xi, yi) == ' ') set(xi, yi, grid.get(xi, yi)); } } return true; } /** * Replaces letters or numbers that are on horizontal or vertical * lines, with the appropriate character that will make the line * continuous (| for vertical and - for horizontal lines) * */ public void replaceTypeOnLine(){ int width = getWidth(); int height = getHeight(); for(int yi = 0; yi < height; yi++){ for(int xi = 0; xi < width; xi++){ char c = get(xi, yi); if(Character.isLetterOrDigit(c)) { boolean isOnHorizontalLine = isOnHorizontalLine(xi, yi); boolean isOnVerticalLine = isOnVerticalLine(xi, yi); if(isOnHorizontalLine && isOnVerticalLine){ set(xi, yi, '+'); if(DEBUG) System.out.println("replaced type on line '"+c+"' with +"); } else if(isOnHorizontalLine){ set(xi, yi, '-'); if(DEBUG) System.out.println("replaced type on line '"+c+"' with -"); } else if(isOnVerticalLine){ set(xi, yi, '|'); if(DEBUG) System.out.println("replaced type on line '"+c+"' with |"); } } } } } public void replacePointMarkersOnLine(){ int width = getWidth(); int height = getHeight(); for(int yi = 0; yi < height; yi++){ for(int xi = 0; xi < width; xi++){ char c = get(xi, yi); Cell cell = new Cell(xi, yi); if(StringUtils.isOneOf(c, pointMarkers) && isStarOnLine(cell)){ boolean isOnHorizontalLine = false; if(StringUtils.isOneOf(get(cell.getEast()), horizontalLines)) isOnHorizontalLine = true; if(StringUtils.isOneOf(get(cell.getWest()), horizontalLines)) isOnHorizontalLine = true; boolean isOnVerticalLine = false; if(StringUtils.isOneOf(get(cell.getNorth()), verticalLines)) isOnVerticalLine = true; if(StringUtils.isOneOf(get(cell.getSouth()), verticalLines)) isOnVerticalLine = true; if(isOnHorizontalLine && isOnVerticalLine){ set(xi, yi, '+'); if(DEBUG) System.out.println("replaced marker on line '"+c+"' with +"); } else if(isOnHorizontalLine){ set(xi, yi, '-'); if(DEBUG) System.out.println("replaced marker on line '"+c+"' with -"); } else if(isOnVerticalLine){ set(xi, yi, '|'); if(DEBUG) System.out.println("replaced marker on line '"+c+"' with |"); } } } } } public CellSet getPointMarkersOnLine(){ CellSet result = new CellSet(); int width = getWidth(); int height = getHeight(); for(int yi = 0; yi < height; yi++){ for(int xi = 0; xi < width; xi++){ char c = get(xi, yi); if(StringUtils.isOneOf(c, pointMarkers) && isStarOnLine(new Cell(xi, yi))){ result.add(new Cell(xi, yi)); } } } return result; } public void replaceHumanColorCodes(){ int height = getHeight(); for(int y = 0; y < height; y++){ String row = rows.get(y).toString(); Iterator it = humanColorCodes.keySet().iterator(); while(it.hasNext()){ String humanCode = (String) it.next(); String hexCode = (String) humanColorCodes.get(humanCode); if(hexCode != null){ humanCode = "c" + humanCode; hexCode = "c" + hexCode; row = row.replaceAll(humanCode, hexCode); rows.set(y, new StringBuffer(row)); //TODO: this is not the most efficient way to do this row = rows.get(y).toString(); } } } } /** * Replace all occurences of c1 with c2 * * @param c1 * @param c2 */ public void replaceAll(char c1, char c2){ int width = getWidth(); int height = getHeight(); for(int yi = 0; yi < height; yi++){ for(int xi = 0; xi < width; xi++){ char c = get(xi, yi); if(c == c1) set(xi, yi, c2); } } } public boolean hasBlankCells(){ CellSet set = new CellSet(); int width = getWidth(); int height = getHeight(); for(int y = 0; y < height; y++){ for(int x = 0; x < width; x++){ Cell cell = new Cell(x, y); if(isBlank(cell)) return true; } } return false; } public CellSet getAllNonBlank(){ CellSet set = new CellSet(); int width = getWidth(); int height = getHeight(); for(int y = 0; y < height; y++){ for(int x = 0; x < width; x++){ Cell cell = new Cell(x, y); if(!isBlank(cell)) set.add(cell); } } return set; } public CellSet getAllBoundaries(){ CellSet set = new CellSet(); int width = getWidth(); int height = getHeight(); for(int y = 0; y < height; y++){ for(int x = 0; x < width; x++){ Cell cell = new Cell(x, y); if(isBoundary(cell)) set.add(cell); } } return set; } public CellSet getAllBlanksBetweenCharacters(){ CellSet set = new CellSet(); int width = getWidth(); int height = getHeight(); for(int y = 0; y < height; y++){ for(int x = 0; x < width; x++){ Cell cell = new Cell(x, y); if(isBlankBetweenCharacters(cell)) set.add(cell); } } return set; } /** * Returns an ArrayList of CellStringPairs that * represents all the continuous (non-blank) Strings * in the grid. Used on buffers that contain only * type, in order to find the positions and the * contents of the strings. * * @return */ public ArrayList findStrings(){ ArrayList result = new ArrayList(); int width = getWidth(); int height = getHeight(); for(int y = 0; y < height; y++){ for(int x = 0; x < width; x++){ if(!isBlank(x, y)){ Cell start = new Cell(x, y); String str = String.valueOf(get(x,y)); char c = get(++x, y); boolean finished = false; //while(c != ' '){ while(!finished){ str += String.valueOf(c); c = get(++x, y); char next = get(x + 1, y); if((c == ' ' || c == 0) && (next == ' ' || next == 0)) finished = true; } result.add(new CellStringPair(start, str)); } } } return result; } /** * This is done in a bit of a messy way, should be impossible * to go out of sync with corresponding GridPatternGroup. * * @param cell * @param entryPointId * @return */ public boolean hasEntryPoint(Cell cell, int entryPointId){ String result = ""; char c = get(cell); if(entryPointId == 1) { return StringUtils.isOneOf(c, entryPoints1); } else if(entryPointId == 2) { return StringUtils.isOneOf(c, entryPoints2); } else if(entryPointId == 3) { return StringUtils.isOneOf(c, entryPoints3); } else if(entryPointId == 4) { return StringUtils.isOneOf(c, entryPoints4); } else if(entryPointId == 5) { return StringUtils.isOneOf(c, entryPoints5); } else if(entryPointId == 6) { return StringUtils.isOneOf(c, entryPoints6); } else if(entryPointId == 7) { return StringUtils.isOneOf(c, entryPoints7); } else if(entryPointId == 8) { return StringUtils.isOneOf(c, entryPoints8); } return false; } /** * true if cell is blank and the east and west cells are not * (used to find gaps between words) * * @param cell * @return */ public boolean isBlankBetweenCharacters(Cell cell){ return (isBlank(cell) && !isBlank(cell.getEast()) && !isBlank(cell.getWest())); } /** * Makes blank all the cells that contain non-text * elements. */ public void removeNonText(){ //the following order is significant //since the south-pointing arrowheads //are determined based on the surrounding boundaries removeArrowheads(); removeColorCodes(); removeBoundaries(); removeMarkupTags(); } public void removeArrowheads(){ int width = getWidth(); int height = getHeight(); for(int yi = 0; yi < height; yi++){ for(int xi = 0; xi < width; xi++){ Cell cell = new Cell(xi, yi); if(isArrowhead(cell)) set(cell, ' '); } } } public void removeColorCodes(){ Iterator cells = findColorCodes().iterator(); while(cells.hasNext()){ Cell cell = ((CellColorPair) cells.next()).cell; set(cell, ' '); cell = cell.getEast(); set(cell, ' '); cell = cell.getEast(); set(cell, ' '); cell = cell.getEast(); set(cell, ' '); } } public void removeBoundaries(){ ArrayList toBeRemoved = new ArrayList(); int width = getWidth(); int height = getHeight(); for(int yi = 0; yi < height; yi++){ for(int xi = 0; xi < width; xi++){ Cell cell = new Cell(xi, yi); if(isBoundary(cell)) toBeRemoved.add(cell); } } //remove in two stages, because decision of //isBoundary depends on contants of surrounding //cells Iterator it = toBeRemoved.iterator(); while(it.hasNext()){ Cell cell = (Cell) it.next(); set(cell, ' '); } } public ArrayList findArrowheads(){ ArrayList result = new ArrayList(); int width = getWidth(); int height = getHeight(); for(int yi = 0; yi < height; yi++){ for(int xi = 0; xi < width; xi++){ Cell cell = new Cell(xi, yi); if(isArrowhead(cell)) result.add(cell); } } if(DEBUG) System.out.println(result.size()+" arrowheads found"); return result; } public ArrayList findColorCodes(){ Pattern colorCodePattern = Pattern.compile("c[A-F0-9]{3}"); ArrayList result = new ArrayList(); int width = getWidth(); int height = getHeight(); for(int yi = 0; yi < height; yi++){ for(int xi = 0; xi < width - 3; xi++){ Cell cell = new Cell(xi, yi); String s = getStringAt(cell, 4); Matcher matcher = colorCodePattern.matcher(s); if(matcher.matches()){ char cR = s.charAt(1); char cG = s.charAt(2); char cB = s.charAt(3); int r = Integer.valueOf(String.valueOf(cR), 16).intValue() * 17; int g = Integer.valueOf(String.valueOf(cG), 16).intValue() * 17; int b = Integer.valueOf(String.valueOf(cB), 16).intValue() * 17; result.add(new CellColorPair(cell, new Color(r, g, b))); } } } if(DEBUG) System.out.println(result.size()+" color codes found"); return result; } public ArrayList findMarkupTags(){ Pattern tagPattern = Pattern.compile("\\{(.+?)\\}"); ArrayList result = new ArrayList(); int width = getWidth(); int height = getHeight(); for(int y = 0; y < height; y++){ for(int x = 0; x < width - 3; x++){ Cell cell = new Cell(x, y); char c = get(cell); if(c == '{'){ String rowPart = rows.get(y).substring(x); Matcher matcher = tagPattern.matcher(rowPart); if(matcher.find()){ String tagName = matcher.group(1); if(markupTags.contains(tagName)){ if(DEBUG) System.out.println("found tag "+tagName+" at "+x+", "+y); result.add(new CellTagPair(new Cell(x, y), tagName)); } } } } } return result; } public void removeMarkupTags(){ Iterator it = findMarkupTags().iterator(); while (it.hasNext()) { CellTagPair pair = (CellTagPair) it.next(); String tagName = pair.tag; if(tagName == null) continue; int length = 2 + tagName.length(); writeStringTo(pair.cell, StringUtils.repeatString(" ", length)); } } public boolean matchesAny(GridPatternGroup criteria){ return criteria.isAnyMatchedBy(this); } public boolean matchesAll(GridPatternGroup criteria){ return criteria.areAllMatchedBy(this); } public boolean matches(GridPattern criteria){ return criteria.isMatchedBy(this); } public boolean isOnHorizontalLine(Cell cell){ return isOnHorizontalLine(cell.x, cell.y); } private boolean isOnHorizontalLine(int x, int y){ char c1 = get(x - 1, y); char c2 = get(x + 1, y); if(isHorizontalLine(c1) && isHorizontalLine(c2)) return true; return false; } public boolean isOnVerticalLine(Cell cell){ return isOnVerticalLine(cell.x, cell.y); } private boolean isOnVerticalLine(int x, int y){ char c1 = get(x, y - 1); char c2 = get(x, y + 1); if(isVerticalLine(c1) && isVerticalLine(c2)) return true; return false; } public static boolean isBoundary(char c){ return StringUtils.isOneOf(c, boundaries); } public boolean isBoundary(int x, int y){ return isBoundary(new Cell(x, y)); } public boolean isBoundary(Cell cell){ char c = get(cell.x, cell.y); if(0 == c) return false; if('+' == c || '\\' == c || '/' == c){ System.out.print(""); if( isIntersection(cell) || isCorner(cell) || isStub(cell) || isCrossOnLine(cell)){ return true; } else return false; } //return StringUtils.isOneOf(c, undisputableBoundaries); if(StringUtils.isOneOf(c, boundaries) && !isLoneDiagonal(cell)){ return true; } return false; } public boolean isLine(Cell cell){ return isHorizontalLine(cell) || isVerticalLine(cell); } public static boolean isHorizontalLine(char c){ return StringUtils.isOneOf(c, horizontalLines); } public boolean isHorizontalLine(Cell cell){ return isHorizontalLine(cell.x, cell.y); } public boolean isHorizontalLine(int x, int y){ char c = get(x, y); if(0 == c) return false; return StringUtils.isOneOf(c, horizontalLines); } public static boolean isVerticalLine(char c){ return StringUtils.isOneOf(c, verticalLines); } public boolean isVerticalLine(Cell cell){ return isVerticalLine(cell.x, cell.y); } public boolean isVerticalLine(int x, int y){ char c = get(x, y); if(0 == c) return false; return StringUtils.isOneOf(c, verticalLines); } public boolean isLinesEnd(int x, int y){ return isLinesEnd(new Cell(x, y)); } /** * Stubs are also considered end of lines * * @param cell * @return */ public boolean isLinesEnd(Cell cell){ return matchesAny(cell, GridPatternGroup.linesEndCriteria); } public boolean isVerticalLinesEnd(Cell cell){ return matchesAny(cell, GridPatternGroup.verticalLinesEndCriteria); } public boolean isHorizontalLinesEnd(Cell cell){ return matchesAny(cell, GridPatternGroup.horizontalLinesEndCriteria); } public boolean isPointCell(Cell cell){ return ( isCorner(cell) || isIntersection(cell) || isStub(cell) || isLinesEnd(cell)); } public boolean containsAtLeastOneDashedLine(CellSet set){ Iterator it = set.iterator(); while(it.hasNext()) { Cell cell = (Cell) it.next(); if(StringUtils.isOneOf(get(cell), dashedLines)) return true; } return false; } public boolean exactlyOneNeighbourIsBoundary(Cell cell) { int howMany = 0; if(isBoundary(cell.getNorth())) howMany++; if(isBoundary(cell.getSouth())) howMany++; if(isBoundary(cell.getEast())) howMany++; if(isBoundary(cell.getWest())) howMany++; return (howMany == 1); } /** * * A stub looks like that: * *
	 * 
	 * +- or -+ or + or + or /- or -/ or / (you get the point)
	 *             |    |                |
	 * 
	 * 
* * @param cell * @return */ public boolean isStub(Cell cell){ return matchesAny(cell, GridPatternGroup.stubCriteria); } public boolean isCrossOnLine(Cell cell){ return matchesAny(cell, GridPatternGroup.crossOnLineCriteria); } public boolean isHorizontalCrossOnLine(Cell cell){ return matchesAny(cell, GridPatternGroup.horizontalCrossOnLineCriteria); } public boolean isVerticalCrossOnLine(Cell cell){ return matchesAny(cell, GridPatternGroup.verticalCrossOnLineCriteria); } public boolean isStarOnLine(Cell cell){ return matchesAny(cell, GridPatternGroup.starOnLineCriteria); } public boolean isLoneDiagonal(Cell cell){ return matchesAny(cell, GridPatternGroup.loneDiagonalCriteria); } public boolean isHorizontalStarOnLine(Cell cell){ return matchesAny(cell, GridPatternGroup.horizontalStarOnLineCriteria); } public boolean isVerticalStarOnLine(Cell cell){ return matchesAny(cell, GridPatternGroup.verticalStarOnLineCriteria); } public boolean isArrowhead(Cell cell){ return (isNorthArrowhead(cell) || isSouthArrowhead(cell) || isWestArrowhead(cell) || isEastArrowhead(cell)); } public boolean isNorthArrowhead(Cell cell){ return get(cell) == '^'; } public boolean isEastArrowhead(Cell cell){ return get(cell) == '>'; } public boolean isWestArrowhead(Cell cell){ return get(cell) == '<'; } public boolean isSouthArrowhead(Cell cell){ return (get(cell) == 'v' || get(cell) == 'V') && isVerticalLine(cell.getNorth()); } // unicode for bullets // // 2022 bullet // 25CF black circle // 25AA black circle (small) // 25A0 black square // 25A1 white square // 25CB white circle // 25BA black right-pointing pointer public boolean isBullet(int x, int y){ return isBullet(new Cell(x, y)); } public boolean isBullet(Cell cell){ char c = get(cell); if((c == 'o' || c == '*') && isBlank(cell.getEast()) && isBlank(cell.getWest()) && Character.isLetterOrDigit(get(cell.getEast().getEast())) ) return true; return false; } public void replaceBullets(){ int width = getWidth(); int height = getHeight(); for(int yi = 0; yi < height; yi++){ for(int xi = 0; xi < width; xi++){ Cell cell = new Cell(xi, yi); if(isBullet(cell)){ set(cell, ' '); set(cell.getEast(), '\u2022'); } } } } /** * true if the cell is not blank * but the previous (west) is * * @param cell * @return */ public boolean isStringsStart(Cell cell){ return (!isBlank(cell) && isBlank(cell.getWest())); } /** * true if the cell is not blank * but the next (east) is * * @param cell * @return */ public boolean isStringsEnd(Cell cell){ return (!isBlank(cell) //&& (isBlank(cell.getEast()) || get(cell.getEast()) == 0)); && isBlank(cell.getEast())); } public int otherStringsStartInTheSameColumn(Cell cell){ if(!isStringsStart(cell)) return 0; int result = 0; int height = getHeight(); for(int y = 0; y < height; y++){ Cell cCell = new Cell(cell.x, y); if(!cCell.equals(cell) && isStringsStart(cCell)){ result++; } } return result; } public int otherStringsEndInTheSameColumn(Cell cell){ if(!isStringsEnd(cell)) return 0; int result = 0; int height = getHeight(); for(int y = 0; y < height; y++){ Cell cCell = new Cell(cell.x, y); if(!cCell.equals(cell) && isStringsEnd(cCell)){ result++; } } return result; } public boolean isColumnBlank(int x){ int height = getHeight(); for(int y = 0; y < height; y++){ if(!isBlank(x, y)) return false; } return true; } public CellSet followLine(int x, int y){ return followLine(new Cell(x, y)); } public CellSet followIntersection(Cell cell){ return followIntersection(cell, null); } public CellSet followIntersection(Cell cell, Cell blocked){ if(!isIntersection(cell)) return null; CellSet result = new CellSet(); Cell cN = cell.getNorth(); Cell cS = cell.getSouth(); Cell cE = cell.getEast(); Cell cW = cell.getWest(); if(hasEntryPoint(cN, 6)) result.add(cN); if(hasEntryPoint(cS, 2)) result.add(cS); if(hasEntryPoint(cE, 8)) result.add(cE); if(hasEntryPoint(cW, 4)) result.add(cW); if(result.contains(blocked)) result.remove(blocked); return result; } /** * Returns the neighbours of a line-cell that are boundaries * (0 to 2 cells are returned) * * @param cell * @return null if the cell is not a line */ public CellSet followLine(Cell cell){ if(isHorizontalLine(cell)){ CellSet result = new CellSet(); if(isBoundary(cell.getEast())) result.add(cell.getEast()); if(isBoundary(cell.getWest())) result.add(cell.getWest()); return result; } else if (isVerticalLine(cell)){ CellSet result = new CellSet(); if(isBoundary(cell.getNorth())) result.add(cell.getNorth()); if(isBoundary(cell.getSouth())) result.add(cell.getSouth()); return result; } return null; } public CellSet followLine(Cell cell, Cell blocked){ CellSet nextCells = followLine(cell); if(nextCells.contains(blocked)) nextCells.remove(blocked); return nextCells; } public CellSet followCorner(Cell cell){ return followCorner(cell, null); } public CellSet followCorner(Cell cell, Cell blocked){ if(!isCorner(cell)) return null; if(isCorner1(cell)) return followCorner1(cell, blocked); if(isCorner2(cell)) return followCorner2(cell, blocked); if(isCorner3(cell)) return followCorner3(cell, blocked); if(isCorner4(cell)) return followCorner4(cell, blocked); return null; } public CellSet followCorner1(Cell cell){ return followCorner1(cell, null); } public CellSet followCorner1(Cell cell, Cell blocked){ if(!isCorner1(cell)) return null; CellSet result = new CellSet(); if(!cell.getSouth().equals(blocked)) result.add(cell.getSouth()); if(!cell.getEast().equals(blocked)) result.add(cell.getEast()); return result; } public CellSet followCorner2(Cell cell){ return followCorner2(cell, null); } public CellSet followCorner2(Cell cell, Cell blocked){ if(!isCorner2(cell)) return null; CellSet result = new CellSet(); if(!cell.getSouth().equals(blocked)) result.add(cell.getSouth()); if(!cell.getWest().equals(blocked)) result.add(cell.getWest()); return result; } public CellSet followCorner3(Cell cell){ return followCorner3(cell, null); } public CellSet followCorner3(Cell cell, Cell blocked){ if(!isCorner3(cell)) return null; CellSet result = new CellSet(); if(!cell.getNorth().equals(blocked)) result.add(cell.getNorth()); if(!cell.getWest().equals(blocked)) result.add(cell.getWest()); return result; } public CellSet followCorner4(Cell cell){ return followCorner4(cell, null); } public CellSet followCorner4(Cell cell, Cell blocked){ if(!isCorner4(cell)) return null; CellSet result = new CellSet(); if(!cell.getNorth().equals(blocked)) result.add(cell.getNorth()); if(!cell.getEast().equals(blocked)) result.add(cell.getEast()); return result; } public CellSet followStub(Cell cell){ return followStub(cell, null); } public CellSet followStub(Cell cell, Cell blocked){ if(!isStub(cell)) return null; CellSet result = new CellSet(); if(isBoundary(cell.getEast())) result.add(cell.getEast()); else if(isBoundary(cell.getWest())) result.add(cell.getWest()); else if(isBoundary(cell.getNorth())) result.add(cell.getNorth()); else if(isBoundary(cell.getSouth())) result.add(cell.getSouth()); if(result.contains(blocked)) result.remove(blocked); return result; } public CellSet followCell(Cell cell){ return followCell(cell, null); } public CellSet followCell(Cell cell, Cell blocked){ if(isIntersection(cell)) return followIntersection(cell, blocked); if(isCorner(cell)) return followCorner(cell, blocked); if(isLine(cell)) return followLine(cell, blocked); if(isStub(cell)) return followStub(cell, blocked); if(isCrossOnLine(cell)) return followCrossOnLine(cell, blocked); System.err.println("Umbiguous input at position "+cell+":"); TextGrid subGrid = getTestingSubGrid(cell); subGrid.printDebug(); throw new RuntimeException("Cannot follow cell "+cell+": cannot determine cell type"); } public String getCellTypeAsString(Cell cell){ if(isK(cell)) return "K"; if(isT(cell)) return "T"; if(isInverseK(cell)) return "inverse K"; if(isInverseT(cell)) return "inverse T"; if(isCorner1(cell)) return "corner 1"; if(isCorner2(cell)) return "corner 2"; if(isCorner3(cell)) return "corner 3"; if(isCorner4(cell)) return "corner 4"; if(isLine(cell)) return "line"; if(isStub(cell)) return "stub"; if(isCrossOnLine(cell)) return "crossOnLine"; return "unrecognisable type"; } public CellSet followCrossOnLine(Cell cell, Cell blocked){ CellSet result = new CellSet(); if(isHorizontalCrossOnLine(cell)){ result.add(cell.getEast()); result.add(cell.getWest()); } else if(isVerticalCrossOnLine(cell)){ result.add(cell.getNorth()); result.add(cell.getSouth()); } if(result.contains(blocked)) result.remove(blocked); return result; } public boolean isOutOfBounds(Cell cell){ if(cell.x > getWidth() - 1 || cell.y > getHeight() - 1 || cell.x < 0 || cell.y < 0) return true; return false; } public boolean isOutOfBounds(int x, int y){ char c = get(x, y); if(0 == c) return true; return false; } public boolean isBlank(Cell cell){ char c = get(cell); if(0 == c) return false; return c == ' '; } public boolean isBlank(int x, int y){ char c = get(x, y); if(0 == c) return true; return c == ' '; } public boolean isCorner(Cell cell){ return isCorner(cell.x, cell.y); } public boolean isCorner(int x, int y){ return (isNormalCorner(x,y) || isRoundCorner(x,y)); } public boolean matchesAny(Cell cell, GridPatternGroup criteria){ TextGrid subGrid = getTestingSubGrid(cell); return subGrid.matchesAny(criteria); } public boolean isCorner1(Cell cell){ return matchesAny(cell, GridPatternGroup.corner1Criteria); } public boolean isCorner2(Cell cell){ return matchesAny(cell, GridPatternGroup.corner2Criteria); } public boolean isCorner3(Cell cell){ return matchesAny(cell, GridPatternGroup.corner3Criteria); } public boolean isCorner4(Cell cell){ return matchesAny(cell, GridPatternGroup.corner4Criteria); } public boolean isCross(Cell cell){ return matchesAny(cell, GridPatternGroup.crossCriteria); } public boolean isK(Cell cell){ return matchesAny(cell, GridPatternGroup.KCriteria); } public boolean isInverseK(Cell cell){ return matchesAny(cell, GridPatternGroup.inverseKCriteria); } public boolean isT(Cell cell){ return matchesAny(cell, GridPatternGroup.TCriteria); } public boolean isInverseT(Cell cell){ return matchesAny(cell, GridPatternGroup.inverseTCriteria); } public boolean isNormalCorner(Cell cell){ return matchesAny(cell, GridPatternGroup.normalCornerCriteria); } public boolean isNormalCorner(int x, int y){ return isNormalCorner(new Cell(x, y)); } public boolean isRoundCorner(Cell cell){ return matchesAny(cell, GridPatternGroup.roundCornerCriteria); } public boolean isRoundCorner(int x, int y){ return isRoundCorner(new Cell(x, y)); } public boolean isIntersection(Cell cell){ return matchesAny(cell, GridPatternGroup.intersectionCriteria); } public boolean isIntersection(int x, int y){ return isIntersection(new Cell(x, y)); } public void copyCellsTo(CellSet cells, TextGrid grid){ Iterator it = cells.iterator(); while(it.hasNext()){ Cell cell = (Cell) it.next(); grid.set(cell, this.get(cell)); } } public boolean equals(TextGrid grid){ if(grid.getHeight() != this.getHeight() || grid.getWidth() != this.getWidth() ){ return false; } int height = grid.getHeight(); for(int i = 0; i < height; i++){ String row1 = this.getRow(i).toString(); String row2 = grid.getRow(i).toString(); if(!row1.equals(row2)) return false; } return true; } // @Override // public int hashCode() { // int h = 0; // for(StringBuffer row : rows) { // h += row.hashCode(); // } // return h; // } /** * Fills all the cells in cells with c * * @param cells * @param c */ public void fillCellsWith(Iterable cells, char c){ Iterator it = cells.iterator(); while(it.hasNext()){ Cell cell = it.next(); set(cell.x, cell.y, c); } } /** * * Fills the continuous area with if c1 characters with c2, * flooding from cell x, y * * @param x * @param y * @param c1 the character to replace * @param c2 the character to replace c1 with * @return the list of cells filled */ // public CellSet fillContinuousArea(int x, int y, char c1, char c2){ // CellSet cells = new CellSet(); // //fillContinuousArea_internal(x, y, c1, c2, cells); // seedFill(new Cell(x, y), c1, c2); // return cells; // } public CellSet fillContinuousArea(int x, int y, char c){ return fillContinuousArea(new Cell(x, y), c); } public CellSet fillContinuousArea(Cell cell, char c){ if(isOutOfBounds(cell)) throw new IllegalArgumentException("Attempted to fill area out of bounds: "+cell); return seedFillOld(cell, c); } private CellSet seedFill(Cell seed, char newChar){ CellSet cellsFilled = new CellSet(); char oldChar = get(seed); if(oldChar == newChar) return cellsFilled; if(isOutOfBounds(seed)) return cellsFilled; Stack stack = new Stack(); stack.push(seed); while(!stack.isEmpty()){ Cell cell = (Cell) stack.pop(); //set(cell, newChar); cellsFilled.add(cell); Cell nCell = cell.getNorth(); Cell sCell = cell.getSouth(); Cell eCell = cell.getEast(); Cell wCell = cell.getWest(); if(get(nCell) == oldChar && !cellsFilled.contains(nCell)) stack.push(nCell); if(get(sCell) == oldChar && !cellsFilled.contains(sCell)) stack.push(sCell); if(get(eCell) == oldChar && !cellsFilled.contains(eCell)) stack.push(eCell); if(get(wCell) == oldChar && !cellsFilled.contains(wCell)) stack.push(wCell); } return cellsFilled; } private CellSet seedFillOld(Cell seed, char newChar){ CellSet cellsFilled = new CellSet(); char oldChar = get(seed); if(oldChar == newChar) return cellsFilled; if(isOutOfBounds(seed)) return cellsFilled; Stack stack = new Stack(); stack.push(seed); while(!stack.isEmpty()){ Cell cell = (Cell) stack.pop(); set(cell, newChar); cellsFilled.add(cell); Cell nCell = cell.getNorth(); Cell sCell = cell.getSouth(); Cell eCell = cell.getEast(); Cell wCell = cell.getWest(); if(get(nCell) == oldChar) stack.push(nCell); if(get(sCell) == oldChar) stack.push(sCell); if(get(eCell) == oldChar) stack.push(eCell); if(get(wCell) == oldChar) stack.push(wCell); } return cellsFilled; } /** * * Locates and returns the '*' boundaries that we would * encounter if we did a flood-fill at seed. * * @param seed * @return */ public CellSet findBoundariesExpandingFrom(Cell seed){ CellSet boundaries = new CellSet(); char oldChar = get(seed); if(isOutOfBounds(seed)) return boundaries; char newChar = 1; //TODO: kludge Stack stack = new Stack(); stack.push(seed); while(!stack.isEmpty()){ Cell cell = (Cell) stack.pop(); set(cell, newChar); Cell nCell = cell.getNorth(); Cell sCell = cell.getSouth(); Cell eCell = cell.getEast(); Cell wCell = cell.getWest(); if(get(nCell) == oldChar) stack.push(nCell); else if(get(nCell) == '*') boundaries.add(nCell); if(get(sCell) == oldChar) stack.push(sCell); else if(get(sCell) == '*') boundaries.add(sCell); if(get(eCell) == oldChar) stack.push(eCell); else if(get(eCell) == '*') boundaries.add(eCell); if(get(wCell) == oldChar) stack.push(wCell); else if(get(wCell) == '*') boundaries.add(wCell); } return boundaries; } //TODO: incomplete method seedFillLine() private CellSet seedFillLine(Cell cell, char newChar){ CellSet cellsFilled = new CellSet(); Stack stack = new Stack(); char oldChar = get(cell); if(oldChar == newChar) return cellsFilled; if(isOutOfBounds(cell)) return cellsFilled; stack.push(new LineSegment(cell.x, cell.x, cell.y, 1)); stack.push(new LineSegment(cell.x, cell.x, cell.y + 1, -1)); int left; while(!stack.isEmpty()){ LineSegment segment = (LineSegment) stack.pop(); int x; //expand to the left for( x = segment.x1; x >= 0 && get(x, segment.y) == oldChar; --x){ set(x, segment.y, newChar); cellsFilled.add(new Cell(x, segment.y)); } left = cell.getEast().x; boolean skip = (x > segment.x1)? true : false; if(left < segment.x1){ //leak on left? //TODO: i think the first param should be x stack.push( //new LineSegment(segment.y, left, segment.x1 - 1, -segment.dy)); new LineSegment(x, left, segment.y - 1, -segment.dy)); } x = segment.x1 + 1; do { if(!skip) { for( ; x < getWidth() && get(x, segment.y) == oldChar; ++x){ set(x, segment.y, newChar); cellsFilled.add(new Cell(x, segment.y)); } stack.push(new LineSegment(left, x - 1, segment.y, segment.dy)); if(x > segment.x2 + 1) //leak on right? stack.push(new LineSegment(segment.x2 + 1, x - 1, segment.y, -segment.dy)); } skip = false; //skip only once for(++x; x <= segment.x2 && get(x, segment.y) != oldChar; ++x){;} left = x; } while( x < segment.x2); } return cellsFilled; } public boolean cellContainsDashedLineChar(Cell cell){ char c = get(cell); return StringUtils.isOneOf(c, dashedLines); } public boolean loadFrom(String filename) throws FileNotFoundException, IOException { return loadFrom(filename, null); } public boolean loadFrom(String filename, ProcessingOptions options) throws IOException { String encoding = (options == null) ? null : options.getCharacterEncoding(); ArrayList lines = new ArrayList(); String[] linesArray = FileUtils.readFile(new File(filename), encoding).split("(\r)?\n"); for(int i = 0; i < linesArray.length; i++) lines.add(new StringBuffer(linesArray[i])); return initialiseWithLines(lines, options); } public boolean initialiseWithText(String text, ProcessingOptions options) throws UnsupportedEncodingException { ArrayList lines = new ArrayList(); String[] linesArray = text.split("(\r)?\n"); for(int i = 0; i < linesArray.length; i++) lines.add(new StringBuffer(linesArray[i])); return initialiseWithLines(lines, options); } public boolean initialiseWithLines(ArrayList lines, ProcessingOptions options) throws UnsupportedEncodingException { //remove blank rows at the bottom boolean done = false; int i; for(i = lines.size() - 1; !done; i--){ StringBuffer row = lines.get(i); if(!StringUtils.isBlank(row.toString())) done = true; } rows = new ArrayList(lines.subList(0, i + 2)); if(options != null) fixTabs(options.getTabSize()); else fixTabs(options.DEFAULT_TAB_SIZE); // make all lines of equal length // add blank outline around the buffer to prevent fill glitch // convert tabs to spaces (or remove them if setting is 0) int blankBorderSize = 2; int maxLength = 0; int index = 0; String encoding = null; if(options != null) encoding = options.getCharacterEncoding(); Iterator it = rows.iterator(); while(it.hasNext()){ String row = it.next().toString(); if(encoding != null){ byte[] bytes = row.getBytes(); row = new String(bytes, encoding); } if(row.length() > maxLength) maxLength = row.length(); rows.set(index, new StringBuffer(row)); index++; } it = rows.iterator(); ArrayList newRows = new ArrayList(); //TODO: make the following depend on blankBorderSize StringBuffer topBottomRow = new StringBuffer(StringUtils.repeatString(" ", maxLength + blankBorderSize * 2)); newRows.add(topBottomRow); newRows.add(topBottomRow); while(it.hasNext()){ StringBuffer row = it.next(); if(row.length() < maxLength) { String borderString = StringUtils.repeatString(" ", blankBorderSize); StringBuffer newRow = new StringBuffer(); newRow.append(borderString); newRow.append(row); newRow.append(StringUtils.repeatString(" ", maxLength - row.length())); newRow.append(borderString); newRows.add(newRow); } else { //TODO: why is the following line like that? newRows.add(new StringBuffer(" ").append(row).append(" ")); } } //TODO: make the following depend on blankBorderSize newRows.add(topBottomRow); newRows.add(topBottomRow); rows = newRows; replaceBullets(); replaceHumanColorCodes(); return true; } private void fixTabs(int tabSize){ int rowIndex = 0; Iterator it = rows.iterator(); while(it.hasNext()){ String row = it.next().toString(); StringBuffer newRow = new StringBuffer(); char[] chars = row.toCharArray(); for(int i = 0; i < chars.length; i++){ if(chars[i] == '\t'){ int spacesLeft = tabSize - newRow.length() % tabSize; if(DEBUG){ System.out.println("Found tab. Spaces left: "+spacesLeft); } String spaces = StringUtils.repeatString(" ", spacesLeft); newRow.append(spaces); } else { String character = Character.toString(chars[i]); newRow.append(character); } } rows.set(rowIndex, newRow); rowIndex++; } } /** * @return */ protected ArrayList getRows() { return rows; } public class CellColorPair{ public CellColorPair(Cell cell, Color color){ this.cell = cell; this.color = color; } public Color color; public Cell cell; } public class CellStringPair{ public CellStringPair(Cell cell, String string){ this.cell = cell; this.string = string; } public Cell cell; public String string; } public class CellTagPair{ public CellTagPair(Cell cell, String tag){ this.cell = cell; this.tag = tag; } public Cell cell; public String tag; } public class Cell{ public int x, y; public Cell(Cell cell){ this(cell.x, cell.y); } public Cell(int x, int y){ this.x = x; this.y = y; } public Cell getNorth(){ return new Cell(x, y - 1); } public Cell getSouth(){ return new Cell(x, y + 1); } public Cell getEast(){ return new Cell(x + 1, y); } public Cell getWest(){ return new Cell(x - 1, y); } public Cell getNW(){ return new Cell(x - 1, y - 1); } public Cell getNE(){ return new Cell(x + 1, y - 1); } public Cell getSW(){ return new Cell(x - 1, y + 1); } public Cell getSE(){ return new Cell(x + 1, y + 1); } public CellSet getNeighbours4(){ CellSet result = new CellSet(); result.add(getNorth()); result.add(getSouth()); result.add(getWest()); result.add(getEast()); return result; } public CellSet getNeighbours8(){ CellSet result = new CellSet(); result.add(getNorth()); result.add(getSouth()); result.add(getWest()); result.add(getEast()); result.add(getNW()); result.add(getNE()); result.add(getSW()); result.add(getSE()); return result; } public boolean isNorthOf(Cell cell){ if(this.y < cell.y) return true; return false; } public boolean isSouthOf(Cell cell){ if(this.y > cell.y) return true; return false; } public boolean isWestOf(Cell cell){ if(this.x < cell.x) return true; return false; } public boolean isEastOf(Cell cell){ if(this.x > cell.x) return true; return false; } public boolean equals(Object o){ Cell cell = (Cell) o; if(cell == null) return false; if(x == cell.x && y == cell.y) return true; else return false; } public int hashCode() { return (x << 16) | y; } public boolean isNextTo(int x2, int y2){ if(Math.abs(x2 - x) == 1 && Math.abs(y2 - y) == 1) return false; if(Math.abs(x2 - x) == 1 && y2 == y) return true; if(Math.abs(y2 - y) == 1 && x2 == x) return true; return false; } public boolean isNextTo(Cell cell){ if(cell == null) throw new IllegalArgumentException("cell cannot be null"); return this.isNextTo(cell.x, cell.y); } public String toString(){ return "("+x+", "+y+")"; } public void scale(int s){ x = x * s; y = y * s; } } private class LineSegment{ int x1, x2, y, dy; public LineSegment(int x1, int x2, int y, int dy){ this.x1 = x1; this.x2 = x2; this.y = y; this.dy = dy; } } } ditaa-0.9/src/org/stathissideris/ascii2image/test/0000755000175000017500000000000011277124560020351 5ustar neoneoditaa-0.9/src/org/stathissideris/ascii2image/test/TextGridTest.java0000644000175000017500000002522611277077356023627 0ustar neoneopackage org.stathissideris.ascii2image.test; import static org.junit.Assert.*; import java.io.FileNotFoundException; import java.io.IOException; import org.junit.Before; import org.junit.Test; import org.stathissideris.ascii2image.text.AbstractionGrid; import org.stathissideris.ascii2image.text.CellSet; import org.stathissideris.ascii2image.text.TextGrid; public class TextGridTest { @Before public void setUp() { } @Test public void testFillContinuousAreaSquareOutside() throws FileNotFoundException, IOException { TextGrid squareGrid; squareGrid = new TextGrid(); squareGrid.loadFrom("tests/text/simple_square01.txt"); CellSet filledArea = squareGrid.fillContinuousArea(0, 0, '*'); int size = filledArea.size(); assertEquals(64, size); CellSet expectedFilledArea = new CellSet(); addSquareToCellSet(squareGrid, expectedFilledArea, 0,0, 11,2); addSquareToCellSet(squareGrid, expectedFilledArea, 0,7, 11,2); addSquareToCellSet(squareGrid, expectedFilledArea, 0,2, 2,5); addSquareToCellSet(squareGrid, expectedFilledArea, 9,2, 2,5); assertEquals(expectedFilledArea, filledArea); } @Test public void testFillContinuousAreaSquareInside() throws FileNotFoundException, IOException { TextGrid squareGrid; squareGrid = new TextGrid(); squareGrid.loadFrom("tests/text/simple_square01.txt"); CellSet filledArea = squareGrid.fillContinuousArea(3, 3, '*'); int size = filledArea.size(); assertEquals(15, size); CellSet expectedFilledArea = new CellSet(); addSquareToCellSet(squareGrid, expectedFilledArea, 3,3, 5,3); assertEquals(expectedFilledArea, filledArea); } @Test public void testFillContinuousAreaUInside() throws FileNotFoundException, IOException { TextGrid uGrid; uGrid = new TextGrid(); uGrid.loadFrom("tests/text/simple_U01.txt"); CellSet filledArea = uGrid.fillContinuousArea(3, 3, '*'); int size = filledArea.size(); assertEquals(62, size); CellSet expectedFilledArea = new CellSet(); addSquareToCellSet(uGrid, expectedFilledArea, 3,3, 5,5); addSquareToCellSet(uGrid, expectedFilledArea, 14,3, 5,5); addSquareToCellSet(uGrid, expectedFilledArea, 8,6, 6,2); assertEquals(expectedFilledArea, filledArea); } @Test public void testFillContinuousAreaUOutside() throws FileNotFoundException, IOException { TextGrid uGrid; uGrid = new TextGrid(); uGrid.loadFrom("tests/text/simple_U01.txt"); CellSet filledArea = uGrid.fillContinuousArea(0, 0, '*'); int size = filledArea.size(); assertEquals(128, size); CellSet expectedFilledArea = new CellSet(); addSquareToCellSet(uGrid, expectedFilledArea, 0,0, 2,11); addSquareToCellSet(uGrid, expectedFilledArea, 20,0, 2,11); addSquareToCellSet(uGrid, expectedFilledArea, 0,0, 22, 2); addSquareToCellSet(uGrid, expectedFilledArea, 0,9, 22, 2); addSquareToCellSet(uGrid, expectedFilledArea, 9,2, 4, 3); assertEquals(expectedFilledArea, filledArea); } @Test public void testFillContinuousAreaSOutside() throws FileNotFoundException, IOException { TextGrid uGrid; uGrid = new TextGrid(); uGrid.loadFrom("tests/text/simple_S01.txt"); CellSet filledArea = uGrid.fillContinuousArea(0, 0, '*'); int size = filledArea.size(); assertEquals(246, size); CellSet expectedFilledArea = new CellSet(); addSquareToCellSet(uGrid, expectedFilledArea, 0, 0, 25, 2); addSquareToCellSet(uGrid, expectedFilledArea, 0,12, 25, 2); addSquareToCellSet(uGrid, expectedFilledArea, 0, 0, 2,14); addSquareToCellSet(uGrid, expectedFilledArea, 23, 0, 2,14); addSquareToCellSet(uGrid, expectedFilledArea, 9, 0, 7, 7); addSquareToCellSet(uGrid, expectedFilledArea, 0, 7, 9, 7); addSquareToCellSet(uGrid, expectedFilledArea, 16, 7, 9, 7); expectedFilledArea.add(uGrid.new Cell(22, 6)); assertEquals(expectedFilledArea, filledArea); } @Test public void testFillContinuousAreaSInside1() throws FileNotFoundException, IOException { TextGrid uGrid; uGrid = new TextGrid(); uGrid.loadFrom("tests/text/simple_S01.txt"); CellSet filledArea = uGrid.fillContinuousArea(3, 3, '*'); int size = filledArea.size(); assertEquals(15, size); CellSet expectedFilledArea = new CellSet(); addSquareToCellSet(uGrid, expectedFilledArea, 3, 3, 5, 3); assertEquals(expectedFilledArea, filledArea); } @Test public void testFillContinuousAreaSInside2() throws FileNotFoundException, IOException { TextGrid uGrid; uGrid = new TextGrid(); uGrid.loadFrom("tests/text/simple_S01.txt"); CellSet filledArea = uGrid.fillContinuousArea(17, 3, '*'); int size = filledArea.size(); assertEquals(15, size); CellSet expectedFilledArea = new CellSet(); addSquareToCellSet(uGrid, expectedFilledArea, 17, 3, 5, 3); assertEquals(expectedFilledArea, filledArea); } @Test public void testFindBoundariesExpandingFromSquare() throws FileNotFoundException, IOException { TextGrid grid; grid = new TextGrid(); grid.loadFrom("tests/text/simple_square01.txt"); CellSet wholeGridSet = new CellSet(); addSquareToCellSet(grid, wholeGridSet, 0,0, grid.getWidth(),grid.getHeight()); TextGrid copyGrid = new AbstractionGrid(grid, wholeGridSet).getCopyOfInternalBuffer(); CellSet boundaries = copyGrid.findBoundariesExpandingFrom(copyGrid.new Cell(8, 8)); int size = boundaries.size(); assertEquals(56, size); CellSet expectedBoundaries = new CellSet(); addSquareToCellSet(copyGrid, expectedBoundaries, 8, 7, 17,1); addSquareToCellSet(copyGrid, expectedBoundaries, 8,19, 17,1); addSquareToCellSet(copyGrid, expectedBoundaries, 7, 8, 1,11); addSquareToCellSet(copyGrid, expectedBoundaries,25, 8, 1,11); assertEquals(expectedBoundaries, boundaries); } @Test public void testFindBoundariesExpandingFromUInside() throws FileNotFoundException, IOException { TextGrid grid; grid = new TextGrid(); grid.loadFrom("tests/text/simple_U01.txt"); CellSet wholeGridSet = new CellSet(); addSquareToCellSet(grid, wholeGridSet, 0,0, grid.getWidth(),grid.getHeight()); TextGrid copyGrid = new AbstractionGrid(grid, wholeGridSet).getCopyOfInternalBuffer(); CellSet boundaries = copyGrid.findBoundariesExpandingFrom(copyGrid.new Cell(8, 8)); int size1 = boundaries.size(); assertEquals(150, size1); String expectedBoundariesString = "(47,25)/(43,7)/(25,25)/(58,18)/(18,7)/(52,7)/(58,12)/(13,25)/(7,8)/" +"(7,11)/(57,7)/(7,10)/(24,25)/(18,25)/(12,7)/(37,25)/(58,19)/(35,16)/(58,11)/(25,12)/" +"(54,25)/(29,16)/(16,25)/(49,7)/(7,12)/(26,25)/(19,7)/(58,17)/(55,25)/(46,25)/(17,25)/" +"(58,13)/(32,16)/(25,11)/(51,25)/(21,25)/(58,14)/(36,16)/(7,16)/(32,25)/(55,7)/(25,8)/" +"(10,25)/(58,21)/(20,7)/(27,25)/(31,16)/(58,9)/(45,25)/(58,20)/(56,25)/(10,7)/(39,25)/" +"(44,7)/(58,10)/(33,16)/(46,7)/(7,9)/(58,22)/(17,7)/(48,25)/(7,15)/(38,16)/(54,7)/(11,25)/" +"(9,7)/(7,14)/(58,24)/(40,25)/(30,16)/(58,23)/(47,7)/(7,13)/(19,25)/(8,7)/(25,16)/(53,25)/" +"(39,16)/(23,7)/(42,25)/(53,7)/(40,15)/(7,23)/(12,25)/(48,7)/(30,25)/(42,7)/(7,24)/(40,14)/" +"(14,7)/(35,25)/(52,25)/(58,16)/(25,15)/(9,25)/(40,16)/(7,22)/(43,25)/(25,9)/(29,25)/" +"(56,7)/(28,16)/(22,7)/(8,25)/(25,10)/(15,7)/(41,7)/(34,25)/(11,7)/(45,7)/(7,21)/(7,18)/" +"(38,25)/(50,7)/(58,15)/(15,25)/(40,12)/(27,16)/(21,7)/(57,25)/(44,25)/(25,13)/(37,16)/" +"(16,7)/(7,17)/(25,14)/(50,25)/(20,25)/(33,25)/(40,13)/(22,25)/(26,16)/(24,7)/(31,25)/" +"(40,8)/(7,19)/(58,8)/(41,25)/(28,25)/(40,11)/(14,25)/(34,16)/(51,7)/(7,20)/(40,10)/" +"(23,25)/(13,7)/(49,25)/(40,9)/(36,25)"; CellSet expectedBoundaries = cellSetFromCellsString(expectedBoundariesString, grid); assertEquals(expectedBoundaries, boundaries); } @Test public void testFindBoundariesExpandingFromUOutside() throws FileNotFoundException, IOException { TextGrid grid; grid = new TextGrid(); grid.loadFrom("tests/text/simple_U01.txt"); CellSet wholeGridSet = new CellSet(); addSquareToCellSet(grid, wholeGridSet, 0,0, grid.getWidth(),grid.getHeight()); TextGrid copyGrid = new AbstractionGrid(grid, wholeGridSet).getCopyOfInternalBuffer(); CellSet boundaries = copyGrid.findBoundariesExpandingFrom(copyGrid.new Cell(0, 0)); int size = boundaries.size(); assertEquals(154, size); System.out.println(boundaries.getCellsAsString()); String expectedBoundariesString = "(47,25)/(43,7)/(25,25)/(58,18)/(18,7)/(52,7)/(13,25)/(58,12)/(7,8)/(7,11)/" +"(7,10)/(57,7)/(24,25)/(18,25)/(12,7)/(37,25)/(58,19)/(35,16)/(58,11)/" +"(25,12)/(54,25)/(29,16)/(16,25)/(7,7)/(7,12)/(49,7)/(26,25)/(19,7)/(58,17)/" +"(55,25)/(46,25)/(17,25)/(58,13)/(32,16)/(25,11)/(51,25)/(21,25)/(36,16)/" +"(58,14)/(7,16)/(32,25)/(25,8)/(10,25)/(55,7)/(58,21)/(20,7)/(27,25)/(31,16)/" +"(58,9)/(45,25)/(58,20)/(25,7)/(56,25)/(10,7)/(39,25)/(44,7)/(33,16)/(58,10)/" +"(7,9)/(46,7)/(58,22)/(17,7)/(48,25)/(7,15)/(38,16)/(54,7)/(11,25)/(9,7)/(7,14)/" +"(58,24)/(40,25)/(30,16)/(58,23)/(7,13)/(47,7)/(19,25)/(8,7)/(53,25)/(39,16)/(23,7)/" +"(42,25)/(40,15)/(7,23)/(12,25)/(53,7)/(48,7)/(30,25)/(7,24)/(7,25)/(42,7)/(40,14)/" +"(14,7)/(52,25)/(35,25)/(58,16)/(25,15)/(9,25)/(7,22)/(43,25)/(25,9)/(29,25)/(28,16)/" +"(56,7)/(22,7)/(25,10)/(8,25)/(15,7)/(41,7)/(34,25)/(11,7)/(7,21)/(45,7)/(7,18)/" +"(40,7)/(38,25)/(50,7)/(15,25)/(58,15)/(40,12)/(27,16)/(21,7)/(57,25)/(44,25)/" +"(25,13)/(37,16)/(16,7)/(25,14)/(7,17)/(50,25)/(33,25)/(20,25)/(40,13)/(22,25)/" +"(26,16)/(24,7)/(31,25)/(40,8)/(7,19)/(58,25)/(58,8)/(41,25)/(28,25)/(40,11)/" +"(14,25)/(34,16)/(58,7)/(7,20)/(51,7)/(40,10)/(23,25)/(13,7)/(49,25)/(40,9)/(36,25)"; CellSet expectedBoundaries = cellSetFromCellsString(expectedBoundariesString, grid); assertEquals(expectedBoundaries, boundaries); } @Test public void testCellSetFromCellsString(){ TextGrid grid; grid = new TextGrid(); String str = "(9,7)/(0, 2)/(3 ,2)/(5,3)"; CellSet cellSet = cellSetFromCellsString(str, grid); CellSet expectedCellSet = new CellSet(); expectedCellSet.add(grid.new Cell(0, 2)); expectedCellSet.add(grid.new Cell(3, 2)); expectedCellSet.add(grid.new Cell(5, 3)); expectedCellSet.add(grid.new Cell(9, 7)); assertEquals(expectedCellSet, cellSet); } private void addSquareToCellSet(TextGrid grid, CellSet cellSet, int x, int y, int width, int height) { for(int xx = 0; xx < width; xx++){ for(int yy = 0; yy < height; yy++){ cellSet.add(grid.new Cell(x + xx, y + yy)); } } } private CellSet cellSetFromCellsString(String str, TextGrid grid){ String[] cellStrings = str.split("/"); CellSet set = new CellSet(); for(String cellString : cellStrings) { int x = Integer.parseInt(cellString.substring(1, cellString.indexOf(",")).trim()); int y = Integer.parseInt(cellString.substring(cellString.indexOf(",") + 1, cellString.length() - 1).trim()); set.add(grid.new Cell(x, y)); } return set; } } ditaa-0.9/src/org/stathissideris/ascii2image/test/CellSetTest.java0000644000175000017500000000126011277077356023420 0ustar neoneopackage org.stathissideris.ascii2image.test; import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; import org.stathissideris.ascii2image.text.CellSet; import org.stathissideris.ascii2image.text.TextGrid; public class CellSetTest { TextGrid g = new TextGrid(); CellSet set = new CellSet(); @Before public void setUp() { set.add(g.new Cell(10, 20)); set.add(g.new Cell(10, 60)); set.add(g.new Cell(10, 30)); set.add(g.new Cell(60, 20)); } @Test public void testContains() { TextGrid.Cell cell1 = g.new Cell(10, 20); TextGrid.Cell cell2 = g.new Cell(10, 20); assertTrue(cell1.equals(cell2)); assertTrue(set.contains(cell1)); } } ditaa-0.9/src/org/stathissideris/ascii2image/test/GridPatternTest.java0000644000175000017500000000134111277125404024274 0ustar neoneopackage org.stathissideris.ascii2image.test; import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; import org.stathissideris.ascii2image.text.CellSet; import org.stathissideris.ascii2image.text.GridPattern; import org.stathissideris.ascii2image.text.TextGrid; public class GridPatternTest { TextGrid g = new TextGrid(6,4); GridPattern pattern = new GridPattern(); @Before public void setUp() { g.setRow(0, "+----+"); g.setRow(1, "| |"); g.setRow(2, "| |"); g.setRow(3, "+----+"); } @Test public void testContains() { pattern.isMatchedBy(g); pattern.isMatchedBy(g); pattern.isMatchedBy(g); pattern.isMatchedBy(g); pattern.isMatchedBy(g); } } ditaa-0.9/src/org/stathissideris/ascii2image/core/0000755000175000017500000000000011277077356020334 5ustar neoneoditaa-0.9/src/org/stathissideris/ascii2image/core/DocBookConverter.java0000644000175000017500000000406611277077356024415 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.core; import java.io.*; import org.xml.sax.*; import org.xml.sax.helpers.*; // using SAX public class DocBookConverter { class HowToHandler extends DefaultHandler { boolean title = false; boolean url = false; public void startElement( String nsURI, String strippedName, String tagName, Attributes attributes) throws SAXException { if (tagName.equalsIgnoreCase("title")) title = true; if (tagName.equalsIgnoreCase("url")) url = true; } public void characters(char[] ch, int start, int length) { if (title) { System.out.println("Title: " + new String(ch, start, length)); title = false; } else if (url) { System.out.println("Url: " + new String(ch, start,length)); url = false; } } } public void list( ) throws Exception { XMLReader parser = XMLReaderFactory.createXMLReader ("org.apache.crimson.parser.XMLReaderImpl"); parser.setContentHandler(new HowToHandler( )); parser.parse("howto.xml"); } public static void main(String[] args) throws Exception { new DocBookConverter().list( ); } } ditaa-0.9/src/org/stathissideris/ascii2image/core/JavadocTaglet.java0000644000175000017500000002330711277077356023714 0ustar neoneo/* * Text Diagram Taglet * * Copyright (C) 2006 Nordic Growth Market NGM AB, * Mikael Brannstrom. * * This 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 St, Fifth Floor, Boston, MA 02110-1301 USA * */ package org.stathissideris.ascii2image.core; import com.sun.javadoc.ProgramElementDoc; import com.sun.javadoc.Tag; import com.sun.tools.doclets.Taglet; import com.sun.tools.doclets.internal.toolkit.Configuration; import com.sun.tools.doclets.standard.Standard; import java.awt.image.RenderedImage; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Map; import java.util.regex.Pattern; import javax.imageio.ImageIO; import org.stathissideris.ascii2image.text.TextGrid; import org.stathissideris.ascii2image.graphics.Diagram; import org.stathissideris.ascii2image.core.ConversionOptions; import org.stathissideris.ascii2image.graphics.BitmapRenderer; /** This class is a custom Javadoc taglet for embedding ditaa diagrams in * javadoc comments. The tag is an inline which can be used in any javadoc * comment. The tag can also be used in package documentation and in the * overview. * This taglet assumes that the Standard Javadoc Doclet is being used. *

* The syntax is:
* * {@textdiagram diagram_name
* the ascii art diagram
* } *
*

* The diagram name will be used when generating the image, so that the image * can be referenced to somewhere else (by using an a-href HTML tag). The * diagram name can only contain letters, numbers and underscore. The name of * the generated image will become "<classname>-<diagram name>.png". *

* The syntax for the ditaa diagram is described at * http://ditaa.sourceforge.net/. *

* Note: The overview file needs to be named "overview.html" if it lies * in the source path, otherwise it is sufficient that it ends with ".html". * * @author Mikael Brannstrom */ public class JavadocTaglet implements Taglet { private static final String NAME = "textdiagram"; private static final Pattern FIGURE_NAME_PATTERN = Pattern.compile("\\w+"); private final File[] srcPath; private final File dstDir; private final boolean simpleMode; /** Creates a new instance of TextDiagramTaglet */ public JavadocTaglet() { String configSourcepath = null; String configDestDirName = null; // Try to get configuration try { // Do this: Configuration config = Standard.htmlDoclet.configuration(); Field htmlDocletField = Standard.class.getField("htmlDoclet"); Object htmlDoclet = htmlDocletField.get(null); // static field Method configurationMethod = htmlDoclet.getClass().getMethod("configuration", null); Object config = configurationMethod.invoke(htmlDoclet, null); // Do this: configSourcepath = config.sourcepath; Field sourcepathField = config.getClass().getField("sourcepath"); configSourcepath = (String)sourcepathField.get(config); // Do this: configDestDirName = config.destDirName; Field destDirNameField = config.getClass().getField("destDirName"); configDestDirName = (String)destDirNameField.get(config); } catch(Exception e) { warning("Could not setup taglet. Falling back to simple mode.\n"+e); } if(configDestDirName == null) { srcPath = null; dstDir = null; simpleMode = true; } else { // setup srcPath String[] srcPathStr = configSourcepath.split("[;:]"); srcPath = new File[srcPathStr.length]; for(int i=0; i"; } int i; if(i1 == -1 || i1 == -1) i = Math.max(i1, i2); else i = Math.min(i1, i2); figureName = text.substring(0, i); figureText = text.substring(i+1); if(!FIGURE_NAME_PATTERN.matcher(figureName).matches()) { error("Illegal "+getName()+" name: \""+figureName+"\""); } // Convert [d] to {d} where d can be 1 character and more figureText = figureText.replaceAll("\\[(\\w+)\\]", "{$1}"); if(simpleMode) { StringBuffer strBuf = new StringBuffer(); strBuf.append("

");
			strBuf.append(figureText);
			strBuf.append("
"); return strBuf.toString(); } else { File outputFile = getOutputFile(tag.position().file(), figureName); generateImage(figureText, outputFile); StringBuffer strBuf = new StringBuffer(); strBuf.append("\"");"); return strBuf.toString(); } } /** Returns null since this is an inline tag. */ public String toString(Tag[] tag) { return null; // should return null, this is an inline tag } /** Returns the path of the output file given * the source file and the diagram name. * @param srcFile the source file which contains the tag * @param name the diagram name that will be used for generating the output * filename. */ private File getOutputFile(File srcFile, String name) { String relPath = getRelativePath(srcFile); // Special hack for the overview file if(srcFile.getName().toLowerCase().equals("overview.html") || (relPath == null && srcFile.getName().toLowerCase().endsWith(".html"))) { relPath = "overview"; } if(relPath == null) { error("The file is not relative to the source path: "+srcFile); } // get the filename and dirname String dirname=null, filename=null; int i = relPath.lastIndexOf(File.separatorChar); if(i == -1) { filename = relPath; } else if(i == 0) { filename = relPath.substring(1); } else { filename = relPath.substring(i+1); dirname = relPath.substring(0, i); } // skip file ending in filename i = filename.lastIndexOf('.'); if(i != -1) filename = filename.substring(0, i); String path; if(dirname == null) { path = filename; } else { path = dirname+File.separator+filename; } return new File(dstDir, path+"-"+name+".png"); } /** Returns the relative path of a (source) file. * The path is relative to one of the source dirs specified to the * standard doclet. * @returns the relative path. If a relative path could not be found * null is returned. */ private String getRelativePath(File file) { file = file.getAbsoluteFile(); try { file = file.getCanonicalFile(); } catch (IOException ex) { warning("Could not get canonical path of file: "+file); } String filePath = file.getAbsolutePath(); for(int i=0; i y2) return -1; if(y1 < y2) return 1; return 0; } } ditaa-0.9/src/org/stathissideris/ascii2image/core/RenderingOptions.java0000644000175000017500000000434611277077356024477 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.core; import java.util.HashMap; import org.stathissideris.ascii2image.graphics.CustomShapeDefinition; /** * * @author Efstathios Sideris */ public class RenderingOptions { private HashMap customShapes; private boolean dropShadows = true; private boolean renderDebugLines = false; private boolean antialias = true; private int cellWidth = 10; private int cellHeight = 14; private float scale = 1; /** * @return */ public int getCellHeight() { return cellHeight; } /** * @return */ public int getCellWidth() { return cellWidth; } /** * @return */ public boolean dropShadows() { return dropShadows; } /** * @return */ public boolean renderDebugLines() { return renderDebugLines; } /** * @return */ public float getScale() { return scale; } /** * @param b */ public void setDropShadows(boolean b) { dropShadows = b; } /** * @param b */ public void setRenderDebugLines(boolean b) { renderDebugLines = b; } /** * @param f */ public void setScale(float f) { scale = f; cellWidth *= scale; cellHeight *= scale; } /** * @return */ public boolean performAntialias() { return antialias; } /** * @param b */ public void setAntialias(boolean b) { antialias = b; } } ditaa-0.9/src/org/stathissideris/ascii2image/core/FileUtils.java0000644000175000017500000001074011277077356023101 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.core; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; /** * * @author Efstathios Sideris */ public class FileUtils { //private static final public static String makeTargetPathname(String sourcePathname, String extension, boolean overwrite){ return makeTargetPathname(sourcePathname, extension, "", overwrite); } public static String makeTargetPathname(String sourcePathname, String extension, String postfix, boolean overwrite){ File sourceFile = new File(sourcePathname); String path = ""; if(sourceFile.getParentFile() != null){ path = sourceFile.getParentFile().getAbsolutePath(); if(!path.endsWith(File.separator)) path += File.separator; } String baseName = getBaseName(sourceFile.getName()); String targetName = path + baseName + postfix + "." + extension; if(new File(targetName).exists() && !overwrite) targetName = makeAlternativePathname(targetName); return targetName; } public static String makeAlternativePathname(String pathName){ int limit = 100; for(int i = 2; i <= limit; i++){ String alternative = getBaseName(pathName)+"_"+i; String extension = getExtension(pathName); if(extension != null) alternative += "."+extension; if(!(new File(alternative).exists())) return alternative; } return null; } public static String getExtension(String pathName){ if(pathName.lastIndexOf('.') == -1) return null; return pathName.substring(pathName.lastIndexOf('.') + 1); } public static String getBaseName(String pathName){ if(pathName.lastIndexOf('.') == -1) return pathName; return pathName.substring(0, pathName.lastIndexOf('.')); } public static String readFile(File file) throws IOException { return readFile(file, null); } public static String readFile(File file, String encoding) throws IOException { InputStream is = new FileInputStream(file); long length = file.length(); if (length > Integer.MAX_VALUE) { // File is too large // TODO: we need some feedback for the case of the file being too large } // Create the byte array to hold the data byte[] bytes = new byte[(int)length]; // Read in the bytes int offset = 0; int numRead = 0; while (offset < bytes.length && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) { offset += numRead; } // Ensure all the bytes have been read in if (offset < bytes.length) { throw new IOException("Could not completely read file "+file.getName()); } // Close the input stream and return bytes is.close(); if(encoding == null){ return new String(bytes); } else { return new String(bytes, encoding); } } public static void main(String[] args){ System.out.println(makeTargetPathname("C:\\Files\\papar.txt", "jpg", false)); System.out.println(makeTargetPathname("C:\\Files\\papar", "jpg", false)); System.out.println(makeTargetPathname("papar.txt", "jpg", false)); System.out.println(makeTargetPathname("/home/sideris/tsourekia/papar.txt", "jpg", false)); System.out.println(makeTargetPathname("D:\\diagram.max", "jpg", false)); System.out.println(makeAlternativePathname("C:\\Files\\papar.txt")); System.out.println(makeAlternativePathname("C:\\Files\\papar")); System.out.println(getExtension("pipi.jpeg")); System.out.println(getExtension("pipi")); } } ditaa-0.9/src/org/stathissideris/ascii2image/core/Pair.java0000644000175000017500000000030111277077356022064 0ustar neoneopackage org.stathissideris.ascii2image.core; public class Pair { public T first; public K second; public Pair(T first, K second) { this.first = first; this.second = second; } } ditaa-0.9/src/org/stathissideris/ascii2image/core/CommandLineConverter.java0000644000175000017500000002136211303054634025241 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.core; import java.awt.image.RenderedImage; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.UnsupportedEncodingException; import javax.imageio.ImageIO; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; import org.apache.commons.cli.PosixParser; import org.stathissideris.ascii2image.graphics.BitmapRenderer; import org.stathissideris.ascii2image.graphics.Diagram; import org.stathissideris.ascii2image.text.TextGrid; /** * * @author Efstathios Sideris */ public class CommandLineConverter { private static String notice = "ditaa version 0.9, Copyright (C) 2004--2009 Efstathios (Stathis) Sideris"; private static String[] markupModeAllowedValues = {"use", "ignore", "render"}; public static void main(String[] args){ long startTime = System.currentTimeMillis(); System.out.println("\n"+notice+"\n"); Options cmdLnOptions = new Options(); cmdLnOptions.addOption( OptionBuilder.withLongOpt("help") .withDescription( "Prints usage help." ) .create() ); cmdLnOptions.addOption("v", "verbose", false, "Makes ditaa more verbose."); cmdLnOptions.addOption("o", "overwrite", false, "If the filename of the destination image already exists, an alternative name is chosen. If the overwrite option is selected, the image file is instead overwriten."); cmdLnOptions.addOption("S", "no-shadows", false, "Turns off the drop-shadow effect."); cmdLnOptions.addOption("A", "no-antialias", false, "Turns anti-aliasing off."); cmdLnOptions.addOption("d", "debug", false, "Renders the debug grid over the resulting image."); cmdLnOptions.addOption("r", "round-corners", false, "Causes all corners to be rendered as round corners."); cmdLnOptions.addOption("E", "no-separation", false, "Prevents the separation of common edges of shapes."); cmdLnOptions.addOption("h", "html", false, "In this case the input is an HTML file. The contents of the
 tags are rendered as diagrams and saved in the images directory and a new HTML file is produced with the appropriate  tags.");
		
		cmdLnOptions.addOption(
				OptionBuilder.withLongOpt("encoding")
				.withDescription("The encoding of the input file.")
				.hasArg()
				.withArgName("ENCODING")
				.create('e')
				);

		cmdLnOptions.addOption(
				OptionBuilder.withLongOpt("scale")
				.withDescription("A natural number that determines the size of the rendered image. The units are fractions of the default size (2.5 renders 1.5 times bigger than the default).")
				.hasArg()
				.withArgName("SCALE")
				.create('s')
				);

		cmdLnOptions.addOption(
				OptionBuilder.withLongOpt("tabs")
				.withDescription("Tabs are normally interpreted as 8 spaces but it is possible to change that using this option. It is not advisable to use tabs in your diagrams.")
				.hasArg()
				.withArgName("TABS")
				.create('t')
				);

//TODO: uncomment this for next version:
//		cmdLnOptions.addOption(
//				OptionBuilder.withLongOpt("config")
//				.withDescription( "The shape configuration file." )
//				.hasArg()
//				.withArgName("CONFIG_FILE")
//				.create('c') );
		
		CommandLine cmdLine = null;
		
		
		
		///// parse command line options
		try {
			// parse the command line arguments
			CommandLineParser parser = new PosixParser();
			
			cmdLine = parser.parse(cmdLnOptions, args);
						
			// validate that block-size has been set
			if( cmdLine.hasOption( "block-size" ) ) {
				// print the value of block-size
				System.out.println( cmdLine.getOptionValue( "block-size" ) );
			}
			
		} catch (org.apache.commons.cli.ParseException e) {
			System.err.println(e.getMessage());
			new HelpFormatter().printHelp("java -jar ditaa.jar  [outfile]", cmdLnOptions, true);
			System.exit(2);
		}
		
		
		if(cmdLine.hasOption("help") || args.length == 0 ){
			new HelpFormatter().printHelp("java -jar ditaa.jar  [outfile]", cmdLnOptions, true);
			System.exit(0);			
		}
						
		ConversionOptions options = null;
		try {
			options = new ConversionOptions(cmdLine);
		} catch (UnsupportedEncodingException e2) {
			System.err.println("Error: " + e2.getMessage());
			System.exit(2);
		}  
		
		args = cmdLine.getArgs();
		
		if(args.length == 0) {
			System.err.println("Error: Please provide the input file filename");
			new HelpFormatter().printHelp("java -jar ditaa.jar  [outfile]", cmdLnOptions, true);
			System.exit(2);
		} 
		
		/////// print options before running
		System.out.println("Running with options:");
		Option[] opts = cmdLine.getOptions();
		for (Option option : opts) {
			if(option.hasArgs()){
				for(String value:option.getValues()){
					System.out.println(option.getLongOpt()+" = "+value);
				}
			} else if(option.hasArg()){
				System.out.println(option.getLongOpt()+" = "+option.getValue());
			} else {
				System.out.println(option.getLongOpt());
			}
		}
		
		if(cmdLine.hasOption("html")){
			String filename = args[0];
			
			boolean overwrite = false;
			if(options.processingOptions.overwriteFiles()) overwrite = true;
			
			String toFilename;
			if(args.length == 1){
				toFilename = FileUtils.makeTargetPathname(filename, "html", "_processed", true);
			} else {
				toFilename = args[1];
			}
			File target = new File(toFilename);
			if(!overwrite && target.exists()) {
				System.out.println("Error: File "+toFilename+" exists. If you would like to overwrite it, please use the --overwrite option.");
				System.exit(0);
			}
			
			new HTMLConverter().convertHTMLFile(filename, toFilename, "ditaa_diagram", "images", options);
			System.exit(0);
			
		} else { //simple mode
			
			TextGrid grid = new TextGrid();
			if(options.processingOptions.getCustomShapes() != null){
				grid.addToMarkupTags(options.processingOptions.getCustomShapes().keySet());
			}
			String filename = args[0];
			System.out.println("Reading file: "+filename);
			try {
				if(!grid.loadFrom(filename, options.processingOptions)){
					System.err.println("Cannot open file "+filename+" for reading");
				}
			} catch (UnsupportedEncodingException e1){
				System.err.println("Error: "+e1.getMessage());
				System.exit(1);
			} catch (FileNotFoundException e1) {
				System.err.println("Error: File "+filename+" does not exist");
				System.exit(1);
			} catch (IOException e1) {
				System.err.println("Error: Cannot open file "+filename+" for reading");
				System.exit(1);
			}
			
			if(options.processingOptions.printDebugOutput()){
				System.out.println("Using grid:");
				grid.printDebug();
			}
			
			boolean overwrite = false;
			if(options.processingOptions.overwriteFiles()) overwrite = true;
			String toFilename;
			if(args.length == 1){
				toFilename = FileUtils.makeTargetPathname(filename, "png", overwrite);
			} else {
				toFilename = args[1];
			}
			
			Diagram diagram = new Diagram(grid, options);
			System.out.println("Rendering to file: "+toFilename);
			
			
			RenderedImage image = new BitmapRenderer().renderToImage(diagram, options.renderingOptions);
			
			try {
				File file = new File(toFilename);
				ImageIO.write(image, "png", file);
			} catch (IOException e) {
				//e.printStackTrace();
				System.err.println("Error: Cannot write to file "+filename);
				System.exit(1);
			}
			
			//BitmapRenderer.renderToPNG(diagram, toFilename, options.renderingOptions);
			
			long endTime = System.currentTimeMillis();
			long totalTime  = (endTime - startTime) / 1000;
			System.out.println("Done in "+totalTime+"sec");
			
//			try {
//			Thread.sleep(Long.MAX_VALUE);
//			} catch (InterruptedException e) {
//			e.printStackTrace();
//			}
			
		}
	}	
}
ditaa-0.9/src/org/stathissideris/ascii2image/core/ConversionOptions.java0000644000175000017500000001362211277077356024704 0ustar  neoneo/*
 * DiTAA - Diagrams Through Ascii Art
 * 
 * Copyright (C) 2004 Efstathios Sideris
 *
 * This 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *   
 */
package org.stathissideris.ascii2image.core;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;

import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.stathissideris.ascii2image.graphics.CustomShapeDefinition;
import org.xml.sax.SAXException;

/**
 * 
 * @author Efstathios Sideris
 */
public class ConversionOptions {
	
	public ProcessingOptions processingOptions =
		new ProcessingOptions();
	public RenderingOptions renderingOptions =
		new RenderingOptions();
		
	public void setDebug(boolean value){
		processingOptions.setPrintDebugOutput(value);
		renderingOptions.setRenderDebugLines(value);
	}
	
	public ConversionOptions(){}
	
	public ConversionOptions(CommandLine cmdLine) throws UnsupportedEncodingException{
		
		processingOptions.setVerbose(cmdLine.hasOption("verbose"));
		renderingOptions.setDropShadows(!cmdLine.hasOption("no-shadows"));
		this.setDebug(cmdLine.hasOption("debug"));
		processingOptions.setOverwriteFiles(cmdLine.hasOption("overwrite"));
		
		if(cmdLine.hasOption("scale")){
			Float scale = Float.parseFloat(cmdLine.getOptionValue("scale"));
			renderingOptions.setScale(scale.floatValue());
		}
		
		processingOptions.setAllCornersAreRound(cmdLine.hasOption("round-corners"));
		processingOptions.setPerformSeparationOfCommonEdges(!cmdLine.hasOption("no-separation"));
		renderingOptions.setAntialias(!cmdLine.hasOption("no-antialias"));



		if(cmdLine.hasOption("tabs")){
			Integer tabSize = Integer.parseInt(cmdLine.getOptionValue("tabs"));
			int tabSizeValue = tabSize.intValue();
			if(tabSizeValue < 0) tabSizeValue = 0;
			processingOptions.setTabSize(tabSizeValue);
		}

		String encoding = (String) cmdLine.getOptionValue("encoding");
		if(encoding != null){
			new String(new byte[2], encoding);
			processingOptions.setCharacterEncoding(encoding);
		}
		
		ConfigurationParser configParser = new ConfigurationParser();
		try {
			for (Option curOption : cmdLine.getOptions()) {
				if(curOption.getLongOpt().equals("config")) {
					String configFilename = curOption.getValue();
					System.out.println("Parsing configuration file "+configFilename);
					File file = new File(configFilename);
					if(file.exists()){
						configParser.parseFile(file);
						HashMap shapes = configParser.getShapeDefinitionsHash();
						processingOptions.putAllInCustomShapes(shapes);
					} else {
						System.err.println("File "+file+" does not exist, skipping");
					}
				}
			}
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}


// may be supported at a later date:
//String exportFormat = (String) cmdLine.getOptionValue("format");
//if(exportFormat != null){
//	exportFormat = exportFormat.toLowerCase();
//	if(exportFormat == "jpeg" || exportFormat == "jpg"){
//		processingOptions.setExportFormat(ProcessingOptions.FORMAT_JPEG);
//	} else if(exportFormat == "png"){
//		processingOptions.setExportFormat(ProcessingOptions.FORMAT_PNG);
//	} else if(exportFormat == "gif"){
//		processingOptions.setExportFormat(ProcessingOptions.FORMAT_GIF);
//	}
//}
//
//String colorCodeMode = (String) cmdLine.getOptionValue("color-codes");
//if(colorCodeMode != null){
//	if(colorCodeMode.equals("use"))
//		processingOptions.setColorCodesProcessingMode(ProcessingOptions.USE_COLOR_CODES);
//	else if(colorCodeMode.equals("ignore"))
//		processingOptions.setColorCodesProcessingMode(ProcessingOptions.IGNORE_COLOR_CODES);
//	else if(colorCodeMode.equals("render"))
//		processingOptions.setColorCodesProcessingMode(ProcessingOptions.RENDER_COLOR_CODES);
//}
//
//String tagsMode = (String) cmdLine.getOptionValue("tags");
//if(tagsMode != null){
//	if(tagsMode.equals("use"))
//		processingOptions.setTagProcessingMode(ProcessingOptions.USE_TAGS);
//	else if(tagsMode.equals("ignore"))
//		processingOptions.setTagProcessingMode(ProcessingOptions.IGNORE_TAGS);
//	else if(tagsMode.equals("render"))
//		processingOptions.setTagProcessingMode(ProcessingOptions.RENDER_TAGS);
//}
//
//
//String markupMode = (String) cmdLine.getOptionValue("markup");
//if(markupMode != null){
//	if(markupMode.equals("use")){
//		processingOptions.setColorCodesProcessingMode(ProcessingOptions.USE_COLOR_CODES);
//		processingOptions.setTagProcessingMode(ProcessingOptions.USE_TAGS);
//	} else if(markupMode.equals("ignore")){
//		processingOptions.setColorCodesProcessingMode(ProcessingOptions.IGNORE_COLOR_CODES);
//		processingOptions.setTagProcessingMode(ProcessingOptions.IGNORE_TAGS);
//	} else if(markupMode.equals("render")){
//		processingOptions.setColorCodesProcessingMode(ProcessingOptions.RENDER_COLOR_CODES);
//		processingOptions.setTagProcessingMode(ProcessingOptions.RENDER_TAGS);
//	}
//}ditaa-0.9/src/org/stathissideris/ascii2image/core/VisualTester.java0000644000175000017500000001311411303045410023601 0ustar  neoneo/*
 * DiTAA - Diagrams Through Ascii Art
 * 
 * Copyright (C) 2004 Efstathios Sideris
 *
 * This 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *   
 */
package org.stathissideris.ascii2image.core;

import java.awt.image.RenderedImage;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.imageio.ImageIO;

import org.stathissideris.ascii2image.graphics.BitmapRenderer;
import org.stathissideris.ascii2image.graphics.Diagram;
import org.stathissideris.ascii2image.text.StringUtils;
import org.stathissideris.ascii2image.text.TextGrid;

/**
 * 
 * @author Efstathios Sideris
 */
public class VisualTester {

	private static final String HTMLReportName = "test_suite";

	public static void main(String[] args){
		VisualTester tester = new VisualTester();
		
		String textDir = "tests/text";
		String reportDir = "tests/images";
		
		File textDirObj = new File(textDir);
		ArrayList textFiles
			= new ArrayList(Arrays.asList(textDirObj.listFiles()));
	
		Set excludedFiles = new HashSet();
		excludedFiles.addAll( Arrays.asList(new String[]{
			"dak_orgstruktur_vs_be.ditaa.OutOfMemoryError.txt",
			"dak_orgstruktur_vs_be.ditaa.OutOfMemoryError.2.txt",
			"dak_orgstruktur_vs_be.ditaa.OutOfMemoryError.3.txt",
			"dak_orgstruktur_vs_be.ditaa.OutOfMemoryError.4.txt",
			"dak_orgstruktur_vs_be.ditaa.OutOfMemoryError.edit.txt",
			"dak_orgstruktur_vs_be.ditaa.txt"
		}));
		
		Iterator it = textFiles.iterator();
		while(it.hasNext()){
			String filename = it.next().toString();
			if(!filename.matches(".+\\.txt$") || isInExcluded(filename, excludedFiles)){
				it.remove();
			}
		}
		
		tester.createHTMLTestReport(textFiles, reportDir, HTMLReportName);
		
		System.out.println("Tests completed");
	}

	private static boolean isInExcluded(String filename, Set excludedSet) {
		for(String excluded : excludedSet) {
			if(filename.endsWith(excluded)) return true;
		}
		return false;
	}
	
	public boolean createHTMLTestReport(ArrayList textFiles, String reportDir, String reportName){

		ConversionOptions options = new ConversionOptions();

		String reportFilename = reportDir+"/"+reportName+".html";

		if(!(new File(reportDir).exists())){
			File dir = new File(reportDir);
			dir.mkdir();
		}

		PrintWriter s = null;
		try {
			s = new PrintWriter(new FileWriter(reportFilename));
		} catch (IOException e) {
			System.err.println("Cannot open file "+reportFilename+" for writing:");
			e.printStackTrace();
			return false;
		}

		s.println("");
		s.println("

ditaa test suite

"); s.println("

generated on: "+Calendar.getInstance().getTime()+"

"); for(File textFile : textFiles) { TextGrid grid = new TextGrid(); File toFile = new File(reportDir + File.separator + textFile.getName() + ".png"); long a = java.lang.System.nanoTime(); long b; try { System.out.println("Rendering "+textFile+" to "+toFile); grid.loadFrom(textFile.toString()); Diagram diagram = new Diagram(grid, options); RenderedImage image = new BitmapRenderer().renderToImage(diagram, options.renderingOptions); b = java.lang.System.nanoTime(); java.lang.System.out.println( "Done in " + Math.round((b - a)/10e6) + "msec"); try { File file = new File(toFile.getAbsolutePath()); ImageIO.write(image, "png", file); } catch (IOException e) { //e.printStackTrace(); System.err.println("Error: Cannot write to file "+toFile); System.exit(1); } } catch (Exception e) { s.println("!!! Failed to render: "+textFile+" !!!"); s.println("
\n"+grid.getDebugString()+"\n
"); s.println(e.getMessage()); s.println("
"); s.flush(); System.err.println("!!! Failed to render: "+textFile+" !!!"); e.printStackTrace(System.err); continue; } s.println(makeReportTable(textFile.getName(), grid, toFile.getName(), b - a)); s.println("
"); s.flush(); } s.println(""); s.flush(); s.close(); System.out.println("Wrote HTML report to " + new File(reportFilename).getAbsolutePath()); return true; } private String makeReportTable(String gridURI, TextGrid grid, String imageURI, long time){ StringBuffer buffer = new StringBuffer("
"); buffer.append(""); buffer.append(""); buffer.append(""); buffer.append("

"+gridURI+" ("+Math.round(time/10e6)+"msec)

\n"+grid.getDebugString()+"\n
"); return buffer.toString(); } } ditaa-0.9/src/org/stathissideris/ascii2image/core/PerformanceTester.java0000644000175000017500000000361411303043274024611 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.core; import java.io.FileNotFoundException; import java.io.IOException; import org.stathissideris.ascii2image.graphics.Diagram; import org.stathissideris.ascii2image.text.TextGrid; /** * * @author Efstathios Sideris */ public class PerformanceTester { public static void main(String[] args){ String inputFilename = "tests/text/ditaa_bug.txt"; ConversionOptions options = new ConversionOptions(); int iterations = 10; try { long a = java.lang.System.currentTimeMillis(); for(int i = 0; i < iterations; i++) { System.out.println("iteration "+i); TextGrid grid = new TextGrid(); grid.loadFrom(inputFilename); new Diagram(grid, options); } long b = java.lang.System.currentTimeMillis(); System.out.println((b-a) + "msec for " + iterations + " iterations on "+inputFilename); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } System.out.println("Tests completed"); } } ditaa-0.9/src/org/stathissideris/ascii2image/core/ProcessingOptions.java0000644000175000017500000001167311277077356024677 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.core; import java.util.HashMap; import org.stathissideris.ascii2image.graphics.CustomShapeDefinition; /** * @author Efstathios Sideris * */ public class ProcessingOptions { private HashMap customShapes = new HashMap(); private boolean beVerbose = false; private boolean printDebugOutput = false; private boolean overwriteFiles = false; private boolean performSeparationOfCommonEdges = true; private boolean allCornersAreRound = false; public static final int USE_TAGS = 0; public static final int RENDER_TAGS = 1; public static final int IGNORE_TAGS = 2; private int tagProcessingMode = USE_TAGS; public static final int USE_COLOR_CODES = 0; public static final int RENDER_COLOR_CODES = 1; public static final int IGNORE_COLOR_CODES = 2; private int colorCodesProcessingMode = USE_COLOR_CODES; public static final int FORMAT_JPEG = 0; public static final int FORMAT_PNG = 1; public static final int FORMAT_GIF = 2; private int exportFormat = FORMAT_PNG; public static final int DEFAULT_TAB_SIZE = 8; private int tabSize = DEFAULT_TAB_SIZE; private String inputFilename; private String outputFilename; private String characterEncoding = null; /** * @return */ public boolean areAllCornersRound() { return allCornersAreRound; } /** * @return */ public int getColorCodesProcessingMode() { return colorCodesProcessingMode; } /** * @return */ public int getExportFormat() { return exportFormat; } /** * @return */ public boolean performSeparationOfCommonEdges() { return performSeparationOfCommonEdges; } /** * @return */ public int getTagProcessingMode() { return tagProcessingMode; } /** * @param b */ public void setAllCornersAreRound(boolean b) { allCornersAreRound = b; } /** * @param i */ public void setColorCodesProcessingMode(int i) { colorCodesProcessingMode = i; } /** * @param i */ public void setExportFormat(int i) { exportFormat = i; } /** * @param b */ public void setPerformSeparationOfCommonEdges(boolean b) { performSeparationOfCommonEdges = b; } /** * @param i */ public void setTagProcessingMode(int i) { tagProcessingMode = i; } /** * @return */ public String getInputFilename() { return inputFilename; } /** * @return */ public String getOutputFilename() { return outputFilename; } /** * @param string */ public void setInputFilename(String string) { inputFilename = string; } /** * @param string */ public void setOutputFilename(String string) { outputFilename = string; } /** * @return */ public boolean verbose() { return beVerbose; } /** * @return */ public boolean printDebugOutput() { return printDebugOutput; } /** * @param b */ public void setVerbose(boolean b) { beVerbose = b; } /** * @param b */ public void setPrintDebugOutput(boolean b) { printDebugOutput = b; } /** * @return */ public boolean overwriteFiles() { return overwriteFiles; } /** * @param b */ public void setOverwriteFiles(boolean b) { overwriteFiles = b; } /** * @return */ public int getTabSize() { return tabSize; } /** * @param i */ public void setTabSize(int i) { tabSize = i; } public String getCharacterEncoding() { return characterEncoding; } public void setCharacterEncoding(String characterEncoding) { this.characterEncoding = characterEncoding; } public HashMap getCustomShapes() { return customShapes; } public void setCustomShapes(HashMap customShapes) { this.customShapes = customShapes; } public void putAllInCustomShapes(HashMap customShapes) { this.customShapes.putAll(customShapes); } public CustomShapeDefinition getFromCustomShapes(String tagName){ return customShapes.get(tagName); } } ditaa-0.9/src/org/stathissideris/ascii2image/core/DebugUtils.java0000644000175000017500000000026611277077356023252 0ustar neoneopackage org.stathissideris.ascii2image.core; public class DebugUtils { public static int getLineNumber() { return Thread.currentThread().getStackTrace()[2].getLineNumber(); } } ditaa-0.9/src/org/stathissideris/ascii2image/core/ConfigurationParser.java0000644000175000017500000001430011277077356025161 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.core; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.HashMap; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.stathissideris.ascii2image.graphics.CustomShapeDefinition; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; public class ConfigurationParser { private static final boolean DEBUG = true; private static final String INLCUDE_TAG_NAME = "include"; private static final String SHAPE_TAG_NAME = "shape"; private static final String SHAPE_GROUP_TAG_NAME = "shapes"; private String currentDir = ""; private File configFile; private HashMap shapeDefinitions = new HashMap(); public Collection getShapeDefinitions() { return shapeDefinitions.values(); } public HashMap getShapeDefinitionsHash() { return shapeDefinitions; } public void parseFile(File file) throws ParserConfigurationException, SAXException, IOException { configFile = file; DefaultHandler handler = new XMLHandler(); // Use the default (non-validating) parser SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser saxParser = factory.newSAXParser(); saxParser.parse(file, handler); } private class XMLHandler extends DefaultHandler{ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if(qName.equals(SHAPE_GROUP_TAG_NAME)){ if(attributes.getLength() == 1){ currentDir = attributes.getValue(0).trim(); if(currentDir.equals("")) currentDir = configFile.getParentFile().getAbsolutePath(); } else { //the dir that contains the config file: currentDir = configFile.getParentFile().getAbsolutePath(); } } if(qName.equals(SHAPE_TAG_NAME)){ CustomShapeDefinition definition = new CustomShapeDefinition(); int len = attributes.getLength(); for(int i = 0; i < len; i++){ String name = attributes.getQName(i); String value = attributes.getValue(i); if(name.equals("tag")){ definition.setTag(value); } else if(name.equals("stretch")){ definition.setStretches(getBooleanFromAttributeValue(value)); } else if(name.equals("border")){ definition.setHasBorder(getBooleanFromAttributeValue(value)); } else if(name.equals("shadow")){ definition.setDropsShadow(getBooleanFromAttributeValue(value)); } else if(name.equals("comment")){ definition.setComment(value); } else if(name.equals("filename")){ File file = new File(value); if(file.isAbsolute()){ definition.setFilename(value); } else { //relative to the location of the config file or to the group's base dir definition.setFilename(createFilename(currentDir, value)); } } } if(shapeDefinitions.containsKey(definition.getTag())){ CustomShapeDefinition oldDef = shapeDefinitions.get(definition.getTag()); System.err.println( "*** Warning: shape \""+oldDef.getTag()+"\" (file: " +oldDef.getFilename()+") has been redefined as file: " +definition.getFilename() ); } File file = new File(definition.getFilename()); if(file.exists()){ shapeDefinitions.put(definition.getTag(), definition); if(DEBUG) System.out.println(definition); } else { System.err.println("File "+file+" does not exist, skipping tag "+definition.getTag()); } } if(qName.equals(INLCUDE_TAG_NAME)){ if(attributes.getLength() == 1){ File includedFile = new File(attributes.getValue(0).trim()); if(!includedFile.isAbsolute()){ includedFile = new File( createFilename( configFile.getParentFile().getAbsolutePath(), includedFile.getPath())); } if(!includedFile.exists()){ System.err.println("Included file "+includedFile+" does not exist, skipping"); return; } ConfigurationParser configParser = new ConfigurationParser(); try { configParser.parseFile(includedFile); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } HashMap shapes = configParser.getShapeDefinitionsHash(); shapeDefinitions.putAll(shapes); } } } } private String createFilename(String baseDir, String filename){ if(baseDir == null || baseDir.trim().equals("")){ return filename; } if(baseDir.endsWith(File.separator)){ return baseDir + filename; } return baseDir + File.separator + filename; } private boolean getBooleanFromAttributeValue(String value){ value = value.toLowerCase(); if("no".equals(value)) return false; if("false".equals(value)) return false; if("yes".equals(value)) return true; if("true".equals(value)) return true; throw new IllegalArgumentException("value "+value+" cannot be interpreted as a boolean"); } public static void main(String argv[]) throws ParserConfigurationException, SAXException, IOException { ConfigurationParser parser = new ConfigurationParser(); parser.parseFile(new File("config.xml")); parser.getShapeDefinitions(); } } ditaa-0.9/src/org/stathissideris/ascii2image/core/HTMLConverter.java0000644000175000017500000001455711277077356023647 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.core; import java.awt.image.RenderedImage; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Iterator; import java.util.List; import javax.imageio.ImageIO; import javax.swing.text.MutableAttributeSet; import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.HTML.Tag; import org.stathissideris.ascii2image.graphics.BitmapRenderer; import org.stathissideris.ascii2image.graphics.Diagram; import org.stathissideris.ascii2image.text.StringUtils; import org.stathissideris.ascii2image.text.TextGrid; import au.id.jericho.lib.html.Attribute; import au.id.jericho.lib.html.Element; import au.id.jericho.lib.html.OutputDocument; import au.id.jericho.lib.html.Source; import au.id.jericho.lib.html.StartTag; import au.id.jericho.lib.html.StringOutputSegment; /** * * TODO: incomplete class * * @author Efstathios Sideris */ public class HTMLConverter extends HTMLEditorKit { private static final String TAG_CLASS = "textdiagram"; public static void main(String[] args){ new HTMLConverter().convertHTMLFile("index.html", "index2.html", "ditaa_diagram", "images", null); } public boolean convertHTMLFile( String filename, String targetFilename, String imageBaseFilename, String imageDirName, ConversionOptions options){ if(options == null){ options = new ConversionOptions(); } File imageDir = new File(imageDirName); if(!imageDir.exists()){ if(!imageDir.mkdir()){ System.err.println("Could not create directory " + imageDirName); return false; } } BufferedReader in = null; try { in = new BufferedReader(new FileReader(filename)); } catch (FileNotFoundException e) { //e.printStackTrace(); System.err.println("Error: cannot read file " + filename); return false; } String htmlText = ""; try { while(in.ready()){ htmlText += in.readLine()+"\n"; } in.close(); } catch (IOException e1) { //e1.printStackTrace(); System.err.println("Error while reading file " + filename); return false; } System.out.print("Convering HTML file ("+filename+" -> "+targetFilename+")... "); Source source = new Source(htmlText); OutputDocument outputDocument = new OutputDocument(htmlText); int index = 1; HashMap diagramList = new HashMap(); List linkStartTags = source.findAllElements("pre"); Iterator it = linkStartTags.iterator(); while (it.hasNext()) { Element element = (Element) it.next(); StartTag tag = element.getStartTag(); Attribute classAttr = tag.getAttributes().get("class"); if(classAttr != null && classAttr.hasValue() && classAttr.getValue().equals(TAG_CLASS)) { String baseFilename = imageBaseFilename; String URL; Attribute nameAttr = tag.getAttributes().get("id"); if(nameAttr != null && nameAttr.hasValue()) { baseFilename = makeFilenameFromTagName(nameAttr.getValue()); URL = imageDirName + "/" + baseFilename + ".png"; } else { URL = imageDirName + "/" + baseFilename + "_" + index + ".png"; index++; } outputDocument.add(new StringOutputSegment(element, "")); diagramList.put(URL, element.getContent().getSourceText()); } } if(diagramList.isEmpty()){ System.out.println("\nHTML document does not contain any " + "
 tags with their class attribute set to \""+TAG_CLASS+"\". Nothing to do.");
			
			//TODO: should return the method with appropriate exit code instead
			System.exit(0);
		}
		
		FileWriter out;
		try {
			out = new FileWriter(targetFilename);
			outputDocument.output(out);
			//out.flush();
			//out.close();
		} catch (IOException e2) {
			System.err.println("Error while writing to file " + targetFilename);
			return false;
		} 

		
		System.out.println("done");
		
		
		System.out.println("Generating diagrams... ");
		
		it = diagramList.keySet().iterator();
		while (it.hasNext()) {
			String URL = (String) it.next();
			String text = (String) diagramList.get(URL);
			if(new File(URL).exists() && !options.processingOptions.overwriteFiles()){
				System.out.println("Error: Cannot overwrite to file "+URL+", file already exists." +
					" Use the --overwrite option if you would like to allow file overwrite.");
				continue;
			}
	


			TextGrid grid = new TextGrid();
			grid.addToMarkupTags(options.processingOptions.getCustomShapes().keySet());

			try {
				grid.initialiseWithText(text, options.processingOptions);
			} catch (UnsupportedEncodingException e1) {
				System.err.println("Error: "+e1.getMessage());
				System.exit(1);
			}

			Diagram diagram = new Diagram(grid, options);
			RenderedImage image = new BitmapRenderer().renderToImage(diagram, options.renderingOptions);

			try {
				File file = new File(URL);
				ImageIO.write(image, "png", file);
			} catch (IOException e) {
				//e.printStackTrace();
				System.err.println("Error: Cannot write to file "+filename+" -- skipping");
				continue;
			}
			
			System.out.println("\t"+URL);
		}
		
		System.out.println("\n...done");
		
		return true;
	}
	
	private String makeFilenameFromTagName(String tagName){
		tagName = tagName.replace(' ', '_');
		return tagName;
	}
	
}
ditaa-0.9/src/org/stathissideris/ascii2image/graphics/0000755000175000017500000000000011277077356021204 5ustar  neoneoditaa-0.9/src/org/stathissideris/ascii2image/graphics/BitmapRenderer.java0000644000175000017500000003646311277502026024751 0ustar  neoneo/*
 * DiTAA - Diagrams Through Ascii Art
 * 
 * Copyright (C) 2004 Efstathios Sideris
 *
 * This 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *   
 */
package org.stathissideris.ascii2image.graphics;

import java.awt.BasicStroke;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;

import javax.imageio.ImageIO;

import org.stathissideris.ascii2image.core.ConversionOptions;
import org.stathissideris.ascii2image.core.RenderingOptions;
import org.stathissideris.ascii2image.core.Shape3DOrderingComparator;
import org.stathissideris.ascii2image.text.TextGrid;

/**
 * 
 * @author Efstathios Sideris
 */
public class BitmapRenderer {

	private static final boolean DEBUG = false;

	private static final String IDREGEX = "^.+_vfill$";
	
	Stroke normalStroke;
	Stroke dashStroke; 
	
	public static void main(String[] args) throws Exception {
		
		
		long startTime = System.currentTimeMillis();
		
		ConversionOptions options = new ConversionOptions();
		
		TextGrid grid = new TextGrid();
		
		String filename = "dak_orgstruktur_vs_be.ditaa.OutOfMemoryError.edit.txt";
		
		grid.loadFrom("tests/text/"+filename);
		
		Diagram diagram = new Diagram(grid, options);
		new BitmapRenderer().renderToPNG(diagram, "tests/images/"+filename+".png", options.renderingOptions);
		long endTime = System.currentTimeMillis();
		long totalTime  = (endTime - startTime) / 1000;
		System.out.println("Done in "+totalTime+"sec");
		
		File workDir = new File("tests/images");
		//Process p = Runtime.getRuntime().exec("display "+filename+".png", null, workDir);
	}

	private boolean renderToPNG(Diagram diagram, String filename, RenderingOptions options){	
		RenderedImage image = renderToImage(diagram, options);
		
		try {
			File file = new File(filename);
			ImageIO.write(image, "png", file);
		} catch (IOException e) {
			//e.printStackTrace();
			System.err.println("Error: Cannot write to file "+filename);
			return false;
		}
		return true;
	}
	
	public RenderedImage renderToImage(Diagram diagram,  RenderingOptions options){
		BufferedImage image = new BufferedImage(
					diagram.getWidth(),
					diagram.getHeight(),
					BufferedImage.TYPE_INT_RGB);
		
		return render(diagram, image, options);
	}
	
	public RenderedImage render(Diagram diagram, BufferedImage image,  RenderingOptions options){
		RenderedImage renderedImage = image;
		Graphics2D g2 = image.createGraphics();

		Object antialiasSetting = RenderingHints.VALUE_ANTIALIAS_OFF;
		if(options.performAntialias())
			antialiasSetting = RenderingHints.VALUE_ANTIALIAS_ON;
		
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antialiasSetting);

		g2.setColor(Color.white);
		//TODO: find out why the next line does not work
		g2.fillRect(0, 0, image.getWidth()+10, image.getHeight()+10);
		/*for(int y = 0; y < diagram.getHeight(); y ++)
			g2.drawLine(0, y, diagram.getWidth(), y);*/
		
		g2.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND));

		ArrayList shapes = diagram.getAllDiagramShapes();

		if(DEBUG) System.out.println("Rendering "+shapes.size()+" shapes (groups flattened)");

		Iterator shapesIt;
		if(options.dropShadows()){
			//render shadows
			shapesIt = shapes.iterator();
			while(shapesIt.hasNext()){
				DiagramShape shape = (DiagramShape) shapesIt.next();

				if(shape.getPoints().isEmpty()) continue;

				//GeneralPath path = shape.makeIntoPath();
				GeneralPath path;
				path = shape.makeIntoRenderPath(diagram);			
							
				float offset = diagram.getMinimumOfCellDimension() / 3.333f;
			
				if(path != null
						&& shape.dropsShadow()
						&& shape.getType() != DiagramShape.TYPE_CUSTOM){
					GeneralPath shadow = new GeneralPath(path);
					AffineTransform translate = new AffineTransform();
					translate.setToTranslation(offset, offset);
					shadow.transform(translate);
					g2.setColor(new Color(150,150,150));
					g2.fill(shadow);
				
				}
			}

		
			//blur shadows
		
			if(true) {
				int blurRadius = 6;
				int blurRadius2 = blurRadius * blurRadius;
				float blurRadius2F = blurRadius2;
				float weight = 1.0f / blurRadius2F;
				float[] elements = new float[blurRadius2];
				for (int k = 0; k < blurRadius2; k++)
					elements[k] = weight;
				Kernel myKernel = new Kernel(blurRadius, blurRadius, elements);

				//if EDGE_NO_OP is not selected, EDGE_ZERO_FILL is the default which creates a black border 
				ConvolveOp simpleBlur =
					new ConvolveOp(myKernel, ConvolveOp.EDGE_NO_OP, null);
								
				BufferedImage destination =
					new BufferedImage(
						image.getWidth(),
						image.getHeight(),
						image.getType());

				simpleBlur.filter(image, (BufferedImage) destination);

				//destination = destination.getSubimage(blurRadius/2, blurRadius/2, image.getWidth(), image.getHeight()); 
				g2 = (Graphics2D) destination.getGraphics();
				g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antialiasSetting);
				renderedImage = (RenderedImage) destination;
			}
		}

		
		//fill and stroke
		
		float dashInterval = Math.min(diagram.getCellWidth(), diagram.getCellHeight()) / 2;
		//Stroke normalStroke = g2.getStroke();
		
		float strokeWeight = diagram.getMinimumOfCellDimension() / 10;
		
		normalStroke =
		  new BasicStroke(
			strokeWeight,
			//10,
			BasicStroke.CAP_ROUND,
			BasicStroke.JOIN_ROUND
		  );

		dashStroke = 
		  new BasicStroke(
			strokeWeight,
			BasicStroke.CAP_BUTT,
			BasicStroke.JOIN_ROUND,
			0,
			new float[] {dashInterval}, 
			0
		  );
		
		//TODO: at this stage we should draw the open shapes first in order to make sure they are at the bottom (this is useful for the {mo} shape) 
		
		
		//find storage shapes
		ArrayList storageShapes = new ArrayList();
		shapesIt = shapes.iterator();
		while(shapesIt.hasNext()){
			DiagramShape shape = (DiagramShape) shapesIt.next();
			if(shape.getType() == DiagramShape.TYPE_STORAGE) {
				storageShapes.add(shape);
				continue;
			} 
		}

		//render storage shapes
		//special case since they are '3d' and should be
		//rendered bottom to top
		//TODO: known bug: if a storage object is within a bigger normal box, it will be overwritten in the main drawing loop
		//(BUT this is not possible since tags are applied to all shapes overlaping shapes)

		
		Collections.sort(storageShapes, new Shape3DOrderingComparator());
		
		g2.setStroke(normalStroke);
		shapesIt = storageShapes.iterator();
		while(shapesIt.hasNext()){
			DiagramShape shape = (DiagramShape) shapesIt.next();

			GeneralPath path;
			path = shape.makeIntoRenderPath(diagram);
			
			if(!shape.isStrokeDashed()) {
				if(shape.getFillColor() != null)
					g2.setColor(shape.getFillColor());
				else
					g2.setColor(Color.white);
				g2.fill(path);
			}

			if(shape.isStrokeDashed())
				g2.setStroke(dashStroke);
			else
				g2.setStroke(normalStroke);
			g2.setColor(shape.getStrokeColor());
			g2.draw(path);
		}


		//render the rest of the shapes
		ArrayList pointMarkers = new ArrayList();
		shapesIt = shapes.iterator();
		while(shapesIt.hasNext()){
			DiagramShape shape = (DiagramShape) shapesIt.next();
			if(shape.getType() == DiagramShape.TYPE_POINT_MARKER) {
				pointMarkers.add(shape);
				continue;
			} 
			if(shape.getType() == DiagramShape.TYPE_STORAGE) {
				continue;
			} 
			if(shape.getType() == DiagramShape.TYPE_CUSTOM){
				renderCustomShape(shape, g2);
				continue;
			}

			if(shape.getPoints().isEmpty()) continue;

			int size = shape.getPoints().size();
			
			GeneralPath path;
			path = shape.makeIntoRenderPath(diagram);
			
			//fill
			if(path != null && shape.isClosed() && !shape.isStrokeDashed()){
				if(shape.getFillColor() != null)
					g2.setColor(shape.getFillColor());
				else
					g2.setColor(Color.white);
				g2.fill(path);
			}
			
			//draw
			if(shape.getType() != DiagramShape.TYPE_ARROWHEAD){
				g2.setColor(shape.getStrokeColor());
				if(shape.isStrokeDashed())
					g2.setStroke(dashStroke);
				else
					g2.setStroke(normalStroke);
				g2.draw(path);
			}
		}
		
		//render point markers
		
		g2.setStroke(normalStroke);
		shapesIt = pointMarkers.iterator();
		while(shapesIt.hasNext()){
			DiagramShape shape = (DiagramShape) shapesIt.next();
			//if(shape.getType() != DiagramShape.TYPE_POINT_MARKER) continue;

			GeneralPath path;
			path = shape.makeIntoRenderPath(diagram);
			
			g2.setColor(Color.white);
			g2.fill(path);
			g2.setColor(shape.getStrokeColor());
			g2.draw(path);
		}		
		
		//handle text
		//g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		//renderTextLayer(diagram.getTextObjects().iterator());
		
		Iterator textIt = diagram.getTextObjects().iterator();
		while(textIt.hasNext()){
			DiagramText text = (DiagramText) textIt.next();
			g2.setFont(text.getFont());
			if(text.hasOutline()){
				g2.setColor(text.getOutlineColor());
				g2.drawString(text.getText(), text.getXPos() + 1, text.getYPos());
				g2.drawString(text.getText(), text.getXPos() - 1, text.getYPos());
				g2.drawString(text.getText(), text.getXPos(), text.getYPos() + 1);
				g2.drawString(text.getText(), text.getXPos(), text.getYPos() - 1);
			}
			g2.setColor(text.getColor());
			g2.drawString(text.getText(), text.getXPos(), text.getYPos());
		}
		
		if(options.renderDebugLines() || DEBUG){
			Stroke debugStroke =
			  new BasicStroke(
				1,
				BasicStroke.CAP_ROUND,
				BasicStroke.JOIN_ROUND
			  );
			g2.setStroke(debugStroke);
			g2.setColor(new Color(170, 170, 170));
			g2.setXORMode(Color.white);
			for(int x = 0; x < diagram.getWidth(); x += diagram.getCellWidth())
				g2.drawLine(x, 0, x, diagram.getHeight());
			for(int y = 0; y < diagram.getHeight(); y += diagram.getCellHeight())
				g2.drawLine(0, y, diagram.getWidth(), y);
		}
		

		g2.dispose();
		
		return renderedImage;
	}
	
	private RenderedImage renderTextLayer(ArrayList textObjects, int width, int height){
		TextCanvas canvas = new TextCanvas(textObjects);
		Image image = canvas.createImage(width, height);
		Graphics g = image.getGraphics();
		canvas.paint(g);
		return (RenderedImage) image;
	}
	
	private class TextCanvas extends Canvas {
		ArrayList textObjects;
		
		public TextCanvas(ArrayList textObjects){
			this.textObjects = textObjects;
		}
		
		public void paint(Graphics g){
			Graphics g2 = (Graphics2D) g;
			Iterator textIt = textObjects.iterator();
			while(textIt.hasNext()){
				DiagramText text = (DiagramText) textIt.next();
				g2.setFont(text.getFont());
				if(text.hasOutline()){
					g2.setColor(text.getOutlineColor());
					g2.drawString(text.getText(), text.getXPos() + 1, text.getYPos());
					g2.drawString(text.getText(), text.getXPos() - 1, text.getYPos());
					g2.drawString(text.getText(), text.getXPos(), text.getYPos() + 1);
					g2.drawString(text.getText(), text.getXPos(), text.getYPos() - 1);
				}
				g2.setColor(text.getColor());
				g2.drawString(text.getText(), text.getXPos(), text.getYPos());
			}
		}
	}
	
	private void renderCustomShape(DiagramShape shape, Graphics2D g2){
		CustomShapeDefinition definition = shape.getDefinition();
		
		Rectangle bounds = shape.getBounds();
		
		if(definition.hasBorder()){
			g2.setColor(shape.getStrokeColor());
			if(shape.isStrokeDashed())
				g2.setStroke(dashStroke);
			else
				g2.setStroke(normalStroke);
			g2.drawLine(bounds.x, bounds.y, bounds.x + bounds.width, bounds.y);
			g2.drawLine(bounds.x + bounds.width, bounds.y, bounds.x + bounds.width, bounds.y + bounds.height);
			g2.drawLine(bounds.x, bounds.y + bounds.height, bounds.x + bounds.width, bounds.y + bounds.height);
			g2.drawLine(bounds.x, bounds.y, bounds.x, bounds.y + bounds.height);
			
//			g2.drawRect(bounds.x, bounds.y, bounds.width, bounds.height); //looks different!			
		}
		
		//TODO: custom shape distintion relies on filename extension. Make this more intelligent
		if(definition.getFilename().endsWith(".png")){
			renderCustomPNGShape(shape, g2);
		} else if(definition.getFilename().endsWith(".svg")){
			renderCustomSVGShape(shape, g2);
		}
	}
	
	private void renderCustomSVGShape(DiagramShape shape, Graphics2D g2){
		CustomShapeDefinition definition = shape.getDefinition();
		Rectangle bounds = shape.getBounds();
		Image graphic;
		try {
			if(shape.getFillColor() == null) {
				graphic = ImageHandler.instance().renderSVG(
						definition.getFilename(), bounds.width, bounds.height, definition.stretches());
			} else {
				graphic = ImageHandler.instance().renderSVG(
						definition.getFilename(), bounds.width, bounds.height, definition.stretches(), IDREGEX, shape.getFillColor());				
			}
			g2.drawImage(graphic, bounds.x, bounds.y, null);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void renderCustomPNGShape(DiagramShape shape, Graphics2D g2){
		CustomShapeDefinition definition = shape.getDefinition();
		Rectangle bounds = shape.getBounds();
		Image graphic = ImageHandler.instance().loadImage(definition.getFilename());
		
		int xPos, yPos, width, height;
		
		if(definition.stretches()){ //occupy all available space
			xPos = bounds.x; yPos = bounds.y;
			width = bounds.width; height = bounds.height;
		} else { //decide how to fit
			int newHeight = bounds.width * graphic.getHeight(null) / graphic.getWidth(null);
			if(newHeight < bounds.height){ //expand to fit width
				height = newHeight;
				width = bounds.width;
				xPos = bounds.x;
				yPos = bounds.y + bounds.height / 2 - graphic.getHeight(null) / 2;
			} else { //expand to fit height
				width = graphic.getWidth(null) * bounds.height / graphic.getHeight(null);
				height = bounds.height;
				xPos = bounds.x + bounds.width / 2 - graphic.getWidth(null) / 2;
				yPos = bounds.y;
			}
		}
		
		g2.drawImage(graphic, xPos, yPos, width, height, null);		
	}
	
	public static boolean isColorDark(Color color){
		int brightness = Math.max(color.getRed(), color.getGreen());
		brightness = Math.max(color.getBlue(), brightness);
		if(brightness < 200) {
			if(DEBUG) System.out.println("Color "+color+" is dark");
			return true;
		}
		if(DEBUG) System.out.println("Color "+color+" is not dark");
		return false;
	}
}
ditaa-0.9/src/org/stathissideris/ascii2image/graphics/DiagramComponent.java0000644000175000017500000001030711277077356025277 0ustar  neoneo/*
 * DiTAA - Diagrams Through Ascii Art
 * 
 * Copyright (C) 2004 Efstathios Sideris
 *
 * This 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *   
 */
package org.stathissideris.ascii2image.graphics;

import java.util.ArrayList;
import java.util.Iterator;

import org.stathissideris.ascii2image.text.CellSet;
import org.stathissideris.ascii2image.text.TextGrid;

/**
 * 
 * @author Efstathios Sideris
 */
public abstract class DiagramComponent {
	
	private static final boolean DEBUG = false;
	
	protected static ShapePoint makePointForCell(TextGrid.Cell cell, TextGrid grid, int cellWidth, int cellHeight, boolean allRound){
		if (DEBUG)
			System.out.println("Found point at cell "+cell);
		if(grid.isCorner(cell) && allRound){
			return new ShapePoint(
							cell.x * cellWidth + cellWidth/2,
							cell.y * cellHeight + cellHeight/2,
							ShapePoint.TYPE_ROUND  
						);
		} else if(grid.isNormalCorner(cell)){
			return new ShapePoint(
							cell.x * cellWidth + cellWidth/2,
							cell.y * cellHeight + cellHeight/2,
							ShapePoint.TYPE_NORMAL  
						);
		} else if(grid.isRoundCorner(cell)){
			return new ShapePoint(
							cell.x * cellWidth + cellWidth/2,
							cell.y * cellHeight + cellHeight/2,
							ShapePoint.TYPE_ROUND  
						);
		} else if(grid.isLinesEnd(cell)){
			return new ShapePoint(
						cell.x * cellWidth + cellWidth/2,
						cell.y * cellHeight + cellHeight/2,
						ShapePoint.TYPE_NORMAL  
					);
		} else if(grid.isIntersection(cell)){
			return new ShapePoint(
					cell.x * cellWidth + cellWidth/2,
					cell.y * cellHeight + cellHeight/2,
					ShapePoint.TYPE_NORMAL 
				);
		}
		throw new RuntimeException("Cannot make point for cell "+cell);
	}

	public static DiagramComponent createClosedFromBoundaryCells(TextGrid grid, CellSet cells, int cellWidth, int cellHeight){
		return createClosedFromBoundaryCells(grid, cells, cellWidth, cellHeight, false);
	}

	public static DiagramComponent createClosedFromBoundaryCells(TextGrid grid, CellSet cells, int cellWidth, int cellHeight, boolean allRound){
		if(cells.getType(grid) == CellSet.TYPE_OPEN) throw new IllegalArgumentException("CellSet is closed and cannot be handled by this method");
		if(cells.size() < 2) return null;
		
		DiagramShape shape = new DiagramShape();
		shape.setClosed(true);
		if(grid.containsAtLeastOneDashedLine(cells)) shape.setStrokeDashed(true);
		
		TextGrid workGrid = new TextGrid(grid.getWidth(), grid.getHeight());
		grid.copyCellsTo(cells, workGrid);
		
		if (DEBUG){
			System.out.println("Making closed shape from buffer:");
			workGrid.printDebug();
		}
		
		TextGrid.Cell start = (TextGrid.Cell) cells.getFirst();
		if(workGrid.isCorner(start)) shape.addToPoints(makePointForCell(start, workGrid, cellWidth, cellHeight, allRound));
		TextGrid.Cell previous = start;
		TextGrid.Cell cell = null;
		CellSet nextCells = workGrid.followCell(previous);
		if(nextCells.size() == 0) return null;
		cell = (TextGrid.Cell) nextCells.getFirst();
		if(workGrid.isCorner(cell)) shape.addToPoints(makePointForCell(cell, workGrid, cellWidth, cellHeight, allRound));
		
		while(!cell.equals(start)){
			nextCells = workGrid.followCell(cell, previous);
			if(nextCells.size() == 1) {
				previous = cell;
				cell = (TextGrid.Cell) nextCells.getFirst();
				if(!cell.equals(start) && workGrid.isCorner(cell))
					shape.addToPoints(makePointForCell(cell, workGrid, cellWidth, cellHeight, allRound));
			} else if(nextCells.size() > 1) {
				return null;
			}
		}
		
		return shape;
		
	}
}
ditaa-0.9/src/org/stathissideris/ascii2image/graphics/DiagramShape.java0000644000175000017500000007344511303043012024355 0ustar  neoneo/*
 * DiTAA - Diagrams Through Ascii Art
 * 
 * Copyright (C) 2004 Efstathios Sideris
 *
 * This 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *   
 */
package org.stathissideris.ascii2image.graphics;

import java.awt.Color;
import java.awt.Rectangle;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import org.stathissideris.ascii2image.text.*;

/**
 * 
 * @author Efstathios Sideris
 */
public class DiagramShape extends DiagramComponent {
	
	private static final boolean DEBUG = false;
	
	public static final int TYPE_SIMPLE = 0;
	public static final int TYPE_ARROWHEAD = 1;
	public static final int TYPE_POINT_MARKER = 2;
	public static final int TYPE_DOCUMENT = 3;
	public static final int TYPE_STORAGE = 4;
	public static final int TYPE_IO = 5;
	public static final int TYPE_DECISION = 6;
	public static final int TYPE_MANUAL_OPERATION = 7; // upside-down trapezoid
	public static final int TYPE_TRAPEZOID = 8; // rightside-up trapezoid
	public static final int TYPE_ELLIPSE = 9;
	public static final int TYPE_CUSTOM = 9999;

	protected int type = TYPE_SIMPLE;

	private Color fillColor = null;
	private Color strokeColor = Color.black;
	
	private boolean isClosed = false;
	private boolean isStrokeDashed = false;

	protected ArrayList points = new ArrayList();

	CustomShapeDefinition definition = null;

	public static void main(String[] args) {
	}

	public static DiagramShape createArrowhead(TextGrid grid, TextGrid.Cell cell, int cellXSize, int cellYSize) {
		if(!grid.isArrowhead(cell)) return null;
		if(grid.isNorthArrowhead(cell)) return createNorthArrowhead(grid, cell, cellXSize, cellYSize);
		if(grid.isSouthArrowhead(cell)) return createSouthArrowhead(grid, cell, cellXSize, cellYSize);
		if(grid.isWestArrowhead(cell)) return createWestArrowhead(grid, cell, cellXSize, cellYSize);
		if(grid.isEastArrowhead(cell)) return createEastArrowhead(grid, cell, cellXSize, cellYSize);
		return null;
	}

	private static DiagramShape createNorthArrowhead(TextGrid grid, TextGrid.Cell cell, int cellXSize, int cellYSize) {
		if(!grid.isNorthArrowhead(cell)) return null;
		DiagramShape shape = new DiagramShape();
		shape.addToPoints(new ShapePoint(
			Diagram.getCellMidX(cell,cellXSize),
			Diagram.getCellMinY(cell,cellYSize)));
		shape.addToPoints(new ShapePoint(
			Diagram.getCellMinX(cell,cellXSize),
			Diagram.getCellMaxY(cell,cellYSize)));
		shape.addToPoints(new ShapePoint(
			Diagram.getCellMaxX(cell,cellXSize),
			Diagram.getCellMaxY(cell,cellYSize)));
		shape.setClosed(true);
		shape.setFillColor(Color.black);
		shape.setStrokeColor(Color.black);
		shape.setType(TYPE_ARROWHEAD);
		return shape;
	}

	private static DiagramShape createSouthArrowhead(TextGrid grid, TextGrid.Cell cell, int cellXSize, int cellYSize) {
		if(!grid.isSouthArrowhead(cell)) return null;
		DiagramShape shape = new DiagramShape();
		shape.addToPoints(new ShapePoint(
			Diagram.getCellMinX(cell,cellXSize),
			Diagram.getCellMinY(cell,cellYSize)));
		shape.addToPoints(new ShapePoint(
			Diagram.getCellMidX(cell,cellXSize),
			Diagram.getCellMaxY(cell,cellYSize)));
		shape.addToPoints(new ShapePoint(
			Diagram.getCellMaxX(cell,cellXSize),
			Diagram.getCellMinY(cell,cellYSize)));
		shape.setClosed(true);
		shape.setFillColor(Color.black);
		shape.setStrokeColor(Color.black);
		shape.setType(TYPE_ARROWHEAD);
		return shape;
	}

	private static DiagramShape createWestArrowhead(TextGrid grid, TextGrid.Cell cell, int cellXSize, int cellYSize) {
		if(!grid.isWestArrowhead(cell)) return null;
		DiagramShape shape = new DiagramShape();
		shape.addToPoints(new ShapePoint(
			Diagram.getCellMaxX(cell,cellXSize),
			Diagram.getCellMinY(cell,cellYSize)));
		shape.addToPoints(new ShapePoint(
			Diagram.getCellMinX(cell,cellXSize),
			Diagram.getCellMidY(cell,cellYSize)));
		shape.addToPoints(new ShapePoint(
			Diagram.getCellMaxX(cell,cellXSize),
			Diagram.getCellMaxY(cell,cellYSize)));
		shape.setClosed(true);
		shape.setFillColor(Color.black);
		shape.setStrokeColor(Color.black);
		shape.setType(TYPE_ARROWHEAD);
		return shape;
	}
	
	private static DiagramShape createEastArrowhead(TextGrid grid, TextGrid.Cell cell, int cellXSize, int cellYSize) {
		if(!grid.isEastArrowhead(cell)) return null;
		DiagramShape shape = new DiagramShape();
		shape.addToPoints(new ShapePoint(
			Diagram.getCellMinX(cell,cellXSize),
			Diagram.getCellMinY(cell,cellYSize)));
		shape.addToPoints(new ShapePoint(
			Diagram.getCellMaxX(cell,cellXSize),
			Diagram.getCellMidY(cell,cellYSize)));
		shape.addToPoints(new ShapePoint(
			Diagram.getCellMinX(cell,cellXSize),
			Diagram.getCellMaxY(cell,cellYSize)));
		shape.setClosed(true);
		shape.setFillColor(Color.black);
		shape.setStrokeColor(Color.black);
		shape.setType(TYPE_ARROWHEAD);
		return shape;
	}

	public static DiagramShape createSmallLine(TextGrid grid, TextGrid.Cell cell, int cellXSize, int cellYSize) {
		if (grid.isLine(cell)) {
			DiagramShape shape = new DiagramShape();
			if (grid.isHorizontalLine(cell)) {
				shape.addToPoints(
					new ShapePoint(
						cell.x * cellXSize,
						cell.y * cellYSize + cellYSize / 2));
				shape.addToPoints(
					new ShapePoint(
						cell.x * cellXSize + cellXSize - 1,
						cell.y * cellYSize + cellYSize / 2));
			} else if (grid.isVerticalLine(cell)) {
				shape.addToPoints(
					new ShapePoint(
						cell.x * cellXSize + cellXSize / 2,
						cell.y * cellYSize));
				shape.addToPoints(
					new ShapePoint(
						cell.x * cellXSize + cellXSize / 2,
						cell.y * cellYSize + cellYSize - 1));
			}
			
			//the -1 above, make a difference: the second point
			//should not fall into the next cell, because this
			//results in a failure of a proper end-of-line
			//plotting correction
			return shape;
		}
		return null;
	}

	public void addToPoints(ShapePoint point){
		points.add(point);
	}
	
	public Iterator getPointsIterator(){
		return points.iterator();
	}
	
	public void scale(float factor){
		Iterator it = getPointsIterator();
		while(it.hasNext()){
			ShapePoint point = (ShapePoint) it.next();
			point.x *= factor;
			point.y *= factor;
		}
	}
	
	public boolean isEmpty(){
		return points.isEmpty();
	}
	
	public boolean isFilled(){
		return (fillColor != null);
	}
	
	public void setIsNotFilled(){
		fillColor = null;
	}
	
	public boolean isPointLinesEnd(ShapePoint point){
		if(isClosed()) return false; //no line-ends in closed shapes!
		if(point == points.get(0)) return true;
		if(point == points.get(points.size() - 1)) return true;
		return false;
	}
	
	//TODO: method in development: isRectangle()
	public boolean isRectangle(){
		if(points.size() != 4) return false;
		ShapePoint p1 = (ShapePoint) points.get(0);
		ShapePoint p2 = (ShapePoint) points.get(1);
		ShapePoint p3 = (ShapePoint) points.get(2);
		ShapePoint p4 = (ShapePoint) points.get(3);
		if(p1.isInLineWith(p2) 
			&& p2.isInLineWith(p3)
			&& p3.isInLineWith(p4)
			&& p4.isInLineWith(p1)) return true;
		return false;
	}
	
	/**
	 * Crude way to determine which of the two shapes is smaller,
	 * based just on their bounding boxes. Used in markup
	 * assignment precendence.
	 * 
	 * @param other
	 * @return
	 */
	public boolean isSmallerThan(DiagramShape other){
		Rectangle bounds = getBounds();
		Rectangle otherBounds = other.getBounds();
		
		int area = bounds.height * bounds.width;
		int otherArea = otherBounds.height * otherBounds.width;
		
		if(area < otherArea) {
			return true;
		}
		return false;
	}
	
	/**
	 * @return
	 */
	public Color getFillColor() {
		return fillColor;
	}

	/**
	 * @return
	 */
	public Color getStrokeColor() {
		return strokeColor;
	}

	/**
	 * @param color
	 */
	public void setFillColor(Color color) {
		fillColor = color;
	}

	/**
	 * @param color
	 */
	public void setStrokeColor(Color color) {
		strokeColor = color;
	}

	/**
	 * @return
	 */
	public boolean isClosed() {
		return isClosed;
	}

	/**
	 * @param b
	 */
	public void setClosed(boolean b) {
		isClosed = b;
	}

	public void printDebug(){
		System.out.print("DiagramShape: ");
		System.out.println(points.size()+" points");
	}

	/**
	 * @return
	 */
	public ArrayList getPoints() {
		return points;
	}

	public ShapePoint getPoint(int i) {
		return (ShapePoint) points.get(i);
	}

	public void setPoint(int i, ShapePoint point) {
		points.set(i, point);
	}


	public boolean equals(Object object){
		DiagramShape shape = null;
		if(!(object instanceof DiagramShape)) { return false; }
		else shape = (DiagramShape) object;
		if(getPoints().size() != shape.getPoints().size()) return false;
		
		if(DEBUG) System.out.println("comparing shapes:");
		
		if(DEBUG) System.out.println("points1: ");
		HashMap points1 = new HashMap();
		Iterator it = getPointsIterator(); 
		while(it.hasNext()){
			ShapePoint point = (ShapePoint) it.next(); 
			points1.put( ""+((int) point.x)+","+((int) point.y), null);
			if(DEBUG) System.out.println(((int) point.x)+", "+((int) point.y));
		}
		
		if(DEBUG) System.out.println("points2: ");
		HashMap points2 = new HashMap();
		it = shape.getPointsIterator(); 
		while(it.hasNext()){
			ShapePoint point = (ShapePoint) it.next(); 
			points2.put( ""+((int) point.x)+","+((int) point.y), null);
			if(DEBUG) System.out.println(((int) point.x)+", "+((int) point.y));
		}
		
		it = points1.keySet().iterator();
		while(it.hasNext()){
			String key = (String) it.next();
			if(!points2.containsKey(key)) {
				if (DEBUG)
					System.out.println("\tare not equal");
				return false;
			} 
		}
		if (DEBUG)
			System.out.println("\tare equal");
		return true;
	}

	public GeneralPath makeIntoPath() {
		int size = getPoints().size();
		
		if(size < 2) return null;
		
		GeneralPath path = new GeneralPath();
		ShapePoint point = (ShapePoint) getPoints().get(0);
		path.moveTo((int) point.x, (int) point.y);
		for(int i = 1; i < size; i++){
			point = (ShapePoint) getPoints().get(i);
			path.lineTo((int) point.x, (int) point.y);
		}
		if(isClosed() && size > 2){
			path.closePath();
		}
		return path;
	}

	public GeneralPath makeMarkerPath(Diagram diagram){
		if(points.size() != 1) return null;
		ShapePoint center = (ShapePoint) this.getPoint(0);
		float diameter =
			(float) 0.7 * Math.min(diagram.getCellWidth(), diagram.getCellHeight());
		return new GeneralPath(new Ellipse2D.Float(
			center.x - diameter/2,
			center.y - diameter/2,
			diameter,
			diameter));
	}

	public Rectangle getBounds(){
		Rectangle bounds = makeIntoPath().getBounds();
		return bounds;
	}
	
	public GeneralPath makeIntoRenderPath(Diagram diagram) {
		int size = getPoints().size();
		
		if(getType() == TYPE_POINT_MARKER){
			return makeMarkerPath(diagram);
		}
		
		if(getType() == TYPE_DOCUMENT && points.size() == 4){
			return makeDocumentPath(diagram);
		}

		if(getType() == TYPE_STORAGE && points.size() == 4){
			return makeStoragePath(diagram);
		}

		if(getType() == TYPE_IO && points.size() == 4){
			return makeIOPath(diagram);
		}

		if(getType() == TYPE_DECISION && points.size() == 4){
			return makeDecisionPath(diagram);
		}

		if(getType() == TYPE_MANUAL_OPERATION && points.size() == 4){
			return makeTrapezoidPath(diagram, true);
		}

		if(getType() == TYPE_TRAPEZOID && points.size() == 4){
			return makeTrapezoidPath(diagram, false);
		}

		if(getType() == TYPE_ELLIPSE && points.size() == 4){
			return makeEllipsePath(diagram);
		}

		if(size < 2) return null;

		GeneralPath path = new GeneralPath();
		ShapePoint point = (ShapePoint) getPoints().get(0);
		TextGrid.Cell cell = diagram.getCellFor(point);
		//path.moveTo((int) point.x, (int) point.y);
		ShapePoint previous = (ShapePoint) getPoints().get(size - 1);
		ShapePoint next = (ShapePoint) getPoints().get(1);
		ShapePoint entryPoint;
		ShapePoint exitPoint;
		
		if(point.getType() == ShapePoint.TYPE_NORMAL){
			//if(isClosed()){
				path.moveTo((int) point.x, (int) point.y);
			/*} else {
				ShapePoint projectionPoint = getCellEdgeProjectionPointBetween(point, next, diagram);
				path.moveTo((int) projectionPoint.x, (int) projectionPoint.y);
			}*/
		} else if(point.getType() == ShapePoint.TYPE_ROUND){
			entryPoint = getCellEdgePointBetween(point, previous, diagram);
			exitPoint = getCellEdgePointBetween(point, next, diagram);
			path.moveTo(entryPoint.x, entryPoint.y);
			path.quadTo(point.x, point.y, exitPoint.x, exitPoint.y);			
		}

		for(int i = 1; i < size; i++){
			previous = point;
			point = (ShapePoint) getPoints().get(i);
			if(i < size - 1)
				next = (ShapePoint) getPoints().get(i + 1);
			else next = (ShapePoint) getPoints().get(0);

			cell = diagram.getCellFor(point);

			if(point.getType() == ShapePoint.TYPE_NORMAL)
				//if(!isPointLinesEnd(point))
					path.lineTo((int) point.x, (int) point.y);
				/*else { //it is line's end, so we plot it at the projected intersection of the line with the cell's edge
					ShapePoint projectionPoint = getCellEdgeProjectionPointBetween(point, previous, diagram);
					path.lineTo((int) projectionPoint.x, (int) projectionPoint.y);
				}*/
			else if(point.getType() == ShapePoint.TYPE_ROUND){
				entryPoint = getCellEdgePointBetween(point, previous, diagram);
				exitPoint = getCellEdgePointBetween(point, next, diagram);

				path.lineTo(entryPoint.x, entryPoint.y);
				path.quadTo(point.x, point.y, exitPoint.x, exitPoint.y);
				//if(!isPointLinesEnd(next)){
					if(next.getType() == ShapePoint.TYPE_NORMAL)
						path.lineTo(next.x, next.y);
					else if(next.getType() == ShapePoint.TYPE_ROUND){
						entryPoint = getCellEdgePointBetween(next, point, diagram);
						path.lineTo(entryPoint.x, entryPoint.y);					
					}
				/*} else {
					entryPoint = getCellEdgeProjectionPointBetween(next, point, diagram);
					path.lineTo(entryPoint.x, entryPoint.y);										
				}*/
			} 
		}
		//TODO: this shouldn't be needed, but it is!
		if(isClosed() && size > 2){
			path.closePath();
		}
		return path;
	}
	
	public ArrayList getEdges(){
		ArrayList edges = new ArrayList();
		if(this.points.size() == 1) return edges;
		int noOfPoints = points.size();
		for(int i = 0; i < noOfPoints - 1; i++){
			ShapePoint startPoint = (ShapePoint) points.get(i);
			ShapePoint endPoint = (ShapePoint) points.get(i + 1);
			ShapeEdge edge = new ShapeEdge(startPoint, endPoint, this);
			edges.add(edge);
		}
		//if it is closed return edge that connects the
		//last point to the first
		if(this.isClosed()){
			ShapePoint firstPoint = (ShapePoint) points.get(0);
			ShapePoint lastPoint = (ShapePoint) points.get(points.size() - 1);
			ShapeEdge edge = new ShapeEdge(lastPoint, firstPoint, this);
			edges.add(edge);
		}
		return edges;
	}

	/**
	 * Finds the point that represents the intersection between the cell edge
	 * that contains pointInCell and the line connecting pointInCell and
	 * otherPoint.
	 * 
  	 * Returns C, if A is point in cell and B is otherPoint:
	 * 
	 *     Cell
	 *    +-----+
	 *    |  A  |C                 B
	 *    |  *--*------------------*
	 *    |     |
	 *    +-----+
	 *
* * @param pointInCell * @param otherPoint * @return */ public ShapePoint getCellEdgePointBetween(ShapePoint pointInCell, ShapePoint otherPoint, Diagram diagram){ if(pointInCell == null || otherPoint == null || diagram == null) throw new IllegalArgumentException("None of the parameters can be null"); if(pointInCell.equals(otherPoint)) throw new IllegalArgumentException("The two points cannot be the same"); ShapePoint result = null; TextGrid.Cell cell = diagram.getCellFor(pointInCell); if(cell == null) throw new RuntimeException("Upexpected error, cannot find cell corresponding to point "+pointInCell+" for diagram "+diagram); if(otherPoint.isNorthOf(pointInCell)) result = new ShapePoint(pointInCell.x, diagram.getCellMinY(cell)); else if(otherPoint.isSouthOf(pointInCell)) result = new ShapePoint(pointInCell.x, diagram.getCellMaxY(cell)); else if(otherPoint.isWestOf(pointInCell)) result = new ShapePoint(diagram.getCellMinX(cell), pointInCell.y); else if(otherPoint.isEastOf(pointInCell)) result = new ShapePoint(diagram.getCellMaxX(cell), pointInCell.y); if(result == null) throw new RuntimeException("Upexpected error, cannot find cell edge point for points "+pointInCell+" and "+otherPoint+" for diagram "+diagram); return result; } /** * * Returns C, if A is point in cell and B is otherPoint: * *
	 *     Cell
	 *    +-----+
	 *    |  A  |                  B
	 *  C *--*--+------------------*
	 *    |     |
	 *    +-----+
	 * 
* * @param pointInCell * @param otherPoint * @param diagram * @return */ public ShapePoint getCellEdgeProjectionPointBetween(ShapePoint pointInCell, ShapePoint otherPoint, Diagram diagram){ if(pointInCell == null || otherPoint == null || diagram == null) throw new IllegalArgumentException("None of the parameters can be null"); if(pointInCell.equals(otherPoint)) throw new IllegalArgumentException("The two points cannot be the same: "+pointInCell+" and "+otherPoint+" passed"); ShapePoint result = null; TextGrid.Cell cell = diagram.getCellFor(pointInCell); if(cell == null) throw new RuntimeException("Upexpected error, cannot find cell corresponding to point "+pointInCell+" for diagram "+diagram); if(otherPoint.isNorthOf(pointInCell)) result = new ShapePoint(pointInCell.x, diagram.getCellMaxY(cell)); else if(otherPoint.isSouthOf(pointInCell)) result = new ShapePoint(pointInCell.x, diagram.getCellMinY(cell)); else if(otherPoint.isWestOf(pointInCell)) result = new ShapePoint(diagram.getCellMaxX(cell), pointInCell.y); else if(otherPoint.isEastOf(pointInCell)) result = new ShapePoint(diagram.getCellMinX(cell), pointInCell.y); if(result == null) throw new RuntimeException("Upexpected error, cannot find cell edge point for points "+pointInCell+" and "+otherPoint+" for diagram "+diagram); return result; } public boolean contains(ShapePoint point){ GeneralPath path = makeIntoPath(); if(path != null) return path.contains(point); return false; } public boolean contains(Rectangle2D rect){ GeneralPath path = makeIntoPath(); if(path != null) return path.contains(rect); return false; } public boolean intersects(Rectangle2D rect){ GeneralPath path = makeIntoPath(); if(path != null) return path.intersects(rect); return false; } public boolean dropsShadow(){ return (isClosed() && getType() != DiagramShape.TYPE_ARROWHEAD && getType() != DiagramShape.TYPE_POINT_MARKER && !isStrokeDashed()); } /** * @return */ public int getType() { return type; } /** * @param i */ public void setType(int i) { type = i; } public void moveEndsToCellEdges(TextGrid grid, Diagram diagram){ if(isClosed()) return; ShapePoint linesEnd = (ShapePoint) points.get(0); ShapePoint nextPoint = (ShapePoint) points.get(1); ShapePoint projectionPoint = getCellEdgeProjectionPointBetween(linesEnd, nextPoint, diagram); linesEnd.moveTo(projectionPoint); linesEnd = (ShapePoint) points.get(points.size() - 1); nextPoint = (ShapePoint) points.get(points.size() - 2); projectionPoint = getCellEdgeProjectionPointBetween(linesEnd, nextPoint, diagram); linesEnd.moveTo(projectionPoint); } public void connectEndsToAnchors(TextGrid grid, Diagram diagram){ if(isClosed()) return; ShapePoint linesEnd; ShapePoint nextPoint; linesEnd = (ShapePoint) points.get(0); nextPoint = (ShapePoint) points.get(1); connectEndToAnchors(grid, diagram, nextPoint, linesEnd); linesEnd = (ShapePoint) points.get(points.size() - 1); nextPoint = (ShapePoint) points.get(points.size() - 2); connectEndToAnchors(grid, diagram, nextPoint, linesEnd); } //TODO: improve connect Ends To Arrowheads to take direction into account private void connectEndToAnchors( TextGrid grid, Diagram diagram, ShapePoint nextPoint, ShapePoint linesEnd){ if(isClosed()) return; TextGrid.Cell anchorCell; anchorCell = getPossibleAnchorCell(linesEnd, nextPoint, diagram); if(grid.isArrowhead(anchorCell)){ linesEnd.x = diagram.getCellMidX(anchorCell); linesEnd.y = diagram.getCellMidY(anchorCell); linesEnd.setLocked(true); } else if (grid.isCorner(anchorCell) || grid.isIntersection(anchorCell)){ linesEnd.x = diagram.getCellMidX(anchorCell); linesEnd.y = diagram.getCellMidY(anchorCell); linesEnd.setLocked(true); } } /** * Given the end of a line, the next point and a Diagram, it * returns the cell that may contain intersections or arrowheads * to which the line's end should be connected * * @param linesEnd * @param nextPoint * @param diagram * @return */ private static TextGrid.Cell getPossibleAnchorCell( ShapePoint linesEnd, ShapePoint nextPoint, Diagram diagram ){ ShapePoint cellPoint = null; if(nextPoint.isNorthOf(linesEnd)) cellPoint = new ShapePoint(linesEnd.x, linesEnd.y + diagram.getCellHeight()); if(nextPoint.isSouthOf(linesEnd)) cellPoint = new ShapePoint(linesEnd.x, linesEnd.y - diagram.getCellHeight()); if(nextPoint.isWestOf(linesEnd)) cellPoint = new ShapePoint(linesEnd.x + diagram.getCellWidth(), linesEnd.y); if(nextPoint.isEastOf(linesEnd)) cellPoint = new ShapePoint(linesEnd.x - diagram.getCellWidth(), linesEnd.y); return diagram.getCellFor(cellPoint); } public String toString(){ String s = "DiagramShape, "+points.size()+" points: "; Iterator it = getPointsIterator(); while(it.hasNext()){ ShapePoint point = (ShapePoint) it.next(); s += point; if(it.hasNext()) s += " "; } return s; } /** * @return */ public boolean isStrokeDashed() { return isStrokeDashed; } /** * @param b */ public void setStrokeDashed(boolean b) { isStrokeDashed = b; } private GeneralPath makeStoragePath(Diagram diagram) { if(points.size() != 4) return null; Rectangle bounds = makeIntoPath().getBounds(); ShapePoint point1 = new ShapePoint((float)bounds.getMinX(), (float)bounds.getMinY()); ShapePoint point2 = new ShapePoint((float)bounds.getMaxX(), (float)bounds.getMinY()); ShapePoint point3 = new ShapePoint((float)bounds.getMaxX(), (float)bounds.getMaxY()); ShapePoint point4 = new ShapePoint((float)bounds.getMinX(), (float)bounds.getMaxY()); ShapePoint pointMidTop = new ShapePoint((float)bounds.getCenterX(), (float)bounds.getMinY()); ShapePoint pointMidBottom = new ShapePoint((float)bounds.getCenterX(), (float)bounds.getMaxY()); float diameterX = bounds.width; float diameterY = 0.75f * diagram.getCellHeight(); //control point offset X, and Y float cpOffsetX = bounds.width / 6; float cpOffsetYTop = diagram.getCellHeight() / 2; float cpOffsetYBottom = 10 * diagram.getCellHeight() / 14; //float cpOffsetYBottom = cpOffsetYTop; GeneralPath path = new GeneralPath(); //top of cylinder path.moveTo(point1.x, point1.y); path.curveTo( point1.x + cpOffsetX, point1.y + cpOffsetYTop, point2.x - cpOffsetX, point2.y + cpOffsetYTop, point2.x, point2.y ); path.curveTo( point2.x - cpOffsetX, point2.y - cpOffsetYTop, point1.x + cpOffsetX, point1.y - cpOffsetYTop, point1.x, point1.y ); //side of cylinder path.moveTo(point1.x, point1.y); path.lineTo(point4.x, point4.y); path.curveTo( point4.x + cpOffsetX, point4.y + cpOffsetYBottom, point3.x - cpOffsetX, point3.y + cpOffsetYBottom, point3.x, point3.y ); path.lineTo(point2.x, point2.y); return path; } private GeneralPath makeDocumentPath(Diagram diagram) { if(points.size() != 4) return null; Rectangle bounds = makeIntoPath().getBounds(); ShapePoint point1 = new ShapePoint((float)bounds.getMinX(), (float)bounds.getMinY()); ShapePoint point2 = new ShapePoint((float)bounds.getMaxX(), (float)bounds.getMinY()); ShapePoint point3 = new ShapePoint((float)bounds.getMaxX(), (float)bounds.getMaxY()); ShapePoint point4 = new ShapePoint((float)bounds.getMinX(), (float)bounds.getMaxY()); ShapePoint pointMid = new ShapePoint((float)bounds.getCenterX(), (float)bounds.getMaxY()); GeneralPath path = new GeneralPath(); path.moveTo(point1.x, point1.y); path.lineTo(point2.x, point2.y); path.lineTo(point3.x, point3.y); //int controlDX = diagram.getCellWidth(); //int controlDY = diagram.getCellHeight() / 2; int controlDX = bounds.width / 6; int controlDY = bounds.height / 8; path.quadTo(pointMid.x + controlDX, pointMid.y - controlDY, pointMid.x, pointMid.y); path.quadTo(pointMid.x - controlDX, pointMid.y + controlDY, point4.x, point4.y); path.closePath(); return path; } // to draw a circle with 4 Bezier curves, set the control points at this ratio of // the radius above & below the side points // thanks to G. Adam Stanislav, http://whizkidtech.redprince.net/bezier/circle/ private static final float KAPPA = 4f * ((float) Math.sqrt(2) - 1) / 3f; private GeneralPath makeEllipsePath(Diagram diagram) { if(points.size() != 4) return null; Rectangle bounds = makeIntoPath().getBounds(); float xOff = (float) bounds.getWidth() * 0.5f * KAPPA; float yOff = (float) bounds.getHeight() * 0.5f * KAPPA; ShapePoint pointMid = new ShapePoint((float)bounds.getCenterX(), (float)bounds.getCenterY()); ShapePoint left = new ShapePoint((float)bounds.getMinX(), (float)pointMid.getY()); ShapePoint right = new ShapePoint((float)bounds.getMaxX(), (float)pointMid.getY()); ShapePoint top = new ShapePoint((float)pointMid.getX(), (float)bounds.getMinY()); ShapePoint bottom = new ShapePoint((float)pointMid.getX(), (float)bounds.getMaxY()); GeneralPath path = new GeneralPath(); path.moveTo(top.x, top.y); path.curveTo(top.x + xOff, top.y, right.x, right.y - yOff, right.x, right.y); path.curveTo(right.x, right.y + yOff, bottom.x + xOff, bottom.y, bottom.x, bottom.y); path.curveTo(bottom.x - xOff, bottom.y, left.x, left.y + yOff, left.x, left.y); path.curveTo(left.x, left.y - yOff, top.x - xOff, top.y, top.x, top.y); path.closePath(); return path; } private GeneralPath makeTrapezoidPath(Diagram diagram, boolean inverted) { if(points.size() != 4) return null; Rectangle bounds = makeIntoPath().getBounds(); float offset = 0.7f * diagram.getCellWidth(); // fixed slope if (inverted) offset = -offset; ShapePoint ul = new ShapePoint((float)bounds.getMinX() + offset, (float)bounds.getMinY()); ShapePoint ur = new ShapePoint((float)bounds.getMaxX() - offset, (float)bounds.getMinY()); ShapePoint br = new ShapePoint((float)bounds.getMaxX() + offset, (float)bounds.getMaxY()); ShapePoint bl = new ShapePoint((float)bounds.getMinX() - offset, (float)bounds.getMaxY()); ShapePoint pointMid = new ShapePoint((float)bounds.getCenterX(), (float)bounds.getMaxY()); GeneralPath path = new GeneralPath(); path.moveTo(ul.x, ul.y); path.lineTo(ur.x, ur.y); path.lineTo(br.x, br.y); path.lineTo(bl.x, bl.y); path.closePath(); return path; } private GeneralPath makeDecisionPath(Diagram diagram) { if(points.size() != 4) return null; Rectangle bounds = makeIntoPath().getBounds(); ShapePoint pointMid = new ShapePoint((float)bounds.getCenterX(), (float)bounds.getCenterY()); ShapePoint left = new ShapePoint((float)bounds.getMinX(), (float)pointMid.getY()); ShapePoint right = new ShapePoint((float)bounds.getMaxX(), (float)pointMid.getY()); ShapePoint top = new ShapePoint((float)pointMid.getX(), (float)bounds.getMinY()); ShapePoint bottom = new ShapePoint((float)pointMid.getX(), (float)bounds.getMaxY()); GeneralPath path = new GeneralPath(); path.moveTo(left.x, left.y); path.lineTo(top.x, top.y); path.lineTo(right.x, right.y); path.lineTo(bottom.x, bottom.y); path.closePath(); return path; } private GeneralPath makeIOPath(Diagram diagram) { if(points.size() != 4) return null; Rectangle bounds = makeIntoPath().getBounds(); ShapePoint point1 = new ShapePoint((float)bounds.getMinX(), (float)bounds.getMinY()); ShapePoint point2 = new ShapePoint((float)bounds.getMaxX(), (float)bounds.getMinY()); ShapePoint point3 = new ShapePoint((float)bounds.getMaxX(), (float)bounds.getMaxY()); ShapePoint point4 = new ShapePoint((float)bounds.getMinX(), (float)bounds.getMaxY()); float offset = diagram.getCellWidth() / 2; GeneralPath path = new GeneralPath(); path.moveTo(point1.x + offset, point1.y); path.lineTo(point2.x + offset, point2.y); path.lineTo(point3.x - offset, point3.y); path.lineTo(point4.x - offset, point4.y); path.closePath(); return path; } public CustomShapeDefinition getDefinition() { return definition; } public void setDefinition(CustomShapeDefinition definition) { this.definition = definition; } } ditaa-0.9/src/org/stathissideris/ascii2image/graphics/ShapePoint.java0000644000175000017500000000622011277077356024121 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.graphics; import java.awt.geom.Point2D.Float; /** * * @author Efstathios Sideris */ public class ShapePoint extends java.awt.geom.Point2D.Float { public static final int TYPE_NORMAL = 0; public static final int TYPE_ROUND = 1; private boolean locked = false; private int type = 0; public ShapePoint() { super(); } public ShapePoint(float x, float y) { super(x, y); this.type = TYPE_NORMAL; } public ShapePoint(float x, float y, int type) { super(x, y); this.type = type; } public ShapePoint(ShapePoint other){ this(other.x, other.y, other.type); } /** * @return */ public int getType() { return type; } /** * @param i */ public void setType(int i) { type = i; } public boolean isInLineWith(ShapePoint point){ if(this.x == point.x) return true; if(this.y == point.y) return true; return false; } public boolean isWithinEdge(ShapeEdge edge) { if(edge.isHorizontal()) { if(x >= edge.getStartPoint().x && x <= edge.getEndPoint().x) return true; if(x >= edge.getEndPoint().x && x <= edge.getStartPoint().x) return true; return false; } else if(edge.isVertical()) { if(y >= edge.getStartPoint().y && y <= edge.getEndPoint().y) return true; if(y >= edge.getEndPoint().y && y <= edge.getStartPoint().y) return true; return false; } throw new RuntimeException("Cannot calculate is ShapePoint is within sloped edge"); } public boolean isNorthOf(ShapePoint point){ return (this.y < point.y); } public boolean isSouthOf(ShapePoint point){ return (this.y > point.y); } public boolean isWestOf(ShapePoint point){ return (this.x < point.x); } public boolean isEastOf(ShapePoint point){ return (this.x > point.x); } public String toString(){ return "("+x+", "+y+")"; } public void assign(ShapePoint point){ this.x = point.x; this.y = point.y; } /** * Does the same as assign, but respects the * locked attribute * * @param point */ public void moveTo(ShapePoint point){ if(locked) return; this.x = point.x; this.y = point.y; } /** * @return */ public boolean isLocked() { return locked; } /** * @param b */ public void setLocked(boolean b) { locked = b; } } ditaa-0.9/src/org/stathissideris/ascii2image/graphics/OffScreenSVGRenderer.java0000644000175000017500000001230511277077356025771 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.graphics; import java.awt.Color; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.StringReader; import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.GVTBuilder; import org.apache.batik.bridge.UserAgentAdapter; import org.apache.batik.dom.svg.SAXSVGDocumentFactory; import org.apache.batik.gvt.GraphicsNode; import org.apache.batik.gvt.renderer.ConcreteImageRendererFactory; import org.apache.batik.gvt.renderer.ImageRenderer; import org.apache.batik.gvt.renderer.ImageRendererFactory; import org.w3c.dom.NodeList; import org.w3c.dom.svg.SVGDocument; import org.w3c.dom.svg.SVGElement; public class OffScreenSVGRenderer { public BufferedImage renderXMLToImage(String xmlContent, int width, int height) throws IOException { return renderXMLToImage(xmlContent, width, height, false, null, null); } public BufferedImage renderXMLToImage(String xmlContent, int width, int height, boolean stretch, String idRegex, Color replacementColor) throws IOException { // the following is necessary so that batik knows how to resolve URI fragments // (#myLinearGradient). Otherwise the resolution fails and you cannot render. String uri = "file:/fake.svg"; SAXSVGDocumentFactory df = new SAXSVGDocumentFactory("org.apache.xerces.parsers.SAXParser"); SVGDocument document = df.createSVGDocument(uri, new StringReader(xmlContent)); if(idRegex != null && replacementColor != null) replaceFill(document, idRegex, replacementColor); return renderToImage(document, width, height, stretch); } public BufferedImage renderToImage(String uri, int width, int height) throws IOException { return renderToImage(uri, width, height, false, null, null); } public BufferedImage renderToImage(String uri, int width, int height, boolean stretch, String idRegex, Color replacementColor) throws IOException { SAXSVGDocumentFactory df = new SAXSVGDocumentFactory("org.apache.xerces.parsers.SAXParser"); SVGDocument document = df.createSVGDocument(uri); if(idRegex != null && replacementColor != null) replaceFill(document, idRegex, replacementColor); return renderToImage(document, width, height, stretch); } public BufferedImage renderToImage(SVGDocument document, int width, int height){ return renderToImage(document, width, height, false); } public void replaceFill(SVGDocument document, String idRegex, Color color){ String colorCode = String.format("#%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue()); System.out.println("color code: "+colorCode); NodeList children = document.getElementsByTagName("*"); for(int i = 0; i < children.getLength(); i++){ if(children.item(i) instanceof SVGElement){ SVGElement element = (SVGElement) children.item(i); if(element.getId().matches(idRegex)){ System.out.println("child>>> "+element+", "+element.getId()); String style = element.getAttributeNS(null, "style"); style = style.replaceFirst("fill:#[a-zA-z0-9]+", "fill:"+colorCode); System.out.println(style); element.setAttributeNS(null, "style", style); } } } } public BufferedImage renderToImage(SVGDocument document, int width, int height, boolean stretch){ ImageRendererFactory rendererFactory; rendererFactory = new ConcreteImageRendererFactory(); ImageRenderer renderer = rendererFactory.createStaticImageRenderer(); GVTBuilder builder = new GVTBuilder(); BridgeContext ctx = new BridgeContext(new UserAgentAdapter()); ctx.setDynamicState(BridgeContext.STATIC); GraphicsNode rootNode = builder.build(ctx, document); renderer.setTree(rootNode); float docWidth = (float) ctx.getDocumentSize().getWidth(); float docHeight = (float) ctx.getDocumentSize().getHeight(); float xscale = width/docWidth; float yscale = height/docHeight; if(!stretch){ float scale = Math.min(xscale, yscale); xscale = scale; yscale = scale; } AffineTransform px = AffineTransform.getScaleInstance(xscale, yscale); double tx = -0 + (width/xscale - docWidth)/2; double ty = -0 + (height/yscale - docHeight)/2; px.translate(tx, ty); //cgn.setViewingTransform(px); renderer.updateOffScreen(width, height); renderer.setTree(rootNode); renderer.setTransform(px); //renderer.clearOffScreen(); renderer.repaint(new Rectangle(0, 0, width, height)); return renderer.getOffScreen(); } } ditaa-0.9/src/org/stathissideris/ascii2image/graphics/ImageHandler.java0000644000175000017500000001107211277077356024370 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.graphics; import java.awt.Color; import java.awt.Image; import java.awt.MediaTracker; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.URI; import java.net.URL; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.GVTBuilder; import org.apache.batik.bridge.UserAgentAdapter; import org.apache.batik.dom.svg.SAXSVGDocumentFactory; import org.apache.batik.dom.util.DocumentFactory; import org.apache.batik.ext.awt.image.codec.PNGEncodeParam; import org.apache.batik.ext.awt.image.codec.PNGImageEncoder; import org.apache.batik.gvt.GraphicsNode; import org.apache.batik.gvt.renderer.ConcreteImageRendererFactory; import org.apache.batik.gvt.renderer.ImageRenderer; import org.apache.batik.gvt.renderer.ImageRendererFactory; import org.apache.batik.gvt.renderer.StaticRenderer; import org.stathissideris.ascii2image.core.FileUtils; import org.w3c.dom.Document; import org.w3c.dom.svg.SVGDocument; public class ImageHandler { private static OffScreenSVGRenderer svgRenderer = new OffScreenSVGRenderer(); private static ImageHandler instance = new ImageHandler(); public static ImageHandler instance(){ return instance; } private static final MediaTracker tracker = new MediaTracker(new JLabel()); public Image loadImage(String filename){ URL url = ClassLoader.getSystemResource(filename); Image result = null; if(url != null) result = Toolkit.getDefaultToolkit().getImage(url); else result = Toolkit.getDefaultToolkit().getImage(filename); // result = null; //wait for the image to load before returning tracker.addImage(result, 0); try { tracker.waitForID(0); } catch (InterruptedException e) { System.err.println("Failed to load image "+filename); e.printStackTrace(); } tracker.removeImage(result, 0); return result; } public BufferedImage renderSVG(String filename, int width, int height, boolean stretch) throws IOException { File file = new File(filename); URI uri = file.toURI(); return svgRenderer.renderToImage(uri.toString(), width, height, stretch, null, null); } public BufferedImage renderSVG(String filename, int width, int height, boolean stretch, String idRegex, Color color) throws IOException { File file = new File(filename); URI uri = file.toURI(); return svgRenderer.renderToImage(uri.toString(), width, height, stretch, idRegex, color); } public static void main(String[] args) throws IOException{ OffScreenSVGRenderer renderer = new OffScreenSVGRenderer(); //BufferedImage image = instance.renderSVG("sphere.svg", 200, 200, false); //BufferedImage image = renderer.renderToImage("file:///Users/sideris/Documents/workspace/ditaa/joystick.svg", FileUtils.readFile(new File("joystick.svg")), 400, 200, false); // BufferedImage image = renderer.renderToImage( // null, FileUtils.readFile(new File("sphere.svg")).replaceFirst("#187637", "#3333FF"), 200, 200, false); String content = FileUtils.readFile(new File("sphere.svg")).replaceAll("#187637", "#1133FF"); System.out.println(content); // BufferedImage image = renderer.renderToImage( // "file:/K:/devel/ditaa/sphere.svg", content, 200, 200, false); BufferedImage image = renderer.renderXMLToImage(content, 200, 200, false, null, null); try { File file = new File("testing.png"); ImageIO.write(image, "png", file); } catch (IOException e) { //e.printStackTrace(); System.err.println("Error: Cannot write to file"); } } } ditaa-0.9/src/org/stathissideris/ascii2image/graphics/CompositeDiagramShape.java0000644000175000017500000002253211277077356026263 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.graphics; import java.util.ArrayList; import java.util.List; import java.util.Iterator; import org.stathissideris.ascii2image.core.DebugUtils; import org.stathissideris.ascii2image.text.*; /** * * @author Efstathios Sideris */ public class CompositeDiagramShape extends DiagramComponent { private static final boolean DEBUG = false; private ArrayList shapes = new ArrayList(); public static void main(String[] args) { } public static DiagramComponent createFromBoundaryCells( final TextGrid grid, final CellSet boundaryCells, final int cellWidth, final int cellHeight) { return createOpenFromBoundaryCells( grid, boundaryCells, cellWidth, cellHeight, false); } public static DiagramComponent createOpenFromBoundaryCells( final TextGrid grid, final CellSet boundaryCells, final int cellWidth, final int cellHeight, boolean allRound) { if(boundaryCells.getType(grid) != CellSet.TYPE_OPEN) throw new IllegalArgumentException("This shape is closed and cannot be handled by this method"); if(boundaryCells.size() == 0) return null; CompositeDiagramShape compositeShape = new CompositeDiagramShape(); TextGrid workGrid = new TextGrid(grid.getWidth(), grid.getHeight()); grid.copyCellsTo(boundaryCells, workGrid); if(DEBUG) { System.out.println("Making composite shape from grid:"); workGrid.printDebug(); } CellSet visitedCells = new CellSet(); List shapes = new ArrayList(100); for(TextGrid.Cell cell : boundaryCells) { if(workGrid.isLinesEnd(cell)) { CellSet nextCells = workGrid.followCell(cell); shapes.addAll(growEdgesFromCell(workGrid, cellWidth, cellHeight, allRound, nextCells.getFirst(), cell, visitedCells)); break; } } //dashed shapes should "infect" the rest of the shapes boolean dashedShapeExists = false; for(DiagramShape shape : shapes) if(shape.isStrokeDashed()) dashedShapeExists = true; for(DiagramShape shape : shapes) { if(dashedShapeExists) shape.setStrokeDashed(true); compositeShape.addToShapes(shape); } return compositeShape; } private static List growEdgesFromCell( TextGrid workGrid, final int cellWidth, final int cellHeight, boolean allRound, TextGrid.Cell cell, TextGrid.Cell previousCell, CellSet visitedCells) { List result = new ArrayList(50); visitedCells.add(previousCell); DiagramShape shape = new DiagramShape(); shape.addToPoints(makePointForCell(previousCell, workGrid, cellWidth, cellHeight, allRound)); if(DEBUG) System.out.println("point at "+previousCell+" (call from line: "+DebugUtils.getLineNumber()+")"); if(workGrid.cellContainsDashedLineChar(previousCell)) shape.setStrokeDashed(true); boolean finished = false; while(!finished) { visitedCells.add(cell); if(workGrid.isPointCell(cell)) { if(DEBUG) System.out.println("point at "+cell+" (call from line: "+DebugUtils.getLineNumber()+")"); shape.addToPoints(makePointForCell(cell, workGrid, cellWidth, cellHeight, allRound)); } if(workGrid.cellContainsDashedLineChar(cell)) shape.setStrokeDashed(true); if(workGrid.isLinesEnd(cell)){ finished = true; if(DEBUG) System.out.println("finished shape"); } CellSet nextCells = workGrid.followCell(cell, previousCell); if(nextCells.size() == 1) { previousCell = cell; cell = (TextGrid.Cell) nextCells.getFirst(); } else if(nextCells.size() > 1) {//3- or 4- way intersection finished = true; if(DEBUG) System.out.println("finished shape"); for(TextGrid.Cell nextCell : nextCells) result.addAll(growEdgesFromCell(workGrid, cellWidth, cellHeight, allRound, nextCell, cell, visitedCells)); } } result.add(shape); return result; } /** * Returns a new diagram component with the lines of * this CompositeDiagramShape connected. It can a composite * or simple shape * * @return */ public DiagramComponent connectLines(){ CompositeDiagramShape result = new CompositeDiagramShape(); //find all lines ArrayList lines = new ArrayList(); Iterator it = shapes.iterator(); while(it.hasNext()){ DiagramShape shape = (DiagramShape) it.next(); if(shape.getPoints().size() == 2){ lines.add(shape); } } it = lines.iterator(); while(it.hasNext()){ DiagramShape line1 = (DiagramShape) it.next(); Iterator it2 = lines.iterator(); while(it2.hasNext()){ DiagramShape line2 = (DiagramShape) it.next(); ShapePoint commonPoint = null; ShapePoint line1UncommonPoint = null; ShapePoint line2UncommonPoint = null; if(line1.getPoint(0).equals(line2.getPoint(0))){ commonPoint = line1.getPoint(0); line1UncommonPoint = line1.getPoint(1); line2UncommonPoint = line2.getPoint(1); } if(line1.getPoint(0).equals(line2.getPoint(1))){ commonPoint = line1.getPoint(0); line1UncommonPoint = line1.getPoint(1); line2UncommonPoint = line2.getPoint(0); } if(line1.getPoint(1).equals(line2.getPoint(0))){ commonPoint = line1.getPoint(1); line1UncommonPoint = line1.getPoint(0); line2UncommonPoint = line2.getPoint(1); } if(line1.getPoint(1).equals(line2.getPoint(1))){ commonPoint = line1.getPoint(1); line1UncommonPoint = line1.getPoint(0); line2UncommonPoint = line2.getPoint(0); } if(commonPoint != null){ } } } return result; } public void connectEndsToAnchors(TextGrid grid, Diagram diagram){ Iterator it = shapes.iterator(); while (it.hasNext()) { DiagramShape shape = (DiagramShape) it.next(); if(!shape.isClosed()){ shape.connectEndsToAnchors(grid, diagram); } } } private static DiagramShape makeLine(TextGrid grid, TextGrid.Cell start, TextGrid.Cell end, int cellWidth, int cellHeight){ DiagramShape line = new DiagramShape(); if(grid.isHorizontalLine(start)){ if(start.isWestOf(end)){ line.addToPoints(new ShapePoint( Diagram.getCellMinX(start, cellWidth), Diagram.getCellMidY(start, cellHeight))); } else { line.addToPoints(new ShapePoint( Diagram.getCellMaxX(start, cellWidth), Diagram.getCellMidY(start, cellHeight))); } } else if(grid.isVerticalLine(start)){ if(start.isNorthOf(end)){ line.addToPoints(new ShapePoint( Diagram.getCellMidX(start, cellWidth), Diagram.getCellMinY(start, cellHeight))); } else { line.addToPoints(new ShapePoint( Diagram.getCellMidX(start, cellWidth), Diagram.getCellMaxY(start, cellHeight))); } } else { //corner if(DEBUG) System.out.println("Corner"); int type = (grid.isRoundCorner(start))?ShapePoint.TYPE_ROUND:ShapePoint.TYPE_NORMAL; line.addToPoints(new ShapePoint( Diagram.getCellMidX(start, cellWidth), Diagram.getCellMidY(start, cellHeight), type)); } if(grid.isHorizontalLine(end)){ if(start.isWestOf(start)){ line.addToPoints(new ShapePoint( Diagram.getCellMinX(end, cellWidth), Diagram.getCellMidY(end, cellHeight))); } else { line.addToPoints(new ShapePoint( Diagram.getCellMaxX(end, cellWidth), Diagram.getCellMidY(end, cellHeight))); } } else if(grid.isVerticalLine(end)){ if(start.isNorthOf(start)){ line.addToPoints(new ShapePoint( Diagram.getCellMidX(end, cellWidth), Diagram.getCellMinY(end, cellHeight))); } else { line.addToPoints(new ShapePoint( Diagram.getCellMidX(end, cellWidth), Diagram.getCellMaxY(end, cellHeight))); } } else { //corner int type = (grid.isRoundCorner(end))?ShapePoint.TYPE_ROUND:ShapePoint.TYPE_NORMAL; if(DEBUG) System.out.println("Corner"); line.addToPoints(new ShapePoint( Diagram.getCellMidX(end, cellWidth), Diagram.getCellMidY(end, cellHeight), type)); } return line; } public void addToShapes(DiagramShape shape){ shapes.add(shape); } private Iterator getShapesIterator(){ return shapes.iterator(); } public void scale(float factor){ Iterator it = getShapesIterator(); while(it.hasNext()){ DiagramShape shape = (DiagramShape) it.next(); shape.scale(factor); } } /** * @return */ public ArrayList getShapes() { return shapes; } } ditaa-0.9/src/org/stathissideris/ascii2image/graphics/ShapeEdge.java0000644000175000017500000001470711277077356023705 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.graphics; import java.awt.geom.GeneralPath; /** * * @author Efstathios Sideris */ public class ShapeEdge { private static final boolean DEBUG = false; private static final int TYPE_HORIZONTAL = 0; private static final int TYPE_VERTICAL = 1; private static final int TYPE_SLOPED = 2; private DiagramShape owner; private ShapePoint startPoint; private ShapePoint endPoint; public ShapeEdge(ShapePoint start, ShapePoint end, DiagramShape owner){ this.startPoint = start; this.endPoint = end; this.owner = owner; } public ShapeEdge(ShapeEdge other){ this( new ShapePoint(other.startPoint), new ShapePoint(other.endPoint), other.owner ); } private float getDistanceFromOrigin() { int type = this.getType(); if(type == TYPE_SLOPED) throw new RuntimeException("Cannot calculate distance of sloped edge from origin"); if(type == TYPE_HORIZONTAL) return startPoint.y; return startPoint.x; //vertical } //TODO: moveInwardsBy() not implemented public void moveInwardsBy(float offset){ int type = this.getType(); if(type == TYPE_SLOPED) throw new RuntimeException("Cannot move a sloped egde inwards: "+this); float xOffset = 0; float yOffset = 0; ShapePoint middle = getMiddle(); GeneralPath path = owner.makeIntoPath(); if(type == TYPE_HORIZONTAL){ xOffset = 0; ShapePoint up = new ShapePoint(middle.x, middle.y - 0.05f); ShapePoint down = new ShapePoint(middle.x, middle.y + 0.05f); if(path.contains(up)) yOffset = -offset; else if(path.contains(down)) yOffset = offset; } else if(type == TYPE_VERTICAL){ yOffset = 0; ShapePoint left = new ShapePoint(middle.x - 0.05f, middle.y); ShapePoint right = new ShapePoint(middle.x + 0.05f, middle.y); if(path.contains(left)) xOffset = -offset; else if(path.contains(right)) xOffset = offset; } if(DEBUG) System.out.println("Moved edge "+this+" by "+xOffset+", "+yOffset); translate(xOffset, yOffset); } public void translate(float dx, float dy){ startPoint.x += dx; startPoint.y += dy; endPoint.x += dx; endPoint.y += dy; } public ShapePoint getMiddle(){ return new ShapePoint( (startPoint.x + endPoint.x) / 2, (startPoint.y + endPoint.y) / 2 ); } /** * Returns the type of the edge * (TYPE_HORIZONTAL, TYPE_VERTICAL, TYPE_SLOPED). * * @return */ private int getType(){ if(isVertical()) return TYPE_VERTICAL; if(isHorizontal()) return TYPE_HORIZONTAL; return TYPE_SLOPED; } /** * @return */ public ShapePoint getEndPoint() { return endPoint; } /** * @return */ public ShapePoint getStartPoint() { return startPoint; } /** * @param point */ public void setEndPoint(ShapePoint point) { endPoint = point; } /** * @param point */ public void setStartPoint(ShapePoint point) { startPoint = point; } /** * @return */ public DiagramShape getOwner() { return owner; } /** * @param shape */ public void setOwner(DiagramShape shape) { owner = shape; } public boolean equals(Object object){ if(!(object instanceof ShapeEdge)) return false; ShapeEdge edge = (ShapeEdge) object; if(startPoint.equals(edge.getStartPoint()) && endPoint.equals(edge.getEndPoint())) return true; if(startPoint.equals(edge.getEndPoint()) && endPoint.equals(edge.getStartPoint())) return true; return false; } public boolean touchesWith(ShapeEdge other){ if(this.equals(other)) return true; if(this.isHorizontal() && other.isVertical()) return false; if(other.isHorizontal() && this.isVertical()) return false; if(this.getDistanceFromOrigin() != other.getDistanceFromOrigin()) return false; //covering this corner case (should produce false): // --------- // --------- ShapeEdge first = new ShapeEdge(this); ShapeEdge second = new ShapeEdge(other); if(first.isVertical()) { first.changeAxis(); second.changeAxis(); } first.fixDirection(); second.fixDirection(); if(first.startPoint.x > second.startPoint.x) { ShapeEdge temp = first; first = second; second = temp; } if(first.endPoint.equals(second.startPoint)) return false; // case 1: // ---------- // ----------- // case 2: // ------ // ----------------- if(this.startPoint.isWithinEdge(other) || this.endPoint.isWithinEdge(other)) return true; if(other.startPoint.isWithinEdge(this) || other.endPoint.isWithinEdge(this)) return true; return false; } private void changeAxis(){ ShapePoint temp = new ShapePoint(startPoint); startPoint = new ShapePoint(endPoint.y, endPoint.x); endPoint = new ShapePoint(temp.y, temp.x); } /** * if horizontal flips start and end points so that start is left of end * if verical flips start and end points so that start is over of end * */ private void fixDirection(){ if(isHorizontal()) { if(startPoint.x > endPoint.x) flipDirection(); } else if(isVertical()) { if(startPoint.y > endPoint.y) flipDirection(); } else { throw new RuntimeException("Cannot fix direction of sloped egde"); } } private void flipDirection(){ ShapePoint temp = startPoint; startPoint = endPoint; endPoint = temp; } public boolean isHorizontal(){ if(startPoint.y == endPoint.y) return true; return false; } public boolean isVertical(){ if(startPoint.x == endPoint.x) return true; return false; } public String toString(){ return startPoint+" -> "+endPoint; } } ditaa-0.9/src/org/stathissideris/ascii2image/graphics/Diagram.java0000644000175000017500000007475311303043072023405 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.graphics; import java.awt.Color; import java.awt.Font; import java.util.*; import javax.swing.plaf.SeparatorUI; import org.stathissideris.ascii2image.core.ConversionOptions; import org.stathissideris.ascii2image.core.Pair; import org.stathissideris.ascii2image.core.ProcessingOptions; import org.stathissideris.ascii2image.text.*; /** * * @author Efstathios Sideris */ public class Diagram { private static final boolean DEBUG = false; private static final boolean VERBOSE_DEBUG = false; private ArrayList shapes = new ArrayList(); private ArrayList compositeShapes = new ArrayList(); private ArrayList textObjects = new ArrayList(); private int width, height; private int cellWidth, cellHeight; /** * *

An outline of the inner workings of this very important (and monstrous) * constructor is presented here. Boundary processing is the first step * of the process:

* *
    *
  1. Copy the grid into a work grid and remove all type-on-line * and point markers from the work grid
  2. *
  3. Split grid into distinct shapes by plotting the grid * onto an AbstractionGrid and its getDistinctShapes() method.
  4. *
  5. Find all the possible boundary sets of each of the * distinct shapes. This can produce duplicate shapes (if the boundaries * are the same when filling from the inside and the outside).
  6. *
  7. Remove duplicate boundaries.
  8. *
  9. Remove obsolete boundaries. Obsolete boundaries are the ones that are * the sum of their parts when plotted as filled shapes. (see method * removeObsoleteShapes())
  10. *
  11. Seperate the found boundary sets to open, closed or mixed * (See CellSet class on how its done).
  12. *
  13. Are there any closed boundaries? *
      *
    • YES. Subtract all the closed boundaries from each of the * open ones. That should convert the mixed shapes into open.
    • *
    • NO. In this (harder) case, we use the method * breakTrulyMixedBoundaries() of CellSet to break boundaries * into open and closed shapes (would work in any case, but it's * probably slower than the other method). This method is based * on tracing from the lines' ends and splitting when we get to * an intersection.
    • *
    *
  14. *
  15. If we had to eliminate any mixed shapes, we seperate the found * boundary sets again to open, closed or mixed.
  16. *
* *

At this stage, the boundary processing is all complete and we * proceed with using those boundaries to create the shapes:

* *
    *
  1. Create closed shapes.
  2. *
  3. Create open shapes. That's when the line end corrections are * also applied, concerning the positioning of the ends of lines * see methods connectEndsToAnchors() and moveEndsToCellEdges() of * DiagramShape.
  4. *
  5. Assign color codes to closed shapes.
  6. *
  7. Assing extended markup tags to closed shapes.

    *
  8. Create arrowheads.

    *
  9. Create point markers.

    *
* *

Finally, the text processing occurs: [pending]

* * @param grid * @param cellWidth * @param cellHeight */ public Diagram(TextGrid grid, ConversionOptions options) { this.cellWidth = options.renderingOptions.getCellWidth(); this.cellHeight = options.renderingOptions.getCellHeight(); width = grid.getWidth() * cellWidth; height = grid.getHeight() * cellHeight; TextGrid workGrid = new TextGrid(grid); workGrid.replaceTypeOnLine(); workGrid.replacePointMarkersOnLine(); if(DEBUG) workGrid.printDebug(); int width = grid.getWidth(); int height = grid.getHeight(); //split distinct shapes using AbstractionGrid AbstractionGrid temp = new AbstractionGrid(workGrid, workGrid.getAllBoundaries()); ArrayList boundarySetsStep1 = temp.getDistinctShapes(); if(DEBUG){ System.out.println("******* Distinct shapes found using AbstractionGrid *******"); Iterator dit = boundarySetsStep1.iterator(); while (dit.hasNext()) { CellSet set = (CellSet) dit.next(); set.printAsGrid(); } System.out.println("******* Same set of shapes after processing them by filling *******"); } //Find all the boundaries by using the special version of the filling method //(fills in a different buffer than the buffer it reads from) ArrayList boundarySetsStep2 = new ArrayList(); Iterator boundarySetIt = boundarySetsStep1.iterator(); while (boundarySetIt.hasNext()) { CellSet set = (CellSet) boundarySetIt.next(); //the fill buffer keeps track of which cells have been //filled already TextGrid fillBuffer = new TextGrid(width * 3, height * 3); for(int yi = 0; yi < height * 3; yi++){ for(int xi = 0; xi < width * 3; xi++){ if(fillBuffer.isBlank(xi, yi)){ TextGrid copyGrid = new AbstractionGrid(workGrid, set).getCopyOfInternalBuffer(); CellSet boundaries = copyGrid .findBoundariesExpandingFrom(copyGrid.new Cell(xi, yi)); if(boundaries.size() == 0) continue; //i'm not sure why these occur boundarySetsStep2.add(boundaries.makeScaledOneThirdEquivalent()); copyGrid = new AbstractionGrid(workGrid, set).getCopyOfInternalBuffer(); CellSet filled = copyGrid .fillContinuousArea(copyGrid.new Cell(xi, yi), '*'); fillBuffer.fillCellsWith(filled, '*'); fillBuffer.fillCellsWith(boundaries, '-'); if(DEBUG){ //System.out.println("Fill buffer:"); //fillBuffer.printDebug(); boundaries.makeScaledOneThirdEquivalent().printAsGrid(); System.out.println("-----------------------------------"); } } } } } if (DEBUG) System.out.println("******* Removed duplicates *******"); boundarySetsStep2 = CellSet.removeDuplicateSets(boundarySetsStep2); if(DEBUG){ Iterator dit = boundarySetsStep2.iterator(); while (dit.hasNext()) { CellSet set = (CellSet) dit.next(); set.printAsGrid(); } } int originalSize = boundarySetsStep2.size(); boundarySetsStep2 = CellSet.removeDuplicateSets(boundarySetsStep2); if(DEBUG) { System.out.println( "******* Removed duplicates: there were " +originalSize +" shapes and now there are " +boundarySetsStep2.size()); } //split boundaries to open, closed and mixed if (DEBUG) System.out.println("******* First evaluation of openess *******"); ArrayList open = new ArrayList(); ArrayList closed = new ArrayList(); ArrayList mixed = new ArrayList(); Iterator sets = boundarySetsStep2.iterator(); while(sets.hasNext()){ CellSet set = (CellSet) sets.next(); int type = set.getType(workGrid); if(type == CellSet.TYPE_CLOSED) closed.add(set); else if(type == CellSet.TYPE_OPEN) open.add(set); else if(type == CellSet.TYPE_MIXED) mixed.add(set); if(DEBUG){ if(type == CellSet.TYPE_CLOSED) System.out.println("Closed boundaries:"); else if(type == CellSet.TYPE_OPEN) System.out.println("Open boundaries:"); else if(type == CellSet.TYPE_MIXED) System.out.println("Mixed boundaries:"); set.printAsGrid(); } } boolean hadToEliminateMixed = false; if(mixed.size() > 0 && closed.size() > 0) { // mixed shapes can be eliminated by // subtracting all the closed shapes from them if (DEBUG) System.out.println("******* Eliminating mixed shapes (basic algorithm) *******"); hadToEliminateMixed = true; //subtract from each of the mixed sets all the closed sets sets = mixed.iterator(); while(sets.hasNext()){ CellSet set = (CellSet) sets.next(); Iterator closedSets = closed.iterator(); while(closedSets.hasNext()){ CellSet closedSet = (CellSet) closedSets.next(); set.subtractSet(closedSet); } // this is necessary because some mixed sets produce // several distinct open sets after you subtract the // closed sets from them if(set.getType(workGrid) == CellSet.TYPE_OPEN) { boundarySetsStep2.remove(set); boundarySetsStep2.addAll(set.breakIntoDistinctBoundaries(workGrid)); } } } else if(mixed.size() > 0 && closed.size() == 0) { // no closed shape exists, will have to // handle mixed shape on its own // an example of this case is the following: // +-----+ // | A |C B // + ---+------------------- // | | // +-----+ hadToEliminateMixed = true; if (DEBUG) System.out.println("******* Eliminating mixed shapes (advanced algorithm for truly mixed shapes) *******"); sets = mixed.iterator(); while(sets.hasNext()){ CellSet set = (CellSet) sets.next(); boundarySetsStep2.remove(set); boundarySetsStep2.addAll(set.breakTrulyMixedBoundaries(workGrid)); } } else { if (DEBUG) System.out.println("No mixed shapes found. Skipped mixed shape elimination step"); } if(hadToEliminateMixed){ if (DEBUG) System.out.println("******* Second evaluation of openess *******"); //split boundaries again to open, closed and mixed open = new ArrayList(); closed = new ArrayList(); mixed = new ArrayList(); sets = boundarySetsStep2.iterator(); while(sets.hasNext()){ CellSet set = (CellSet) sets.next(); int type = set.getType(workGrid); if(type == CellSet.TYPE_CLOSED) closed.add(set); else if(type == CellSet.TYPE_OPEN) open.add(set); else if(type == CellSet.TYPE_MIXED) mixed.add(set); if(DEBUG){ if(type == CellSet.TYPE_CLOSED) System.out.println("Closed boundaries:"); else if(type == CellSet.TYPE_OPEN) System.out.println("Open boundaries:"); else if(type == CellSet.TYPE_MIXED) System.out.println("Mixed boundaries:"); set.printAsGrid(); } } } boolean removedAnyObsolete = removeObsoleteShapes(workGrid, closed); boolean allCornersRound = false; if(options.processingOptions.areAllCornersRound()) allCornersRound = true; //make shapes from the boundary sets //make closed shapes ArrayList closedShapes = new ArrayList(); sets = closed.iterator(); while(sets.hasNext()){ CellSet set = (CellSet) sets.next(); DiagramComponent shape = DiagramComponent.createClosedFromBoundaryCells(workGrid, set, cellWidth, cellHeight, allCornersRound); if(shape != null){ if(shape instanceof DiagramShape){ addToShapes((DiagramShape) shape); closedShapes.add(shape); } else if(shape instanceof CompositeDiagramShape) addToCompositeShapes((CompositeDiagramShape) shape); } } if(options.processingOptions.performSeparationOfCommonEdges()) separateCommonEdges(closedShapes); //make open shapes sets = open.iterator(); while(sets.hasNext()){ CellSet set = (CellSet) sets.next(); if(set.size() == 1){ //single cell "shape" TextGrid.Cell cell = (TextGrid.Cell) set.getFirst(); if(!grid.cellContainsDashedLineChar(cell)) { DiagramShape shape = DiagramShape.createSmallLine(workGrid, cell, cellWidth, cellHeight); if(shape != null) { addToShapes(shape); shape.connectEndsToAnchors(workGrid, this); } } } else { //normal shape DiagramComponent shape = CompositeDiagramShape .createOpenFromBoundaryCells( workGrid, set, cellWidth, cellHeight, allCornersRound); if(shape != null){ if(shape instanceof CompositeDiagramShape){ addToCompositeShapes((CompositeDiagramShape) shape); ((CompositeDiagramShape) shape).connectEndsToAnchors(workGrid, this); } else if(shape instanceof DiagramShape) { addToShapes((DiagramShape) shape); ((DiagramShape) shape).connectEndsToAnchors(workGrid, this); ((DiagramShape) shape).moveEndsToCellEdges(grid, this); } } } } //assign color codes to shapes //TODO: text on line should not change its color //TODO: each color tag should be assigned to the smallest containing shape (like shape tags) Iterator cellColorPairs = grid.findColorCodes().iterator(); while(cellColorPairs.hasNext()){ TextGrid.CellColorPair pair = (TextGrid.CellColorPair) cellColorPairs.next(); ShapePoint point = new ShapePoint(getCellMidX(pair.cell), getCellMidY(pair.cell)); Iterator shapes = getShapes().iterator(); while(shapes.hasNext()){ DiagramShape shape = (DiagramShape) shapes.next(); if(shape.contains(point)) shape.setFillColor(pair.color); } } //assign markup to shapes Iterator cellTagPairs = grid.findMarkupTags().iterator(); while(cellTagPairs.hasNext()){ TextGrid.CellTagPair pair = (TextGrid.CellTagPair) cellTagPairs.next(); ShapePoint point = new ShapePoint(getCellMidX(pair.cell), getCellMidY(pair.cell)); //find the smallest shape that contains the tag DiagramShape containingShape = null; Iterator shapes = getShapes().iterator(); while(shapes.hasNext()){ DiagramShape shape = (DiagramShape) shapes.next(); if(shape.contains(point)){ if(containingShape == null){ containingShape = shape; } else { if(shape.isSmallerThan(containingShape)){ containingShape = shape; } } } } //this tag is not within a shape, skip if(containingShape == null) continue; //TODO: the code below could be a lot more concise if(pair.tag.equals("d")){ CustomShapeDefinition def = options.processingOptions.getFromCustomShapes("d"); if(def == null) containingShape.setType(DiagramShape.TYPE_DOCUMENT); else { containingShape.setType(DiagramShape.TYPE_CUSTOM); containingShape.setDefinition(def); } } else if(pair.tag.equals("s")){ CustomShapeDefinition def = options.processingOptions.getFromCustomShapes("s"); if(def == null) containingShape.setType(DiagramShape.TYPE_STORAGE); else { containingShape.setType(DiagramShape.TYPE_CUSTOM); containingShape.setDefinition(def); } } else if(pair.tag.equals("io")){ CustomShapeDefinition def = options.processingOptions.getFromCustomShapes("io"); if(def == null) containingShape.setType(DiagramShape.TYPE_IO); else { containingShape.setType(DiagramShape.TYPE_CUSTOM); containingShape.setDefinition(def); } } else if(pair.tag.equals("c")){ CustomShapeDefinition def = options.processingOptions.getFromCustomShapes("c"); if(def == null) containingShape.setType(DiagramShape.TYPE_DECISION); else { containingShape.setType(DiagramShape.TYPE_CUSTOM); containingShape.setDefinition(def); } } else if(pair.tag.equals("mo")){ CustomShapeDefinition def = options.processingOptions.getFromCustomShapes("mo"); if(def == null) containingShape.setType(DiagramShape.TYPE_MANUAL_OPERATION); else { containingShape.setType(DiagramShape.TYPE_CUSTOM); containingShape.setDefinition(def); } } else if(pair.tag.equals("tr")){ CustomShapeDefinition def = options.processingOptions.getFromCustomShapes("tr"); if(def == null) containingShape.setType(DiagramShape.TYPE_TRAPEZOID); else { containingShape.setType(DiagramShape.TYPE_CUSTOM); containingShape.setDefinition(def); } } else if(pair.tag.equals("o")){ CustomShapeDefinition def = options.processingOptions.getFromCustomShapes("o"); if(def == null) containingShape.setType(DiagramShape.TYPE_ELLIPSE); else { containingShape.setType(DiagramShape.TYPE_CUSTOM); containingShape.setDefinition(def); } } else { CustomShapeDefinition def = options.processingOptions.getFromCustomShapes(pair.tag); containingShape.setType(DiagramShape.TYPE_CUSTOM); containingShape.setDefinition(def); } } //make arrowheads Iterator arrowheadCells = workGrid.findArrowheads().iterator(); while(arrowheadCells.hasNext()){ TextGrid.Cell cell = (TextGrid.Cell) arrowheadCells.next(); DiagramShape arrowhead = DiagramShape.createArrowhead(workGrid, cell, cellWidth, cellHeight); if(arrowhead != null) addToShapes(arrowhead); else System.err.println("Could not create arrowhead shape. Unexpected error."); } //make point markers Iterator markersIt = grid.getPointMarkersOnLine().iterator(); while (markersIt.hasNext()) { TextGrid.Cell cell = (TextGrid.Cell) markersIt.next(); DiagramShape mark = new DiagramShape(); mark.addToPoints(new ShapePoint( getCellMidX(cell), getCellMidY(cell) )); mark.setType(DiagramShape.TYPE_POINT_MARKER); mark.setFillColor(Color.white); shapes.add(mark); } removeDuplicateShapes(); if(DEBUG) System.out.println("Shape count: "+shapes.size()); if(DEBUG) System.out.println("Composite shape count: "+compositeShapes.size()); //copy again workGrid = new TextGrid(grid); workGrid.removeNonText(); // ****** handle text ******* //break up text into groups TextGrid textGroupGrid = new TextGrid(workGrid); CellSet gaps = textGroupGrid.getAllBlanksBetweenCharacters(); //kludge textGroupGrid.fillCellsWith(gaps, '|'); CellSet nonBlank = textGroupGrid.getAllNonBlank(); ArrayList textGroups = nonBlank.breakIntoDistinctBoundaries(); if(DEBUG) System.out.println(textGroups.size()+" text groups found"); Font font = FontMeasurer.instance().getFontFor(cellHeight); Iterator textGroupIt = textGroups.iterator(); while(textGroupIt.hasNext()){ CellSet textGroupCellSet = (CellSet) textGroupIt.next(); TextGrid isolationGrid = new TextGrid(width, height); workGrid.copyCellsTo(textGroupCellSet, isolationGrid); ArrayList strings = isolationGrid.findStrings(); Iterator it = strings.iterator(); while(it.hasNext()){ TextGrid.CellStringPair pair = (TextGrid.CellStringPair) it.next(); TextGrid.Cell cell = pair.cell; String string = pair.string; if (DEBUG) System.out.println("Found string "+string); TextGrid.Cell lastCell = isolationGrid.new Cell(cell.x + string.length() - 1, cell.y); int minX = getCellMinX(cell); int y = getCellMaxY(cell); int maxX = getCellMaxX(lastCell); DiagramText textObject; if(FontMeasurer.instance().getWidthFor(string, font) > maxX - minX){ //does not fit horizontally Font lessWideFont = FontMeasurer.instance().getFontFor(maxX - minX, string); textObject = new DiagramText(minX, y, string, lessWideFont); } else textObject = new DiagramText(minX, y, string, font); textObject.centerVerticallyBetween(getCellMinY(cell), getCellMaxY(cell)); //TODO: if the strings start with bullets they should be aligned to the left //position text correctly int otherStart = isolationGrid.otherStringsStartInTheSameColumn(cell); int otherEnd = isolationGrid.otherStringsEndInTheSameColumn(lastCell); if(0 == otherStart && 0 == otherEnd) { textObject.centerHorizontallyBetween(minX, maxX); } else if(otherEnd > 0 && otherStart == 0) { textObject.alignRightEdgeTo(maxX); } else if(otherEnd > 0 && otherStart > 0){ if(otherEnd > otherStart){ textObject.alignRightEdgeTo(maxX); } else if(otherEnd == otherStart){ textObject.centerHorizontallyBetween(minX, maxX); } } addToTextObjects(textObject); } } if (DEBUG) System.out.println("Positioned text"); //correct the color of the text objects according //to the underlying color Iterator shapes = this.getAllDiagramShapes().iterator(); while(shapes.hasNext()){ DiagramShape shape = (DiagramShape) shapes.next(); Color fillColor = shape.getFillColor(); if(shape.isClosed() && shape.getType() != DiagramShape.TYPE_ARROWHEAD && fillColor != null && BitmapRenderer.isColorDark(fillColor)){ Iterator textObjects = getTextObjects().iterator(); while(textObjects.hasNext()){ DiagramText textObject = (DiagramText) textObjects.next(); if(shape.intersects(textObject.getBounds())){ textObject.setColor(Color.white); } } } } //set outline to true for test within custom shapes shapes = this.getAllDiagramShapes().iterator(); while(shapes.hasNext()){ DiagramShape shape = (DiagramShape) shapes.next(); if(shape.getType() == DiagramShape.TYPE_CUSTOM){ Iterator textObjects = getTextObjects().iterator(); while(textObjects.hasNext()){ DiagramText textObject = (DiagramText) textObjects.next(); textObject.setHasOutline(true); textObject.setColor(DiagramText.DEFAULT_COLOR); } } } if (DEBUG) System.out.println("Corrected color of text according to underlying color"); } /** * Returns a list of all DiagramShapes in the Diagram, including * the ones within CompositeDiagramShapes * * @return */ public ArrayList getAllDiagramShapes(){ ArrayList shapes = new ArrayList(); shapes.addAll(this.getShapes()); Iterator shapesIt = this.getCompositeShapes().iterator(); while(shapesIt.hasNext()){ CompositeDiagramShape compShape = (CompositeDiagramShape) shapesIt.next(); shapes.addAll(compShape.getShapes()); } return shapes; } /** * Removes the sets from setsthat are the sum of their parts * when plotted as filled shapes. * * @return true if it removed any obsolete. * */ private boolean removeObsoleteShapes(TextGrid grid, ArrayList sets){ if (DEBUG) System.out.println("******* Removing obsolete shapes *******"); boolean removedAny = false; ArrayList filledSets = new ArrayList(); Iterator it; if(VERBOSE_DEBUG) { System.out.println("******* Sets before *******"); it = sets.iterator(); while(it.hasNext()){ CellSet set = (CellSet) it.next(); set.printAsGrid(); } } //make filled versions of all the boundary sets it = sets.iterator(); while(it.hasNext()){ CellSet set = (CellSet) it.next(); set = set.getFilledEquivalent(grid); if(set == null){ return false; } else filledSets.add(set); } ArrayList toBeRemovedIndices = new ArrayList(); it = filledSets.iterator(); while(it.hasNext()){ CellSet set = (CellSet) it.next(); if(VERBOSE_DEBUG){ System.out.println("*** Deciding if the following should be removed:"); set.printAsGrid(); } //find the other sets that have common cells with set ArrayList common = new ArrayList(); common.add(set); Iterator it2 = filledSets.iterator(); while(it2.hasNext()){ CellSet set2 = (CellSet) it2.next(); if(set != set2 && set.hasCommonCells(set2)){ common.add(set2); } } //it only makes sense for more than 2 sets if(common.size() == 2) continue; //find largest set CellSet largest = set; it2 = common.iterator(); while(it2.hasNext()){ CellSet set2 = (CellSet) it2.next(); if(set2.size() > largest.size()){ largest = set2; } } if(VERBOSE_DEBUG){ System.out.println("Largest:"); largest.printAsGrid(); } //see if largest is sum of others common.remove(largest); //make the sum set of the small sets on a grid TextGrid gridOfSmalls = new TextGrid(largest.getMaxX() + 2, largest.getMaxY() + 2); CellSet sumOfSmall = new CellSet(); it2 = common.iterator(); while(it2.hasNext()){ CellSet set2 = (CellSet) it2.next(); if(VERBOSE_DEBUG){ System.out.println("One of smalls:"); set2.printAsGrid(); } gridOfSmalls.fillCellsWith(set2, '*'); } if(VERBOSE_DEBUG){ System.out.println("Sum of smalls:"); gridOfSmalls.printDebug(); } TextGrid gridLargest = new TextGrid(largest.getMaxX() + 2, largest.getMaxY() + 2); gridLargest.fillCellsWith(largest, '*'); int index = filledSets.indexOf(largest); if(gridLargest.equals(gridOfSmalls) && !toBeRemovedIndices.contains(new Integer(index))) { toBeRemovedIndices.add(new Integer(index)); if (DEBUG){ System.out.println("Decided to remove set:"); largest.printAsGrid(); } } else if (DEBUG){ System.out.println("This set WILL NOT be removed:"); largest.printAsGrid(); } //if(gridLargest.equals(gridOfSmalls)) toBeRemovedIndices.add(new Integer(index)); } ArrayList setsToBeRemoved = new ArrayList(); it = toBeRemovedIndices.iterator(); while(it.hasNext()){ int i = ((Integer) it.next()).intValue(); setsToBeRemoved.add(sets.get(i)); } it = setsToBeRemoved.iterator(); while(it.hasNext()){ CellSet set = (CellSet) it.next(); removedAny = true; sets.remove(set); } if(VERBOSE_DEBUG) { System.out.println("******* Sets after *******"); it = sets.iterator(); while(it.hasNext()){ CellSet set = (CellSet) it.next(); set.printAsGrid(); } } return removedAny; } public float getMinimumOfCellDimension(){ return Math.min(getCellWidth(), getCellHeight()); } private void separateCommonEdges(ArrayList shapes){ float offset = getMinimumOfCellDimension() / 5; ArrayList edges = new ArrayList(); //get all adges Iterator it = shapes.iterator(); while (it.hasNext()) { DiagramShape shape = (DiagramShape) it.next(); edges.addAll(shape.getEdges()); } //group edges into pairs of touching edges ArrayList> listOfPairs = new ArrayList>(); it = edges.iterator(); //all-against-all touching test for the edges int startIndex = 1; //skip some to avoid duplicate comparisons and self-to-self comparisons while(it.hasNext()){ ShapeEdge edge1 = (ShapeEdge) it.next(); for(int k = startIndex; k < edges.size(); k++) { ShapeEdge edge2 = edges.get(k); if(edge1.touchesWith(edge2)) { listOfPairs.add(new Pair(edge1, edge2)); } } startIndex++; } ArrayList movedEdges = new ArrayList(); //move equivalent edges inwards it = listOfPairs.iterator(); while(it.hasNext()){ Pair pair = (Pair) it.next(); if(!movedEdges.contains(pair.first)) { pair.first.moveInwardsBy(offset); movedEdges.add(pair.first); } if(!movedEdges.contains(pair.second)) { pair.second.moveInwardsBy(offset); movedEdges.add(pair.second); } } } //TODO: removes more than it should private void removeDuplicateShapes() { ArrayList originalShapes = new ArrayList(); Iterator shapesIt = getShapesIterator(); while(shapesIt.hasNext()){ DiagramShape shape = (DiagramShape) shapesIt.next(); boolean isOriginal = true; Iterator originals = originalShapes.iterator(); while(originals.hasNext()){ DiagramShape originalShape = (DiagramShape) originals.next(); if(shape.equals(originalShape)){ isOriginal = false; } } if(isOriginal) originalShapes.add(shape); } shapes.clear(); shapes.addAll(originalShapes); } private void addToTextObjects(DiagramText shape){ textObjects.add(shape); } private void addToCompositeShapes(CompositeDiagramShape shape){ compositeShapes.add(shape); } private void addToShapes(DiagramShape shape){ shapes.add(shape); } public Iterator getShapesIterator(){ return shapes.iterator(); } /** * @return */ public int getHeight() { return height; } /** * @return */ public int getWidth() { return width; } /** * @return */ public int getCellWidth() { return cellWidth; } /** * @return */ public int getCellHeight() { return cellHeight; } /** * @return */ public ArrayList getCompositeShapes() { return compositeShapes; } /** * @return */ public ArrayList getShapes() { return shapes; } public int getCellMinX(TextGrid.Cell cell){ return getCellMinX(cell, cellWidth); } public static int getCellMinX(TextGrid.Cell cell, int cellXSize){ return cell.x * cellXSize; } public int getCellMidX(TextGrid.Cell cell){ return getCellMidX(cell, cellWidth); } public static int getCellMidX(TextGrid.Cell cell, int cellXSize){ return cell.x * cellXSize + cellXSize / 2; } public int getCellMaxX(TextGrid.Cell cell){ return getCellMaxX(cell, cellWidth); } public static int getCellMaxX(TextGrid.Cell cell, int cellXSize){ return cell.x * cellXSize + cellXSize; } public int getCellMinY(TextGrid.Cell cell){ return getCellMinY(cell, cellHeight); } public static int getCellMinY(TextGrid.Cell cell, int cellYSize){ return cell.y * cellYSize; } public int getCellMidY(TextGrid.Cell cell){ return getCellMidY(cell, cellHeight); } public static int getCellMidY(TextGrid.Cell cell, int cellYSize){ return cell.y * cellYSize + cellYSize / 2; } public int getCellMaxY(TextGrid.Cell cell){ return getCellMaxY(cell, cellHeight); } public static int getCellMaxY(TextGrid.Cell cell, int cellYSize){ return cell.y * cellYSize + cellYSize; } public TextGrid.Cell getCellFor(ShapePoint point){ if(point == null) throw new IllegalArgumentException("ShapePoint cannot be null"); //TODO: the fake grid is a problem TextGrid g = new TextGrid(); return g.new Cell((int) point.x / cellWidth, (int) point.y / cellHeight); } /** * @return */ public ArrayList getTextObjects() { return textObjects; } } ditaa-0.9/src/org/stathissideris/ascii2image/graphics/FontMeasurer.java0000644000175000017500000001432611277077356024467 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.graphics; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.GraphicsEnvironment; import java.awt.Rectangle; import java.awt.font.FontRenderContext; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.Locale; import javax.swing.JOptionPane; /** * * @author Efstathios Sideris */ public class FontMeasurer { private static final String fontFamilyName = "Dialog"; //private static final String fontFamilyName = "Helvetica"; private static final boolean DEBUG = false; private static final FontMeasurer instance = new FontMeasurer(); FontRenderContext fakeRenderContext; Graphics2D fakeGraphics; { BufferedImage image = new BufferedImage(1,1, BufferedImage.TYPE_INT_RGB); fakeGraphics = image.createGraphics(); System.out.println("Locale: "+Locale.getDefault()); fakeRenderContext = fakeGraphics.getFontRenderContext(); } public int getWidthFor(String str, int pixelHeight){ Font font = getFontFor(pixelHeight); Rectangle2D rectangle = font.getStringBounds(str, fakeRenderContext); return (int) rectangle.getWidth(); } public int getHeightFor(String str, int pixelHeight){ Font font = getFontFor(pixelHeight); Rectangle2D rectangle = font.getStringBounds(str, fakeRenderContext); return (int) rectangle.getHeight(); } public int getWidthFor(String str, Font font){ Rectangle2D rectangle = font.getStringBounds(str, fakeRenderContext); return (int) rectangle.getWidth(); } public int getHeightFor(String str, Font font){ Rectangle2D rectangle = font.getStringBounds(str, fakeRenderContext); return (int) rectangle.getHeight(); } public Rectangle2D getBoundsFor(String str, Font font){ return font.getStringBounds(str, fakeRenderContext); } public Font getFontFor(int pixelHeight){ BufferedImage image = new BufferedImage(1,1, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); return getFontFor(pixelHeight, fakeRenderContext); } public int getAscent(Font font){ fakeGraphics.setFont(font); FontMetrics metrics = fakeGraphics.getFontMetrics(); if(DEBUG) System.out.println("Ascent: "+metrics.getAscent()); return metrics.getAscent(); } public int getZHeight(Font font){ int height = (int) font.createGlyphVector(fakeRenderContext, "Z").getOutline().getBounds().getHeight(); if(DEBUG) System.out.println("Z height: "+height); return height; } public Font getFontFor(int maxWidth, String string){ float size = 12; Font currentFont = new Font(fontFamilyName, Font.BOLD, (int) size); //ascent is the distance between the baseline and the tallest character int width = getWidthFor(string, currentFont); int direction; //direction of size change (towards smaller or bigger) if(width > maxWidth){ currentFont = currentFont.deriveFont(size - 1); size--; direction = -1; } else { currentFont = currentFont.deriveFont(size + 1); size++; direction = 1; } while(size > 0){ currentFont = currentFont.deriveFont(size); //rectangle = currentFont.getStringBounds(testString, frc); width = getWidthFor(string, currentFont); if(direction == 1){ if(width > maxWidth){ size = size - 1; return currentFont.deriveFont(size); } else size = size + 1; } else { if(width < maxWidth) return currentFont; else size = size - 1; } } return null; } public Font getFontFor(int pixelHeight, FontRenderContext frc){ float size = 12; Font currentFont = new Font(fontFamilyName, Font.BOLD, (int) size); // Font currentFont = new Font("Times", Font.BOLD, (int) size); System.out.println(currentFont.getFontName()); //ascent is the distance between the baseline and the tallest character int ascent = getAscent(currentFont); int direction; //direction of size change (towards smaller or bigger) if(ascent > pixelHeight){ currentFont = currentFont.deriveFont(size - 1); size--; direction = -1; } else { currentFont = currentFont.deriveFont(size + 1); size++; direction = 1; } while(size > 0){ currentFont = currentFont.deriveFont(size); //rectangle = currentFont.getStringBounds(testString, frc); ascent = getAscent(currentFont); if(direction == 1){ if(ascent > pixelHeight){ size = size - 0.5f; return currentFont.deriveFont(size); } else size = size + 0.5f; } else { if(ascent < pixelHeight) return currentFont; else size = size - 0.5f; } } return null; } public static FontMeasurer instance(){ return instance; } public FontMeasurer(){ } public static void main(String[] args) { //FontMeasurer.instance().getFontFor(7); float size = 12; Font currentFont = new Font("Sans", Font.BOLD, (int) size); System.out.println(currentFont.getSize()); currentFont = currentFont.deriveFont(--size); System.out.println(currentFont.getSize()); currentFont = currentFont.deriveFont(--size); System.out.println(currentFont.getSize()); currentFont = currentFont.deriveFont(--size); System.out.println(currentFont.getSize()); currentFont = currentFont.deriveFont(--size); System.out.println(currentFont.getSize()); currentFont = currentFont.deriveFont(--size); System.out.println(currentFont.getSize()); } } ditaa-0.9/src/org/stathissideris/ascii2image/graphics/DiagramText.java0000644000175000017500000001017711277077356024266 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.graphics; import java.awt.Color; import java.awt.Font; import java.awt.Rectangle; import java.awt.geom.Rectangle2D; /** * * @author Efstathios Sideris */ public class DiagramText extends DiagramComponent { public static final Color DEFAULT_COLOR = Color.black; private String text; private Font font; private int xPos, yPos; private Color color = Color.black; private boolean isTextOnLine = false; private boolean hasOutline = false; private Color outlineColor = Color.white; public DiagramText(int x, int y, String text, Font font){ if(text == null) throw new IllegalArgumentException("DiagramText cannot be initialised with a null string"); if(font == null) throw new IllegalArgumentException("DiagramText cannot be initialised with a null font"); this.xPos = x; this.yPos = y; this.text = text; this.font = font; } public void centerInBounds(Rectangle2D bounds){ centerHorizontallyBetween((int) bounds.getMinX(), (int) bounds.getMaxX()); centerVerticallyBetween((int) bounds.getMinY(), (int) bounds.getMaxY()); } public void centerHorizontallyBetween(int minX, int maxX){ int width = FontMeasurer.instance().getWidthFor(text, font); int center = Math.abs(maxX - minX) / 2; xPos += Math.abs(center - width / 2); } public void centerVerticallyBetween(int minY, int maxY){ int zHeight = FontMeasurer.instance().getZHeight(font); int center = Math.abs(maxY - minY) / 2; yPos -= Math.abs(center - zHeight / 2); } public void alignRightEdgeTo(int x){ int width = FontMeasurer.instance().getWidthFor(text, font); xPos = x - width; } /** * @return */ public Color getColor() { return color; } /** * @return */ public Font getFont() { return font; } /** * @return */ public String getText() { return text; } /** * @return */ public int getXPos() { return xPos; } /** * @return */ public int getYPos() { return yPos; } /** * @param color */ public void setColor(Color color) { this.color = color; } /** * @param font */ public void setFont(Font font) { this.font = font; } /** * @param string */ public void setText(String string) { text = string; } /** * @param i */ public void setXPos(int i) { xPos = i; } /** * @param i */ public void setYPos(int i) { yPos = i; } public Rectangle2D getBounds(){ Rectangle2D bounds = FontMeasurer.instance().getBoundsFor(text, font); bounds.setRect( bounds.getMinX() + xPos, bounds.getMinY() + yPos, bounds.getWidth(), bounds.getHeight()); return bounds; } public String toString(){ return "DiagramText, at ("+xPos+", "+yPos+"), within "+getBounds()+" '"+text+"', "+color+" "+font; } /** * @return */ public boolean isTextOnLine() { return isTextOnLine; } /** * @param b */ public void setTextOnLine(boolean b) { isTextOnLine = b; } public boolean hasOutline() { return hasOutline; } public void setHasOutline(boolean hasOutline) { this.hasOutline = hasOutline; } public Color getOutlineColor() { return outlineColor; } public void setOutlineColor(Color outlineColor) { this.outlineColor = outlineColor; } } ditaa-0.9/src/org/stathissideris/ascii2image/graphics/CustomShapeDefinition.java0000644000175000017500000000407211277077356026316 0ustar neoneo/* * DiTAA - Diagrams Through Ascii Art * * Copyright (C) 2004 Efstathios Sideris * * This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.stathissideris.ascii2image.graphics; public class CustomShapeDefinition { private String tag; private boolean stretch = false; private boolean dropShadow = true; private boolean hasBorder = false; private String filename; private String comment; public boolean dropsShadow() { return dropShadow; } public void setDropsShadow(boolean dropShadow) { this.dropShadow = dropShadow; } public String getFilename() { return filename; } public void setFilename(String filename) { this.filename = filename; } public boolean stretches() { return stretch; } public void setStretches(boolean stretch) { this.stretch = stretch; } public boolean hasBorder() { return hasBorder; } public void setHasBorder(boolean hasBorder) { this.hasBorder = hasBorder; } public String getTag() { return tag; } public void setTag(String tag) { this.tag = tag; } public String getComment() { return comment; } public void setComment(String comment) { this.comment = comment; } public String toString(){ return "Custom shape: \""+getTag()+"\":\n" +"\tfile: "+getFilename()+"\n" +"\tstretches: "+stretches()+"\n" +"\thas border: "+hasBorder()+"\n" +"\tdrops shadow: "+dropsShadow()+"\n" +"\tcomment: "+getComment()+"\n" ; } } ditaa-0.9/lib/0000755000175000017500000000000011361663105011340 5ustar neoneo