layered-graph-drawing (empty) → 0.1.0.0
raw patch · 7 files changed
+2986/−0 lines, 7 filesdep +aesondep +basedep +containerssetup-changed
Dependencies added: aeson, base, containers, data-ordlist, intmap-graph, layered-graph-drawing, text, vector, vector-algorithms
Files
- LICENSE +674/−0
- Setup.hs +2/−0
- app/Main.hs +319/−0
- layered-graph-drawing.cabal +46/−0
- src/Graph/CommonGraph.hs +226/−0
- src/Graph/GraphDrawing.hs +1478/−0
- src/Graph/SubGraphWindows.hs +241/−0
+ LICENSE view
@@ -0,0 +1,674 @@+ GNU GENERAL PUBLIC LICENSE+ Version 3, 29 June 2007++ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>+ Everyone is permitted to copy and distribute verbatim copies+ of this license document, but changing it is not allowed.++ Preamble++ The GNU General Public License is a free, copyleft license for+software and other kinds of works.++ The licenses for most software and other practical works are designed+to take away your freedom to share and change the works. By contrast,+the GNU General Public License is intended to guarantee your freedom to+share and change all versions of a program--to make sure it remains free+software for all its users. We, the Free Software Foundation, use the+GNU General Public License for most of our software; it applies also to+any other work released this way by its authors. You can apply it to+your programs, too.++ When we speak of free software, we are referring to freedom, not+price. Our General Public Licenses are designed to make sure that you+have the freedom to distribute copies of free software (and charge for+them if you wish), that you receive source code or can get it if you+want it, that you can change the software or use pieces of it in new+free programs, and that you know you can do these things.++ To protect your rights, we need to prevent others from denying you+these rights or asking you to surrender the rights. Therefore, you have+certain responsibilities if you distribute copies of the software, or if+you modify it: responsibilities to respect the freedom of others.++ For example, if you distribute copies of such a program, whether+gratis or for a fee, you must pass on to the recipients the same+freedoms that you received. You must make sure that they, too, receive+or can get the source code. And you must show them these terms so they+know their rights.++ Developers that use the GNU GPL protect your rights with two steps:+(1) assert copyright on the software, and (2) offer you this License+giving you legal permission to copy, distribute and/or modify it.++ For the developers' and authors' protection, the GPL clearly explains+that there is no warranty for this free software. For both users' and+authors' sake, the GPL requires that modified versions be marked as+changed, so that their problems will not be attributed erroneously to+authors of previous versions.++ Some devices are designed to deny users access to install or run+modified versions of the software inside them, although the manufacturer+can do so. This is fundamentally incompatible with the aim of+protecting users' freedom to change the software. The systematic+pattern of such abuse occurs in the area of products for individuals to+use, which is precisely where it is most unacceptable. Therefore, we+have designed this version of the GPL to prohibit the practice for those+products. If such problems arise substantially in other domains, we+stand ready to extend this provision to those domains in future versions+of the GPL, as needed to protect the freedom of users.++ Finally, every program is threatened constantly by software patents.+States should not allow patents to restrict development and use of+software on general-purpose computers, but in those that do, we wish to+avoid the special danger that patents applied to a free program could+make it effectively proprietary. To prevent this, the GPL assures that+patents cannot be used to render the program non-free.++ The precise terms and conditions for copying, distribution and+modification follow.++ TERMS AND CONDITIONS++ 0. Definitions.++ "This License" refers to version 3 of the GNU General Public License.++ "Copyright" also means copyright-like laws that apply to other kinds of+works, such as semiconductor masks.++ "The Program" refers to any copyrightable work licensed under this+License. Each licensee is addressed as "you". "Licensees" and+"recipients" may be individuals or organizations.++ To "modify" a work means to copy from or adapt all or part of the work+in a fashion requiring copyright permission, other than the making of an+exact copy. The resulting work is called a "modified version" of the+earlier work or a work "based on" the earlier work.++ A "covered work" means either the unmodified Program or a work based+on the Program.++ To "propagate" a work means to do anything with it that, without+permission, would make you directly or secondarily liable for+infringement under applicable copyright law, except executing it on a+computer or modifying a private copy. Propagation includes copying,+distribution (with or without modification), making available to the+public, and in some countries other activities as well.++ To "convey" a work means any kind of propagation that enables other+parties to make or receive copies. Mere interaction with a user through+a computer network, with no transfer of a copy, is not conveying.++ An interactive user interface displays "Appropriate Legal Notices"+to the extent that it includes a convenient and prominently visible+feature that (1) displays an appropriate copyright notice, and (2)+tells the user that there is no warranty for the work (except to the+extent that warranties are provided), that licensees may convey the+work under this License, and how to view a copy of this License. If+the interface presents a list of user commands or options, such as a+menu, a prominent item in the list meets this criterion.++ 1. Source Code.++ The "source code" for a work means the preferred form of the work+for making modifications to it. "Object code" means any non-source+form of a work.++ A "Standard Interface" means an interface that either is an official+standard defined by a recognized standards body, or, in the case of+interfaces specified for a particular programming language, one that+is widely used among developers working in that language.++ The "System Libraries" of an executable work include anything, other+than the work as a whole, that (a) is included in the normal form of+packaging a Major Component, but which is not part of that Major+Component, and (b) serves only to enable use of the work with that+Major Component, or to implement a Standard Interface for which an+implementation is available to the public in source code form. A+"Major Component", in this context, means a major essential component+(kernel, window system, and so on) of the specific operating system+(if any) on which the executable work runs, or a compiler used to+produce the work, or an object code interpreter used to run it.++ The "Corresponding Source" for a work in object code form means all+the source code needed to generate, install, and (for an executable+work) run the object code and to modify the work, including scripts to+control those activities. However, it does not include the work's+System Libraries, or general-purpose tools or generally available free+programs which are used unmodified in performing those activities but+which are not part of the work. For example, Corresponding Source+includes interface definition files associated with source files for+the work, and the source code for shared libraries and dynamically+linked subprograms that the work is specifically designed to require,+such as by intimate data communication or control flow between those+subprograms and other parts of the work.++ The Corresponding Source need not include anything that users+can regenerate automatically from other parts of the Corresponding+Source.++ The Corresponding Source for a work in source code form is that+same work.++ 2. Basic Permissions.++ All rights granted under this License are granted for the term of+copyright on the Program, and are irrevocable provided the stated+conditions are met. This License explicitly affirms your unlimited+permission to run the unmodified Program. The output from running a+covered work is covered by this License only if the output, given its+content, constitutes a covered work. This License acknowledges your+rights of fair use or other equivalent, as provided by copyright law.++ You may make, run and propagate covered works that you do not+convey, without conditions so long as your license otherwise remains+in force. You may convey covered works to others for the sole purpose+of having them make modifications exclusively for you, or provide you+with facilities for running those works, provided that you comply with+the terms of this License in conveying all material for which you do+not control copyright. Those thus making or running the covered works+for you must do so exclusively on your behalf, under your direction+and control, on terms that prohibit them from making any copies of+your copyrighted material outside their relationship with you.++ Conveying under any other circumstances is permitted solely under+the conditions stated below. Sublicensing is not allowed; section 10+makes it unnecessary.++ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.++ No covered work shall be deemed part of an effective technological+measure under any applicable law fulfilling obligations under article+11 of the WIPO copyright treaty adopted on 20 December 1996, or+similar laws prohibiting or restricting circumvention of such+measures.++ When you convey a covered work, you waive any legal power to forbid+circumvention of technological measures to the extent such circumvention+is effected by exercising rights under this License with respect to+the covered work, and you disclaim any intention to limit operation or+modification of the work as a means of enforcing, against the work's+users, your or third parties' legal rights to forbid circumvention of+technological measures.++ 4. Conveying Verbatim Copies.++ You may convey verbatim copies of the Program's source code as you+receive it, in any medium, provided that you conspicuously and+appropriately publish on each copy an appropriate copyright notice;+keep intact all notices stating that this License and any+non-permissive terms added in accord with section 7 apply to the code;+keep intact all notices of the absence of any warranty; and give all+recipients a copy of this License along with the Program.++ You may charge any price or no price for each copy that you convey,+and you may offer support or warranty protection for a fee.++ 5. Conveying Modified Source Versions.++ You may convey a work based on the Program, or the modifications to+produce it from the Program, in the form of source code under the+terms of section 4, provided that you also meet all of these conditions:++ a) The work must carry prominent notices stating that you modified+ it, and giving a relevant date.++ b) The work must carry prominent notices stating that it is+ released under this License and any conditions added under section+ 7. This requirement modifies the requirement in section 4 to+ "keep intact all notices".++ c) You must license the entire work, as a whole, under this+ License to anyone who comes into possession of a copy. This+ License will therefore apply, along with any applicable section 7+ additional terms, to the whole of the work, and all its parts,+ regardless of how they are packaged. This License gives no+ permission to license the work in any other way, but it does not+ invalidate such permission if you have separately received it.++ d) If the work has interactive user interfaces, each must display+ Appropriate Legal Notices; however, if the Program has interactive+ interfaces that do not display Appropriate Legal Notices, your+ work need not make them do so.++ A compilation of a covered work with other separate and independent+works, which are not by their nature extensions of the covered work,+and which are not combined with it such as to form a larger program,+in or on a volume of a storage or distribution medium, is called an+"aggregate" if the compilation and its resulting copyright are not+used to limit the access or legal rights of the compilation's users+beyond what the individual works permit. Inclusion of a covered work+in an aggregate does not cause this License to apply to the other+parts of the aggregate.++ 6. Conveying Non-Source Forms.++ You may convey a covered work in object code form under the terms+of sections 4 and 5, provided that you also convey the+machine-readable Corresponding Source under the terms of this License,+in one of these ways:++ a) Convey the object code in, or embodied in, a physical product+ (including a physical distribution medium), accompanied by the+ Corresponding Source fixed on a durable physical medium+ customarily used for software interchange.++ b) Convey the object code in, or embodied in, a physical product+ (including a physical distribution medium), accompanied by a+ written offer, valid for at least three years and valid for as+ long as you offer spare parts or customer support for that product+ model, to give anyone who possesses the object code either (1) a+ copy of the Corresponding Source for all the software in the+ product that is covered by this License, on a durable physical+ medium customarily used for software interchange, for a price no+ more than your reasonable cost of physically performing this+ conveying of source, or (2) access to copy the+ Corresponding Source from a network server at no charge.++ c) Convey individual copies of the object code with a copy of the+ written offer to provide the Corresponding Source. This+ alternative is allowed only occasionally and noncommercially, and+ only if you received the object code with such an offer, in accord+ with subsection 6b.++ d) Convey the object code by offering access from a designated+ place (gratis or for a charge), and offer equivalent access to the+ Corresponding Source in the same way through the same place at no+ further charge. You need not require recipients to copy the+ Corresponding Source along with the object code. If the place to+ copy the object code is a network server, the Corresponding Source+ may be on a different server (operated by you or a third party)+ that supports equivalent copying facilities, provided you maintain+ clear directions next to the object code saying where to find the+ Corresponding Source. Regardless of what server hosts the+ Corresponding Source, you remain obligated to ensure that it is+ available for as long as needed to satisfy these requirements.++ e) Convey the object code using peer-to-peer transmission, provided+ you inform other peers where the object code and Corresponding+ Source of the work are being offered to the general public at no+ charge under subsection 6d.++ A separable portion of the object code, whose source code is excluded+from the Corresponding Source as a System Library, need not be+included in conveying the object code work.++ A "User Product" is either (1) a "consumer product", which means any+tangible personal property which is normally used for personal, family,+or household purposes, or (2) anything designed or sold for incorporation+into a dwelling. In determining whether a product is a consumer product,+doubtful cases shall be resolved in favor of coverage. For a particular+product received by a particular user, "normally used" refers to a+typical or common use of that class of product, regardless of the status+of the particular user or of the way in which the particular user+actually uses, or expects or is expected to use, the product. A product+is a consumer product regardless of whether the product has substantial+commercial, industrial or non-consumer uses, unless such uses represent+the only significant mode of use of the product.++ "Installation Information" for a User Product means any methods,+procedures, authorization keys, or other information required to install+and execute modified versions of a covered work in that User Product from+a modified version of its Corresponding Source. The information must+suffice to ensure that the continued functioning of the modified object+code is in no case prevented or interfered with solely because+modification has been made.++ If you convey an object code work under this section in, or with, or+specifically for use in, a User Product, and the conveying occurs as+part of a transaction in which the right of possession and use of the+User Product is transferred to the recipient in perpetuity or for a+fixed term (regardless of how the transaction is characterized), the+Corresponding Source conveyed under this section must be accompanied+by the Installation Information. But this requirement does not apply+if neither you nor any third party retains the ability to install+modified object code on the User Product (for example, the work has+been installed in ROM).++ The requirement to provide Installation Information does not include a+requirement to continue to provide support service, warranty, or updates+for a work that has been modified or installed by the recipient, or for+the User Product in which it has been modified or installed. Access to a+network may be denied when the modification itself materially and+adversely affects the operation of the network or violates the rules and+protocols for communication across the network.++ Corresponding Source conveyed, and Installation Information provided,+in accord with this section must be in a format that is publicly+documented (and with an implementation available to the public in+source code form), and must require no special password or key for+unpacking, reading or copying.++ 7. Additional Terms.++ "Additional permissions" are terms that supplement the terms of this+License by making exceptions from one or more of its conditions.+Additional permissions that are applicable to the entire Program shall+be treated as though they were included in this License, to the extent+that they are valid under applicable law. If additional permissions+apply only to part of the Program, that part may be used separately+under those permissions, but the entire Program remains governed by+this License without regard to the additional permissions.++ When you convey a copy of a covered work, you may at your option+remove any additional permissions from that copy, or from any part of+it. (Additional permissions may be written to require their own+removal in certain cases when you modify the work.) You may place+additional permissions on material, added by you to a covered work,+for which you have or can give appropriate copyright permission.++ Notwithstanding any other provision of this License, for material you+add to a covered work, you may (if authorized by the copyright holders of+that material) supplement the terms of this License with terms:++ a) Disclaiming warranty or limiting liability differently from the+ terms of sections 15 and 16 of this License; or++ b) Requiring preservation of specified reasonable legal notices or+ author attributions in that material or in the Appropriate Legal+ Notices displayed by works containing it; or++ c) Prohibiting misrepresentation of the origin of that material, or+ requiring that modified versions of such material be marked in+ reasonable ways as different from the original version; or++ d) Limiting the use for publicity purposes of names of licensors or+ authors of the material; or++ e) Declining to grant rights under trademark law for use of some+ trade names, trademarks, or service marks; or++ f) Requiring indemnification of licensors and authors of that+ material by anyone who conveys the material (or modified versions of+ it) with contractual assumptions of liability to the recipient, for+ any liability that these contractual assumptions directly impose on+ those licensors and authors.++ All other non-permissive additional terms are considered "further+restrictions" within the meaning of section 10. If the Program as you+received it, or any part of it, contains a notice stating that it is+governed by this License along with a term that is a further+restriction, you may remove that term. If a license document contains+a further restriction but permits relicensing or conveying under this+License, you may add to a covered work material governed by the terms+of that license document, provided that the further restriction does+not survive such relicensing or conveying.++ If you add terms to a covered work in accord with this section, you+must place, in the relevant source files, a statement of the+additional terms that apply to those files, or a notice indicating+where to find the applicable terms.++ Additional terms, permissive or non-permissive, may be stated in the+form of a separately written license, or stated as exceptions;+the above requirements apply either way.++ 8. Termination.++ You may not propagate or modify a covered work except as expressly+provided under this License. Any attempt otherwise to propagate or+modify it is void, and will automatically terminate your rights under+this License (including any patent licenses granted under the third+paragraph of section 11).++ However, if you cease all violation of this License, then your+license from a particular copyright holder is reinstated (a)+provisionally, unless and until the copyright holder explicitly and+finally terminates your license, and (b) permanently, if the copyright+holder fails to notify you of the violation by some reasonable means+prior to 60 days after the cessation.++ Moreover, your license from a particular copyright holder is+reinstated permanently if the copyright holder notifies you of the+violation by some reasonable means, this is the first time you have+received notice of violation of this License (for any work) from that+copyright holder, and you cure the violation prior to 30 days after+your receipt of the notice.++ Termination of your rights under this section does not terminate the+licenses of parties who have received copies or rights from you under+this License. If your rights have been terminated and not permanently+reinstated, you do not qualify to receive new licenses for the same+material under section 10.++ 9. Acceptance Not Required for Having Copies.++ You are not required to accept this License in order to receive or+run a copy of the Program. Ancillary propagation of a covered work+occurring solely as a consequence of using peer-to-peer transmission+to receive a copy likewise does not require acceptance. However,+nothing other than this License grants you permission to propagate or+modify any covered work. These actions infringe copyright if you do+not accept this License. Therefore, by modifying or propagating a+covered work, you indicate your acceptance of this License to do so.++ 10. Automatic Licensing of Downstream Recipients.++ Each time you convey a covered work, the recipient automatically+receives a license from the original licensors, to run, modify and+propagate that work, subject to this License. You are not responsible+for enforcing compliance by third parties with this License.++ An "entity transaction" is a transaction transferring control of an+organization, or substantially all assets of one, or subdividing an+organization, or merging organizations. If propagation of a covered+work results from an entity transaction, each party to that+transaction who receives a copy of the work also receives whatever+licenses to the work the party's predecessor in interest had or could+give under the previous paragraph, plus a right to possession of the+Corresponding Source of the work from the predecessor in interest, if+the predecessor has it or can get it with reasonable efforts.++ You may not impose any further restrictions on the exercise of the+rights granted or affirmed under this License. For example, you may+not impose a license fee, royalty, or other charge for exercise of+rights granted under this License, and you may not initiate litigation+(including a cross-claim or counterclaim in a lawsuit) alleging that+any patent claim is infringed by making, using, selling, offering for+sale, or importing the Program or any portion of it.++ 11. Patents.++ A "contributor" is a copyright holder who authorizes use under this+License of the Program or a work on which the Program is based. The+work thus licensed is called the contributor's "contributor version".++ A contributor's "essential patent claims" are all patent claims+owned or controlled by the contributor, whether already acquired or+hereafter acquired, that would be infringed by some manner, permitted+by this License, of making, using, or selling its contributor version,+but do not include claims that would be infringed only as a+consequence of further modification of the contributor version. For+purposes of this definition, "control" includes the right to grant+patent sublicenses in a manner consistent with the requirements of+this License.++ Each contributor grants you a non-exclusive, worldwide, royalty-free+patent license under the contributor's essential patent claims, to+make, use, sell, offer for sale, import and otherwise run, modify and+propagate the contents of its contributor version.++ In the following three paragraphs, a "patent license" is any express+agreement or commitment, however denominated, not to enforce a patent+(such as an express permission to practice a patent or covenant not to+sue for patent infringement). To "grant" such a patent license to a+party means to make such an agreement or commitment not to enforce a+patent against the party.++ If you convey a covered work, knowingly relying on a patent license,+and the Corresponding Source of the work is not available for anyone+to copy, free of charge and under the terms of this License, through a+publicly available network server or other readily accessible means,+then you must either (1) cause the Corresponding Source to be so+available, or (2) arrange to deprive yourself of the benefit of the+patent license for this particular work, or (3) arrange, in a manner+consistent with the requirements of this License, to extend the patent+license to downstream recipients. "Knowingly relying" means you have+actual knowledge that, but for the patent license, your conveying the+covered work in a country, or your recipient's use of the covered work+in a country, would infringe one or more identifiable patents in that+country that you have reason to believe are valid.++ If, pursuant to or in connection with a single transaction or+arrangement, you convey, or propagate by procuring conveyance of, a+covered work, and grant a patent license to some of the parties+receiving the covered work authorizing them to use, propagate, modify+or convey a specific copy of the covered work, then the patent license+you grant is automatically extended to all recipients of the covered+work and works based on it.++ A patent license is "discriminatory" if it does not include within+the scope of its coverage, prohibits the exercise of, or is+conditioned on the non-exercise of one or more of the rights that are+specifically granted under this License. You may not convey a covered+work if you are a party to an arrangement with a third party that is+in the business of distributing software, under which you make payment+to the third party based on the extent of your activity of conveying+the work, and under which the third party grants, to any of the+parties who would receive the covered work from you, a discriminatory+patent license (a) in connection with copies of the covered work+conveyed by you (or copies made from those copies), or (b) primarily+for and in connection with specific products or compilations that+contain the covered work, unless you entered into that arrangement,+or that patent license was granted, prior to 28 March 2007.++ Nothing in this License shall be construed as excluding or limiting+any implied license or other defenses to infringement that may+otherwise be available to you under applicable patent law.++ 12. No Surrender of Others' Freedom.++ If conditions are imposed on you (whether by court order, agreement or+otherwise) that contradict the conditions of this License, they do not+excuse you from the conditions of this License. If you cannot convey a+covered work so as to satisfy simultaneously your obligations under this+License and any other pertinent obligations, then as a consequence you may+not convey it at all. For example, if you agree to terms that obligate you+to collect a royalty for further conveying from those to whom you convey+the Program, the only way you could satisfy both those terms and this+License would be to refrain entirely from conveying the Program.++ 13. Use with the GNU Affero General Public License.++ Notwithstanding any other provision of this License, you have+permission to link or combine any covered work with a work licensed+under version 3 of the GNU Affero General Public License into a single+combined work, and to convey the resulting work. The terms of this+License will continue to apply to the part which is the covered work,+but the special requirements of the GNU Affero General Public License,+section 13, concerning interaction through a network will apply to the+combination as such.++ 14. Revised Versions of this License.++ The Free Software Foundation may publish revised and/or new versions of+the GNU General Public License from time to time. Such new versions will+be similar in spirit to the present version, but may differ in detail to+address new problems or concerns.++ Each version is given a distinguishing version number. If the+Program specifies that a certain numbered version of the GNU General+Public License "or any later version" applies to it, you have the+option of following the terms and conditions either of that numbered+version or of any later version published by the Free Software+Foundation. If the Program does not specify a version number of the+GNU General Public License, you may choose any version ever published+by the Free Software Foundation.++ If the Program specifies that a proxy can decide which future+versions of the GNU General Public License can be used, that proxy's+public statement of acceptance of a version permanently authorizes you+to choose that version for the Program.++ Later license versions may give you additional or different+permissions. However, no additional obligations are imposed on any+author or copyright holder as a result of your choosing to follow a+later version.++ 15. Disclaimer of Warranty.++ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.++ 16. Limitation of Liability.++ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF+SUCH DAMAGES.++ 17. Interpretation of Sections 15 and 16.++ If the disclaimer of warranty and limitation of liability provided+above cannot be given local legal effect according to their terms,+reviewing courts shall apply local law that most closely approximates+an absolute waiver of all civil liability in connection with the+Program, unless a warranty or assumption of liability accompanies a+copy of the Program in return for a fee.++ END OF TERMS AND CONDITIONS++ How to Apply These Terms to Your New Programs++ If you develop a new program, and you want it to be of the greatest+possible use to the public, the best way to achieve this is to make it+free software which everyone can redistribute and change under these terms.++ To do so, attach the following notices to the program. It is safest+to attach them to the start of each source file to most effectively+state the exclusion of warranty; and each file should have at least+the "copyright" line and a pointer to where the full notice is found.++ <one line to give the program's name and a brief idea of what it does.>+ Copyright (C) <year> <name of author>++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <https://www.gnu.org/licenses/>.++Also add information on how to contact you by electronic and paper mail.++ If the program does terminal interaction, make it output a short+notice like this when it starts in an interactive mode:++ <program> Copyright (C) <year> <name of author>+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.+ This is free software, and you are welcome to redistribute it+ under certain conditions; type `show c' for details.++The hypothetical commands `show w' and `show c' should show the appropriate+parts of the General Public License. Of course, your program's commands+might be different; for a GUI interface, you would use an "about box".++ You should also get your employer (if you work as a programmer) or school,+if any, to sign a "copyright disclaimer" for the program, if necessary.+For more information on this, and how to apply and follow the GNU GPL, see+<https://www.gnu.org/licenses/>.++ The GNU General Public License does not permit incorporating your program+into proprietary programs. If your program is a subroutine library, you+may consider it more useful to permit linking proprietary applications with+the library. If this is what you want to do, use the GNU Lesser General+Public License instead of this License. But first, please read+<https://www.gnu.org/licenses/why-not-lgpl.html>.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ app/Main.hs view
@@ -0,0 +1,319 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoMonomorphismRestriction #-}++module Main where++import qualified Data.Map as Map+import Data.Maybe (catMaybes)+import Data.Text (Text)+import qualified Data.Text as T+import Data.Word (Word32)+import Debug.Trace (trace)+--import Diagrams.Backend.SVG.CmdLine (B, mainWith)+--import Diagrams.Prelude+import GHC.Generics+import Graph.CommonGraph (Channel, CGraph, CGraphL, EdgeType (..), NodeClass (..), EdgeClass (..), UINode)+import Graph.GraphDrawing (fromAdj, layeredGraph)+import qualified Graph.IntMap as Graph+--import Graphics.SVGFonts (textSVG)++-- several papers use this graph+testGraph :: CGraph UINodeLabel UIEdgeLabel+testGraph = fromAdj (extractNodes gr) gr+ where+ gr =+ [ (0, [4, 5, 6, 20, 29], dEdge),+ (17, [3], dEdge),+ (5, [2, 3, 4, 10, 14, 26], dEdge),+ (3, [4], dEdge),+ (26, [18], dEdge),+ (14, [10], dEdge),+ (2, [8, 23], dEdge),+ (18, [13], dEdge),+ (10, [27], dEdge),+ (2, [8, 23], dEdge),+ (23, [24, 9], dEdge),+ (24, [12, 15, 28], dEdge),+ (1, [12], dEdge),+ (12, [6, 9, 19], dEdge),+ (6, [9, 13, 21, 25, 29], dEdge),+ (29, [25], dEdge),+ (21, [22], dEdge),+ (25, [11], dEdge),+ (22, [8, 9], dEdge),+ (15, [27, 16], dEdge),+ (8, [16, 20], dEdge),+ (11, [16, 9], dEdge),+ (16, [27], dEdge),+ (27, [13, 19], dEdge),+ (13, [20], dEdge),+ (19, [9], dEdge),+ (20, [4], dEdge),+ (9, [4, 28], dEdge),+ (28, [7], dEdge)+ ]++testGraph2 :: CGraph UINodeLabel UIEdgeLabel+testGraph2 = fromAdj (extractNodes gr) gr+ where+ gr =+ [ (1, [3, 4, 21, 13], dEdge),+ (2, [3, 20], dEdge),+ (3, [4, 5, 23], dEdge),+ (4, [6], dEdge),+ (5, [7], dEdge),+ (6, [8, 16, 23], dEdge),+ (7, [9], dEdge),+ (8, [10, 11], dEdge),+ (9, [12], dEdge),+ (10, [13, 14, 15], dEdge),+ (11, [15, 16], dEdge),+ (12, [20], dEdge),+ (13, [17], dEdge),+ (14, [17, 18], dEdge),+ (16, [18, 19, 20], dEdge),+ (18, [21], dEdge),+ (19, [22], dEdge),+ (21, [23], dEdge),+ (22, [23], dEdge),+ (23, [24], dEdge),+ (24, [25], dEdge),+ (26, [27], dEdge),+ (27, [28], dEdge),+ (29, [30], dEdge),+ (30, [31], dEdge),+ (32, [31], dEdge),+ (33, [31], dEdge),+ (25, [34], dEdge),+ -- (19, [26], dEdge),+ (23, [27], vdummyEdge),+ (27, [30], vdummyEdge)+ ]++-- A typical graph of three unconnected functions with one input and output.+-- Should be displayed vertical as options+-- Vertical lines between the function nodes give an order. If the graph grows the vertical nodes should+-- still be in one column+optionsGraph :: CGraph UINodeLabel UIEdgeLabel+optionsGraph = fromAdj (extractNodes gr) gr+ where+ gr =+ [ (1, [2], dEdge),+ (2, [3], dEdge),+ (4, [5], dEdge),+ (5, [6], dEdge),+ (7, [8], dEdge),+ (8, [9], dEdge),+ (9, [10], dEdge),+ (2, [5], vdummyEdge),+ (5, [8], vdummyEdge)+ ]++dEdge :: [UIEdgeLabel]+dEdge = [standard NormalEdge]++-- [UIEdge 2 1 "" Curly "#ff5863" "" 0 False False]++vdummyEdge :: [UIEdgeLabel]+vdummyEdge = [standard VirtualHorEdge]++-- [UIEdge 2 1 "" Curly "#ff5863" "" 0 True False]+extractNodes :: EdgeClass e => [(Word32, [Word32], [e])] -> Map.Map Word32 UINodeLabel+extractNodes adj = Map.fromList (map labelNode nodes)+ where+ nodes = (map sel1 adj) ++ (concat (map sel2 adj))+ sel1 (x, y, z) = x+ sel2 (x, y, z) = y+ labelNode n =+ ( n,+ UINodeLabel+ ( FuN+ ( FunctionNode+ ""+ (T.pack (show n))+ ""+ )+ )+ )++main :: IO ()+main = return () -- mainWith (visualGraph (layeredGraph True testGraph) :: Diagram B)++------------------------------------------------------------------------------------------------++data UINodeLabel = UINodeLabel+ {+ uinode :: SpecialNode [Text]+ }+ deriving (Show)++data UIEdgeLabel = UIEdgeLabel+ { lineClass :: Text,+ strokeWidth :: Maybe Int,+ opacity :: Maybe Double,+ dashArray :: Maybe Text,+ lineColor :: Maybe Text,+ lineName :: Text,+ lineType :: Maybe LineType,+ channelNrIn :: Maybe Channel, -- arg nr x of constructor node++ -- | Me, Colleague-Name to remeber past decisions, but only people you know,+ -- or company, group+ channelNrOut :: Channel, -- function output is connected with input nr x of type node+ edgeType :: EdgeType+ }+ deriving (Show)++data LineType+ = Slanted Int -- a line consisiting of slanted sublines,+ -- (a lot of people did this, but no famous)+ | Curly -- contains a several subpaths+ deriving (Show, Generic, Eq, Ord)++data SpecialNode a+ = FuN FunctionNode+ | SubN [a]+ | DN DummyNode+ | CN ConnectionNode+ | AN ArgNode+ deriving (Eq, Ord, Show, Generic)++data DummyNode = DummyNode {dsize :: Int} deriving (Eq, Ord, Show, Generic)++data ConnectionNode = ConnectionNode {size :: Int} deriving (Eq, Ord, Show, Generic)++data FunctionNode = FunctionNode+ { functionUnique :: Text,+ functionName :: Text,+ functionType :: Text -- maybe not displayed+ }+ deriving (Eq, Ord, Generic, Show)++data ArgNode = ArgNode+ { refFunctionUnique :: Text,+ argName :: Text,+ argNr :: Int,+ isValueArg :: Bool,+ isMainFunctionArg :: Bool,+ argSelected :: Bool+ }+ deriving (Eq, Ord, Show, Generic)++instance NodeClass UINodeLabel where+ isDummy gr n = False -- maybe False isDummyLabel (Graph.lookupNode n gr)+ isConnNode gr n = maybe False isConnLabel (Graph.lookupNode n gr)+ isFunction gr n = maybe False isFuncLabel (Graph.lookupNode n gr)+ isMainArg gr n = False -- maybe False isArgLabel (Graph.lookupNode n gr)+ isSubLabel (UINodeLabel (SubN _)) = True+ isSubLabel _ = False+ isArgLabel (UINodeLabel (AN _)) = True+ isArgLabel _ = False+ subLabels (UINodeLabel (SubN as)) = length as+ connectionNode = UINodeLabel (CN (ConnectionNode 1))+ dummyNode x = UINodeLabel (DN (DummyNode x))+ nestingFeatures _ = Nothing+ updateLayer _ n = n+ verticalNumber _ = Nothing++instance EdgeClass UIEdgeLabel where+ dummyEdge chIn chOut = UIEdgeLabel "" Nothing Nothing Nothing Nothing "" Nothing chIn chOut NormalEdge+ standard edgeType = UIEdgeLabel "" Nothing Nothing Nothing Nothing "" Nothing Nothing 0 edgeType+ edgeType (UIEdgeLabel _ _ _ _ _ _ _ _ _ e) = e+ channelNrIn (UIEdgeLabel _ _ _ _ _ _ _ channelIn channelOut _) = channelIn+ channelNrOut (UIEdgeLabel _ _ _ _ _ _ _ channelIn channelOut _) = channelOut++isDummyLabel :: UINodeLabel -> Bool+isDummyLabel (UINodeLabel (DN _)) = True+isDummyLabel _ = False++isConnLabel :: UINodeLabel -> Bool+isConnLabel (UINodeLabel (CN _)) = True+isConnLabel _ = False++isFuncLabel :: UINodeLabel -> Bool+isFuncLabel (UINodeLabel (FuN _)) = True+isFuncLabel _ = False++isArgLabel :: UINodeLabel -> Bool+isArgLabel (UINodeLabel (AN _)) = True+isArgLabel _ = False++-----------------------------------------------------------------------------------------------++-- Using diagrams to visualise the graph (this is commented out, so that you only need the quite big diagrams dependency when using this for debugging)+{-+visualGraph :: (NodeClass n, EdgeClass e) => CGraphL n e -> Diagram B+visualGraph (g, nPositions) =+ Debug.Trace.trace (show (length shortEs) ++ " : " ++ show (length longEs) ++ show (Graph.nodes testGraph, Graph.nodes g)) $+ position graphNodes # connections shortEs+ # connectionsWithoutArrow longEs+ where+ graphNodes = catMaybes (map pos (Graph.nodes g))+ pos n = fmap (\(x, y) -> (p2 ((fromIntegral x) * 2, (fromIntegral y)), vNode (fromIntegral n))) lu+ where+ lu = Map.lookup (fromIntegral n) nPositions+ es = Graph.edges g+ shortEs = filter (\(src, tgt) -> not (isConnNode g src) && not (isConnNode g tgt)) es+ longEs = filter (\(src, tgt) -> (isConnNode g src) || (isConnNode g tgt)) es+ vNode :: Int -> Diagram B+ vNode n = visualNode (n > 31) (show n) -- (isDummyNode g (fromIntegral n)) (show n)++connections es+ | (length es) < 2 = id+ | otherwise =+ foldr1 (.) $+ map+ ( \(src, tgt) ->+ connectOutside' arrowStyle2 (show src) (show tgt)+ )+ es++connectionsWithoutArrow es+ | (length es) < 2 = id+ | otherwise =+ foldr1 (.) $+ map+ ( \(src, tgt) ->+ connectOutside' arrowStyle3 (show src) (show tgt)+ )+ es++visualNode l str+ | l -- (stateLabel str <>+ =+ dummy # named str+ | otherwise = (stateLabel str <> state) # named str++text' d s = (strokeP $ textSVG s 0.7) # lw none # fc black # center # showOrigin++stateLabel = text' 0.6++arrowLabel txt size = text' size txt++state = circle 0.4 # fc silver++dummy = circle 0.05 # fc black -- mempty++shaft = arc xDir (-1 / 6 @@ turn)++shaft' = arc xDir (-2.7 / 5 @@ turn)++line = trailFromOffsets [unitX]++arrowStyle1 =+ ( with & arrowHead .~ spike & headLength .~ small+ & arrowShaft .~ shaft+ )++arrowStyle2 =+ ( with & arrowHead .~ spike & headLength .~ small+ & arrowShaft .~ line+ )++arrowStyle3 = (with & arrowHead .~ noHead & arrowShaft .~ line) -- & shaftStyle %~ dashingG [0.1, 0.05] 0)+-}
+ layered-graph-drawing.cabal view
@@ -0,0 +1,46 @@+name: layered-graph-drawing+version: 0.1.0.0+synopsis: Layered Graph Drawing after Sugiyama+description: Calculates the positions of graph nodes in a table with layered graph drawing+homepage: https://github.com/BeFunctional/layered-graph-drawing+license: BSD3+license-file: LICENSE+author: Tillmann Vogt+maintainer: tillk.vogt@gmail.com+copyright: 2023 Tillmann Vogt+category: graphics, graph+build-type: Simple+-- extra-source-files:+cabal-version: >=1.10++library+ hs-source-dirs: src+ exposed-modules: Graph.CommonGraph+ , Graph.GraphDrawing+ , Graph.SubGraphWindows+ build-depends: aeson+ , base >= 4.7 && < 5+ , containers+ , data-ordlist+ , intmap-graph+ , text+ , vector+ , vector-algorithms+ default-language: Haskell2010++executable graph-drawing-exe+ hs-source-dirs: app+ main-is: Main.hs+ ghc-options: -threaded -rtsopts -with-rtsopts=-N+ build-depends: base+ , containers+ , layered-graph-drawing+-- , diagrams+-- , diagrams-lib+-- , diagrams-svg+ , intmap-graph+-- , SVGFonts+ , text++ default-language: Haskell2010+
+ src/Graph/CommonGraph.hs view
@@ -0,0 +1,226 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoMonomorphismRestriction #-}++module Graph.CommonGraph where++import Data.Aeson (FromJSON (..), ToJSON (..))+import qualified Data.IntMap as I+import Data.List (group, sort)+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+import Data.Maybe (fromJust, isJust)+import qualified Data.Vector.Unboxed as VU+import Data.Word (Word32, Word8)+import Debug.Trace (trace)+import GHC.Generics (Generic)+import Graph.IntMap+ ( Edge8 (..),+ EdgeAttribute (..),+ ExtractNodeType (..),+ Graph (..),+ adjacentNodesByAttr,+ )++type UINode = Word32++-- A Graph consists of nodes and edges, graph drawing arranges it from left to right,+-- start to end.+type CGraph n e = Graph n [e]++type CGraphL n e = (Graph n [e], Map UINode (Int, Int))++type Channel = Int -- The nth type of a type node+-- This is path of function and type nodes with spaces that can be filled with options++data EdgeType+ = NormalEdge+ | VerticalEdge -- When having options, they appear continuously in one column+ -- We mark this in the graph with vertical edges from the first+ -- option to the second and so on+ | VirtualHorEdge -- virtual edges are not displayed but used for layouting and+ -- naviagtion with the keyboard+ | SeparatingEdge -- to connect graph components that are separate+ deriving (Show, Generic, Eq, Ord)++type GraphMoveX = Int++type Column = (GraphMoveX, [UINode])++--instance NodeClass n => Eq n where+-- node1 == node2 = uinode node1 == uinode node2++class NodeClass n where+ isDummy :: EdgeClass e => CGraph n e -> UINode -> Bool+ isConnNode :: EdgeClass e => CGraph n e -> UINode -> Bool+ isFunction :: EdgeClass e => CGraph n e -> UINode -> Bool+ isMainArg :: CGraph n e -> UINode -> Bool+ isSubLabel :: n -> Bool+ isArgLabel :: n -> Bool+ subLabels :: n -> Int+ connectionNode :: n+ dummyNode :: Int -> n -- Depth -> n+ nestingFeatures :: n -> Maybe LayerFeatures+ updateLayer :: Maybe LayerFeatures -> n -> n+ verticalNumber :: n -> Maybe Word32 -- we want to keep the order of vertically connected Nodes,++type ChannelNrIn = Maybe Channel++type ChannelNrOut = Channel++class EdgeClass e where+ dummyEdge :: ChannelNrIn -> ChannelNrOut -> e+ standard :: EdgeType -> e+ edgeType :: e -> EdgeType+ channelNrIn :: e -> ChannelNrIn+ channelNrOut :: e -> ChannelNrOut++------------------------------------------------------------------------------------------------------++myFromJust :: Int -> Maybe a -> a+myFromJust i term+ | isJust term -- Debug.Trace.trace ("myFromJustTrue "++ show i)+ =+ fromJust term+ | otherwise =+ Debug.Trace.trace+ ("myFromJust " ++ show i)+ fromJust+ term++myhead :: Int -> [a] -> a+myhead i a+ | null a = error ("head: empty list " ++ show i)+ | otherwise = head a++vhead :: (VU.Unbox a) => Int -> VU.Vector a -> a+vhead i a+ | VU.null a = error ("VU.head: empty list " ++ show i)+ | otherwise = VU.head a++rmdups :: (Ord a) => [a] -> [a]+rmdups = map (myhead 500) . group . sort++------------------------------------------------------------------------------------------------------++vertBit :: Word8+vertBit = 0x1++virtBit :: Word8+virtBit = 0x2++sepBit :: Word8+sepBit = 0x4++instance EdgeClass e => EdgeAttribute [e] where -- Why can two nodes be connected with more than one edge?+-- To connect one function with several input types that are part of one type node+ fastEdgeAttr (e : _) = f (edgeType e)+ where+ f VerticalEdge = vertBit+ f VirtualHorEdge = virtBit+ f _ = 0+ fastEdgeAttr _ = 0+ edgeFromAttr =+ Map.fromList+ [ (vertBit, [standard VerticalEdge]),+ (virtBit, [standard VirtualHorEdge]),+ (0, [standard NormalEdge])+ ]++ -- show_e (Just [UIEdge standard Nothing 0 e]) = show e+ -- show_e _ = "no Edge"+ bases _ = [Edge8 0, Edge8 vertBit, Edge8 virtBit]++childrenSeparating :: EdgeClass e => CGraph n e -> Word32 -> VU.Vector Word32+childrenSeparating gr n = adjacentNodesByAttr gr True n (Edge8 sepBit)++childrenNoVertical :: EdgeClass e => Graph n [e] -> Word32 -> VU.Vector Word32+childrenNoVertical gr n =+ adjacentNodesByAttr gr True n (Edge8 virtBit)+ VU.++ adjacentNodesByAttr gr True n (Edge8 0)++childrenVertical :: EdgeClass e => Graph n [e] -> Word32 -> VU.Vector Word32+childrenVertical gr n = adjacentNodesByAttr gr True n (Edge8 vertBit)++parentsVertical :: EdgeClass e => Graph n [e] -> Word32 -> VU.Vector Word32+parentsVertical gr n = adjacentNodesByAttr gr False n (Edge8 vertBit)++parentsNoVertical :: EdgeClass e => Graph n [e] -> Word32 -> VU.Vector Word32+parentsNoVertical gr n =+ adjacentNodesByAttr gr False n (Edge8 virtBit)+ VU.++ adjacentNodesByAttr gr False n (Edge8 0)++verticallyConnectedNodes :: EdgeClass e => CGraph n e -> UINode -> [UINode]+verticallyConnectedNodes g n =+ VU.toList $+ goUp (parentsVertical g n)+ VU.++ VU.cons n (goDown (childrenVertical g n))+ where+ goUp nodes+ | VU.null nodes = VU.empty+ | otherwise =+ nodes+ VU.++ VU.concatMap (goUp . parentsVertical g) nodes+ goDown nodes+ | VU.null nodes = VU.empty+ | otherwise =+ nodes+ VU.++ VU.concatMap (goDown . childrenVertical g) nodes++------------------------------------------------------------------------------------------------------++data LayerFeatures = LayerFeatures+ { layer :: Int, -- Expanding a fuction generates a new layer+ -- (maybe make every new layer a little bit darker)+ border :: Maybe Border -- To set the right css values (border, boxshadow)+ }+ deriving (Show, Generic)++instance FromJSON LayerFeatures++instance ToJSON LayerFeatures++lb :: Int -> Maybe LayerFeatures+lb n = Just (LayerFeatures n (Just LeftBorder))++rb :: Int -> Maybe LayerFeatures+rb n = Just (LayerFeatures n (Just RightBorder))++tb :: Int -> Maybe LayerFeatures+tb n = Just (LayerFeatures n (Just TopBorder))++bb :: Int -> Maybe LayerFeatures+bb n = Just (LayerFeatures n (Just BottomBorder))++ltb :: Int -> Maybe LayerFeatures+ltb n = Just (LayerFeatures n (Just LeftTopBorder))++rtb :: Int -> Maybe LayerFeatures+rtb n = Just (LayerFeatures n (Just RightTopBorder))++lbb :: Int -> Maybe LayerFeatures+lbb n = Just (LayerFeatures n (Just LeftBottomBorder))++rbb :: Int -> Maybe LayerFeatures+rbb n = Just (LayerFeatures n (Just RightBottomBorder))++mid :: Int -> Maybe LayerFeatures+mid n = Just (LayerFeatures n Nothing)++data Border+ = LeftBorder+ | RightBorder+ | TopBorder+ | BottomBorder+ | LeftTopBorder+ | RightTopBorder+ | LeftBottomBorder+ | RightBottomBorder+ deriving (Show, Generic)++instance FromJSON Border++instance ToJSON Border
+ src/Graph/GraphDrawing.hs view
@@ -0,0 +1,1478 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoMonomorphismRestriction #-}+{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}++{-# HLINT ignore "Eta reduce" #-}+{-# HLINT ignore "Use =<<" #-}++module Graph.GraphDrawing where++import qualified Data.IntMap as I+import qualified Data.IntMap.Strict as IM+import Data.List (elemIndex, find, group, groupBy, intercalate, sort, sortBy, sortOn, (\\))+import Data.Map (Map)+import qualified Data.Map as Map+import Data.Maybe (catMaybes, fromMaybe, isJust, isNothing, mapMaybe)+import Data.Set (Set)+import qualified Data.Set as Set+import Data.Tuple (swap)+import qualified Data.Vector.Algorithms.Intro as I+import Data.Vector.Unboxed (Vector)+import qualified Data.Vector.Unboxed as VU+import Data.Word (Word32)+import Debug.Trace (trace)+import Graph.CommonGraph+ ( CGraph,+ CGraphL,+ Channel,+ Column,+ EdgeClass (channelNrIn, channelNrOut, dummyEdge, standard),+ EdgeType (NormalEdge),+ GraphMoveX,+ LayerFeatures (LayerFeatures),+ NodeClass (connectionNode, dummyNode, isConnNode, isDummy, isMainArg, isSubLabel, subLabels),+ UINode,+ childrenNoVertical,+ childrenSeparating,+ childrenVertical,+ isFunction,+ myFromJust,+ myhead,+ parentsNoVertical,+ parentsVertical,+ rmdups,+ verticallyConnectedNodes,+ vhead,+ )+import qualified Graph.CommonGraph as Common+import Graph.IntMap (Graph (..), nodes)+import qualified Graph.IntMap as Graph++-- Also returns a map with Columns to allow navigation with arrows+layeredGraphAndCols ::+ (NodeClass n, Show n, EdgeClass e, Show e) =>+ Bool ->+ CGraph n e ->+ (CGraphL n e, (Map.Map GraphMoveX [UINode], Map.Map Int [Column]))+layeredGraphAndCols cross graph = (g, getColumns g)+ where+ g = layeredGraph cross graph++-- Debug with https://dreampuf.github.io/GraphvizOnline using neato or fdp engine++-- ^ Layout a directed acyclic graph in several steps (Sugiyama)+-- 1. Assign the nodes to several layers (longest path)+-- 2. Dummy vertices for lines that are longer than a layer+-- 3. Reduce crossings, place the longest path on top+-- 4. Assign y-coordinates to the nodes so that long lines that pass several layers are+-- as straight as possible++layeredGraph ::+ (VU.Unbox UINode, NodeClass n, Show n, EdgeClass e, Show e) =>+ Bool ->+ CGraph n e ->+ CGraphL n e+layeredGraph cross graph =+ -- Debug.Trace.trace ("layered "++ show graph ++"\n") -- ++ showEdges graph ++ show (Graph.edgeLabels graph)) $ -- ++"\nnewGraph\n" ++ show newGraph ++"\n") $+ newGraph+ where+ sortLayers (gr, ls) = (gr, map sort ls) -- makes the dummy vertices appear lower+ newGraph =+ ( -- subgraphWindows .+ yCoordinateAssignement+ -- .+ -- primitiveYCoordinateAssignement+ . crossingReduction 1 cross+ . sortLayers+ . addConnectionVertices+ . longestPathAlgo+ . addMissingInputNodes -- does not change the graph, only computes layers+ )+ graph++fr :: (Int, n) -> (UINode, n)+fr (n, nl) = (fromIntegral n, nl)++graphvizNodes :: (CGraph n e, Map.Map Int [Column]) -> String+graphvizNodes (gr, m) = concatMap ((++ "\n") . sh) (I.toList (Graph.nodeLabels gr))+ where+ sh (n, _nl) = show n ++ " [ pos = \"" ++ show (myFromJust 499 $ Map.lookup n m) ++ "!\"]"++primitiveYCoordinateAssignement :: (CGraph n e, [[UINode]]) -> CGraphL n e+primitiveYCoordinateAssignement (graph, layers) =+ -- Debug.Trace.trace ("primitiveY1 "++ show (graph,layers,ns)) $+ (graph, Map.fromList ns)+ where+ ns :: [(UINode, (Int, Int))]+ ns = concat $ zipWith (\layer i -> map (incX i) layer) (map oneLayer layers) ([0 ..] :: [Int])+ oneLayer l = zip (iterate incY (0, 0)) l+ incX i ((x, y), n) = (n, (x - i, y))+ incY (x, y) = (x, y + 1)+{-+primitiveYCoordinateAssignement2 :: (CGraph, [[UINode]]) -> CGraph+primitiveYCoordinateAssignement2 (g, (la:layers)) =+ Debug.Trace.trace ("primitiveY2 "++ show (g, newGraph, ns, la, layers)) $+-- ++ show (reverse $ oneLayer newLa layers)) $+ newGraph+ where+ newGraph = g { nodeLabels = I.fromList $ map fr2 $ map (positionNode g) (concat ns) }+ ns = zipWith (\layer i -> map (incX i) layer) (oneLayer newLa layers) ([0..] :: [Int])+ newLa = zip (iterate incY (0,0)) la+ oneLayer :: [((Int,Int), UINode)] -> [[UINode]] -> [[((Int,Int), UINode)]]+ oneLayer l0 [] = [l0]+ oneLayer l0 (l1:rest) = l0 : (oneLayer newL1 rest)+ where+ newL1 = childYOrInc 0 (-1) l1++ childYOrInc _ _ [] = []+ childYOrInc y lastY (e:es)+ | isJust cy && (fromJust cy) /= lastY =+-- Debug.Trace.trace ("cy " ++ show (fromJust cy) ++" "++ show e ++ " " ++ show lu) $+ ((0,fromJust cy),e) : childYOrInc ((fromJust cy)+1) (fromJust cy) es+ | otherwise =+-- Debug.Trace.trace ("other y "++ show y ++" cy "++ show cy ++" "++ show e) $+ ((0,y),e) : childYOrInc (y+1) (fromMaybe y cy) es+ where cy | VU.null (child e) = Nothing+ | otherwise = fmap snd lu+ lu = lookup (vhead 500 (child e)) (map (\(a,b) -> (b,a)) l0)+ child el = childrenNoVertical g el+ incX i ((x,y),n) = (x-i,y,n)+ incY (x,y) = (x,y+1)+-}+{-+positionNode :: CGraph -> (Int, Int, UINode) -> (UINode, UINodeLabel)+positionNode graph (x,y,n) =+ (n, UINodeLabel { option = maybe NoOption option lu,+ formerNonOption = maybe False formerNonOption lu,+ uinode = maybe (DN (DummyNode 1)) uinode lu,+ nestingFeatures = maybe Nothing nestingFeatures lu,+ verticalNumber = maybe Nothing verticalNumber lu+ })+ where lu = Graph.lookupNode n graph+-}++-- ^ See "Fast and Simple Horizontal Coordinate Assignment" (Brandes, Köpf)++yCoordinateAssignement :: (NodeClass n, EdgeClass e) => (CGraph n e, [[UINode]]) -> CGraphL n e+yCoordinateAssignement (graph, layers) =+ -- Debug.Trace.trace ("\nyCoordAssign "++ show (layers,graph,pos)) $+ (graph, pos)+ where+ -- newGraph = graph { nodeLabels = I.fromList placedNodes } -- for debugging (Map.fromList edgesToKeep)+ pos :: Map UINode (Int, Int)+ pos = horizontalBalancing lu ld ru rd+ lu = biasedAlignment graph yPos medians (reverse nLayers) (True, True)+ ld = biasedAlignment graph yPos medians (reverse nLayers) (True, False)+ ru = biasedAlignment graph yPos medians (reverse nLayers) (False, True)+ rd = biasedAlignment graph yPos medians (reverse nLayers) (False, False)++ -- for debugging+ -- edgesToKeep :: [(Graph.DirEdge UINode, [UIEdge])]+ -- edgesToKeep = map (\(x,y) -> (Graph.DirEdge x y, fromJust (Graph.lookupEdge (Graph.DirEdge x y) graph))) $+ -- concat $ map (sweep medians Map.empty 0 (True,True)) (tuples (reverse nLayers))++ yPos = Map.fromList (concat enumLayers)+ enumLayers = map (\l -> zip l [0 ..]) layers+ nLayers = map (map connProp) layers+ connProp n = (n, isConnNode graph n)++ medians = (Map.fromList lowerMedians, Map.fromList upperMedians)+ upperMedians =+ -- Debug.Trace.trace ("upper"++ show (map upper ns, map (getMedian . upper) ns)) $+ mapMaybe (getMedian . upper) ns+ lowerMedians =+ -- Debug.Trace.trace ("lower"++ show (map lower ns)) $+ mapMaybe (getMedian . lower) ns+ ns = map fr $ I.toList (Graph.nodeLabels graph)+ upper (n, _) = (n, VU.toList (childrenNoVertical graph n))+ lower (n, _) = (n, VU.toList (parentsNoVertical graph n))++ getMedian (n, ns1)+ | l == 0 -- Debug.Trace.trace "get l0" $+ =+ Nothing+ | l == 1 = Just (n, Single rightMedian)+ | even l -- Debug.Trace.trace ("get lmod2"++ show (nodeLbls,ns1,(n, (leftMedian, rightMedian)))) $+ =+ Just (n, UpLowMedian leftMedian rightMedian) -- an even list has two medians+ | otherwise -- Debug.Trace.trace ("get other"++ show (nodeLbls,ns1,(n, (rightMedian, rightMedian)))) $+ =+ Just (n, Middle rightMedian) -- an odd list has only one median+ where+ leftMedian =+ -- Debug.Trace.trace ("median "++ show (n,ns1,nodeLbls,sorted,l)) $+ addConnProp (sorted !! ((l `div` 2) - 1))+ rightMedian = addConnProp (sorted !! (l `div` 2))+ l = length ns1+ sorted = sortBy py nodeLbls+ py (y0, _) (y1, _) = compare y0 y1+ nodeLbls = map (\node -> (fromMaybe 0 (Map.lookup node yPos), node)) ns1+ addConnProp (y, node) = (y, (node, isConnNode graph node))++-- the paper suggest to use an average of four alignments (TODO)+horizontalBalancing :: Map UINode (X, Y) -> Map UINode (X, Y) -> Map UINode (X, Y) -> Map UINode (X, Y) -> Map UINode (X, Y)+horizontalBalancing lu _ld _ru _rd =+ -- Debug.Trace.trace ("horizontalBalancing "++ show (lu,ru)) -- ++"\n"++ show ld ++"\n"++ show average)+ -- lu -- ld ru rd+ lu++-- average = zipWith f lu ru+-- f :: (UINode,(X,Y)) -> (UINode,(X,Y)) -> (UINode,(X,Y))+-- f (n0,(x0,y0)) (n1,(x1,y1)) | n0 /= n1 = error "horizontalBalancing n0 /= n1 "+-- | otherwise = (n0, (x0, (y0+y1) `div` 2 ))++type X = Int++type Y = Int++type YN = (Y, (UINode, Bool))++data MYN+ = Single (Y, (UINode, Bool)) -- no medians because there is only one connection+ | Middle (Y, (UINode, Bool)) -- an odd number of connections has only one median+ | UpLowMedian (Y, (UINode, Bool)) (Y, (UINode, Bool))+ deriving (Eq, Ord, Show) -- an even number of connections has two medians++type Median = Map UINode MYN++biasedAlignment ::+ (NodeClass n, EdgeClass e) =>+ CGraph n e ->+ Map UINode Y ->+ (Median, Median) ->+ [[(UINode, Bool)]] ->+ (Bool, Bool) ->+ Map UINode (X, Y)+biasedAlignment graph _ medians layers dir =+ -- Debug.Trace.trace ("\nbalign"++ show (layers, balign)) $ --edgesToKeep, map sweep2 (tuples layers)) +++ -- "\nunpositioned " ++ show (map removePositioned (map (map fst) layers))) $+ balign+ where+ (left, _up) = dir+ positioned = Map.keys balign+ _removePositioned ns = ns \\ positioned+ -- see with https://dreampuf.github.io/GraphvizOnline/+ balign =+ -- Debug.Trace.trace ("\n\nedgesToKeep "++ show dir ++ "\ndigraph G {" +++ -- (concat $ map line edgesToKeep) ++"\n"++ placeNodes ++ "\n}") -- \n\nmedians "++ show medians) $+ align graph (map (map fst) layers) edgesToKeep dir+ edgesToKeep = rmdups $ concatMap (concatMap resolve . sweep2) (tuples layers)+ _line (from, to) = "\n" ++ show from ++ " -> " ++ show to+ _placeNodes = concat $ concatMap (map placeNode) (zipWith (zip . repeat) [1 ..] (map (zip [1 ..]) layers))+ where+ placeNode :: (X, (Y, (UINode, Bool))) -> String+ placeNode (x, (y, (n, _b))) = show n ++ " [pos=\"" ++ show x ++ "," ++ show (-y) ++ "!\"];\n"+ resolve :: [(MYN, MYN)] -> [(UINode, UINode)]+ resolve ts =+ -- Debug.Trace.trace ("\nresolve "++ show (ts, res))+ res+ where+ res = rmdups $ map toNode (resolveConflicts dir ts)++ _sweep :: ([(UINode, Bool)], [(UINode, Bool)]) -> [[(MYN, MYN)]]+ _sweep (layer0, layer1) =+ -- Debug.Trace.trace ("\nsweep "++ show (dir, layer0, layer1) ++"\n"++ show sfiel)+ -- Debug.Trace.trace ("(l0,l1)\n"++ show (layer0, layer1) ++"\n\n"++ show medians ++"\n\n"++ show sfiel) $+ sfiel+ where+ sfiel = sweepForIndependentEdgeLists graph medians allowedEdges dir (Map.empty, Map.empty) (0, 0) (layer0, layer1) Set.empty+ allowedEdges :: Set.Set (UINode, UINode)+ allowedEdges = Set.fromList (mapMaybe f layer0)+ -- f :: UINode -> (UINode, UINode)+ f (n, _b)+ | isJust lu = Just (n, dest)+ | otherwise = Nothing+ where+ dest = fst $ snd $ getYN (fst dir) (myFromJust 500 lu)+ lu = Map.lookup n (snd medians)++ -- sweeping through a layer to find all edges without separating them into independent lists+ -- maybe slower in some cases, faster in others+ sweep2 :: ([(UINode, Bool)], [(UINode, Bool)]) -> [[(MYN, MYN)]]+ sweep2 (layer0, _layer1) =+ -- Debug.Trace.trace ("sweep2 "++ show (layer0, layer1,es))+ es+ where+ es = [catMaybes (zipWith f [0 ..] layer0)]+ f y (n, b)+ | isJust lu && isValidEdge -- Debug.Trace.trace ("sweep2lu0 "++ show lu) $+ =+ Just (Single (y, (n, b)), myFromJust 501 lu)+ | otherwise -- Debug.Trace.trace ("sweep2lu1 "++ show (n,lu))+ =+ Nothing+ where+ lu = Map.lookup n (snd medians)+ luBack = Map.lookup (fst $ snd $ getYN left $ myFromJust 502 lu) (fst medians)+ isValidEdge =+ -- Debug.Trace.trace ("n,lu,luBack "++ show (n,lu,luBack)) $+ isJust luBack && n == fst (snd $ getYN left $ myFromJust 503 luBack)++toNode :: ((a1, (a2, b1)), (a3, (b2, b3))) -> (a2, b2)+toNode ((_, (n0, _)), (_, (n1, _))) = (n0, n1)++tuples :: [a] -> [(a, a)]+tuples (x : y : xs) = (x, y) : tuples (y : xs)+tuples _ = []++type Insp = (Map Int (MYN, MYN), Map Int (MYN, MYN))++-- | Takes two layers and returns a list of lists of independent edges.+-- A list A of edges is independent of a list B of edges if every edge of A does not intersect or connect any edge of B.+-- This sweeping should save some time because graphs often have edges crossing near to each other.+-- The number of intersections has been reduced in crossingreduction.+-- Because of this we can assume that most edges are quite short and rectangular to layer0 and layer1.+-- A sweep in the parallel direction of the two layers should reduce the number of edges that have to be examined.+-- The overall algorithm (sweep + resolve) should have a runtime more like n*log(n) instead of n²,+-- because we only have to search for conflicts inside of these independent lists.+-- The Brandes-Köpf paper is not explaining very well how they find intersections between two layers.+-- I doubt that the whole algorithm is O(n). It seems more like a quadratic runtime in the worst case.+-- Even finding the number of intersections (without giving back the exact edges that intersect) is O(n log n),+-- See: Simple and Efficient Bilayer Cross Counting by Barth, Mutzel, Jünger+-- or chapter 33 of Cormen: Introduction to algorithms+-- If several edges emanate from a node the algorithm takes (one of the) the median. (e.g. three edges have one median, 4 edges have two)+-- The sweep works by looking at the next node in the two layers, and comparing which node deletes more edges and+-- introduces less new edges from the set of edges to look at. Every edge has a start node (first appearing at its+-- y-position) and an end node. A start node means adding an edge when its source or target node appears in one of+-- the two layers, and the edge disappears when both its nodes have been swept over.+sweepForIndependentEdgeLists ::+ (NodeClass n, EdgeClass e) =>+ CGraph n e ->+ (Median, Median) ->+ Set (UINode, UINode) ->+ (Bool, Bool) ->+ Insp ->+ (Y, Y) ->+ ([(UINode, Bool)], [(UINode, Bool)]) ->+ Set (MYN, MYN) ->+ [[(MYN, MYN)]]+sweepForIndependentEdgeLists graph medians allowedEdges dir inspectionEdges (y0, y1) (layer0, layer1) missingEdges+ | null layer0 && null layer1 = Debug.Trace.trace ("nullnull " ++ show (layer0, layer1)) []+ | y0 >= 10 || y1 >= 10 = Debug.Trace.trace ("1010 " ++ show (y0, y1, layer0, layer1)) []+ | -- node at postion y1 is connected vertically with node at position y1+1+ (length layer1 >= 2) && verticalNode && isFunction graph hl1 =+ -- Debug.Trace.trace "sweep vert node" $+ sweepForIndependentEdgeLists graph medians allowedEdges dir sweepedOver (y0, y1 + 1) (layer0, tl1) Set.empty+ | Map.null sweepedOverFrom && Map.null sweepedOverTo =+ -- Debug.Trace.trace ("res"++ show (y0,y1) ++"\nlayer0 "++ show layer0 ++"\nlayer1 "++ show layer1+ -- ++"\nresEdges "++ show resEdges ++"\nnewInsFrom "++ show newInsFrom ++"\nnewInsTo "+ -- ++ show newInsTo ++"\nsweepedOver "++ show sweepedOver ++"\n") $+ resEdges : (sweepForIndependentEdgeLists graph medians allowedEdges dir sweepedOver (y0 + 1, y1 + 1) (tl0, tl1) Set.empty)+ | Map.size sweepedOverFrom < Map.size sweepedOverTo =+ -- Debug.Trace.trace (show (Map.size sweepedOverFrom)++ "<"++ show (Map.size sweepedOverTo) ++"\n"+++ -- show (y0,y1) ++"\nnewInsFrom "++ show newInsFrom ++"\nnewInsTo "++ show newInsTo+ -- ++"\nsweepedOverFrom "++ show sweepedOverFrom ++"\nsweepedOverTo "++ show sweepedOverTo ++"\n") $+ sweepForIndependentEdgeLists+ graph+ medians+ allowedEdges+ dir+ sweepedOver+ (y0 + 1, y1)+ (tl0, layer1)+ (Set.union missingEdges newMissingEdges)+ | otherwise -- Debug.Trace.trace (show (Map.size sweepedOverFrom)++ ">="++ show (Map.size sweepedOverTo) ++"\n"+++ -- show (y0,y1) ++"\nnewInsFrom "++ show newInsFrom ++"\nnewInsTo "++ show newInsTo+ -- ++"\nsweepedOverFrom "++ show sweepedOverFrom ++"\nsweepedOverTo "++ show sweepedOverTo ++"\n") $+ =+ sweepForIndependentEdgeLists+ graph+ medians+ allowedEdges+ dir+ sweepedOver+ (y0, y1 + 1)+ (layer0, tl1)+ (Set.union missingEdges newMissingEdges)+ where+ (inspectEdgesFrom, inspectEdgesTo) = inspectionEdges+ (lowerMedians, upperMedians) = medians+ (left, _up) = dir+ (n0, b0) = myhead 60 layer0+ (n1, b1) = myhead 61 layer1+ tl0+ | null layer0 = []+ | otherwise = tail layer0+ tl1+ | null layer1 = []+ | otherwise = tail layer1+ hl1 = fst (myhead 62 layer1)+ verticalNode = VU.elem (fst (myhead 63 tl1)) (Graph.adjacentNodesByAttr graph True hl1 (Graph.Edge8 Common.vertBit))+ resEdges = myNub (Map.elems newInsFrom ++ Map.elems newInsTo ++ Set.toList missingEdges)++ edgeFrom :: Maybe MYN+ edgeFrom+ | null layer0 = Nothing+ | otherwise -- Debug.Trace.trace ("up2 "++ show (n0, Map.lookup n0 upperMedians)) $+ =+ Map.lookup n0 upperMedians+ edgeTo :: Maybe MYN+ edgeTo+ | null layer1 -- || (not (Set.member (n0,n1) allowedEdges))+ =+ Nothing+ | otherwise -- Debug.Trace.trace ("up4 "++ show (n1, Map.lookup n1 lowerMedians)) $+ =+ Map.lookup n1 lowerMedians++ newInsFrom :: Map Int (MYN, MYN)+ newInsFrom+ | isJust edgeFrom && yy1 >= y1 = Map.insert yy1 (Single (y0, (n0, b0)), myFromJust 504 edgeFrom) inspectEdgesFrom+ | otherwise = inspectEdgesFrom+ where+ yy1 = getY left (myFromJust 505 edgeFrom)++ newInsTo :: Map Int (MYN, MYN)+ newInsTo+ | isJust edgeTo && yy0 >= y0 = Map.insert yy0 (myFromJust 506 edgeTo, Single (y1, (n1, b1))) inspectEdgesTo+ | otherwise = inspectEdgesTo+ where+ yy0 = getY left (myFromJust 506 edgeTo)++ newMissingEdges :: Set.Set (MYN, MYN)+ newMissingEdges+ | isJust edgeFrom && isJust edgeTo =+ Set.fromList+ [ (Single (y0, (n0, b0)), myFromJust 507 edgeFrom),+ (myFromJust 508 edgeTo, Single (y1, (n1, b1)))+ ]+ | isJust edgeFrom = Set.singleton (Single (y0, (n0, b0)), myFromJust 509 edgeFrom)+ | isJust edgeTo = Set.singleton (myFromJust 510 edgeTo, Single (y1, (n1, b1)))+ | otherwise = Set.empty++ sweepedOverFrom = Map.delete y1 newInsFrom+ sweepedOverTo = Map.delete y0 newInsTo+ sweepedOver = (sweepedOverFrom, sweepedOverTo) :: Insp++-- | Either e0 prevails against all e1s or all e1s prevail against e0+data EdgeTy a = E0Prevails a | E1Prevails a | NoIntersect (a, a) deriving (Eq, Show)++resolveConflicts :: (Bool, Bool) -> [(MYN, MYN)] -> [(YN, YN)]+resolveConflicts (_, _) [] = []+resolveConflicts (left, _) [e] = [toYN left e]+resolveConflicts (left, up) es =+ -- Debug.Trace.trace ("resolveConflicts"++ show (es, resolveConfs (left,up) es 0)) $+ map (toYN left) (resolveConfs (left, up) es 0)++toYN :: Bool -> (MYN, MYN) -> ((Y, (UINode, Bool)), (Y, (UINode, Bool)))+toYN left (n0, n1) = (getYN left n0, getYN left n1)++-- | Compare all edges of a layer with each other. Worst case: O(n²).+-- But n can shrink fast in every round and n is small, because of sweepForIndependentEdgeLists+resolveConfs :: (Bool, Bool) -> [(MYN, MYN)] -> Int -> [(MYN, MYN)]+resolveConfs (_, _) [] _ =+ -- Debug.Trace.trace "ch0 "+ []+resolveConfs (left, up) (e0 : edges) i+ | i > 20 -- Debug.Trace.trace ("ch1 "++ show (e0:edges))+ =+ e0 : edges -- avoid endless loop+ | checkE0 consistent -- Debug.Trace.trace ("checkE0 "++ show (map te (e0:edges)) ++"\n"++ show (map _toEdges2 conflictList) ++"\n") $+ =+ e0 : (resolveConfs (left, up) removeInferiorToE0 (i + 1))+ | checkNoIntersect consistent -- Debug.Trace.trace ("check noIntersect "++ show (map te (e0:edges)) ++"\n"++ show (conflictList, consistent, i) ++ "\n") $+ =+ if null conflictList+ then e0 : edges+ else e0 : (resolveConfs (left, up) edges (i + 1)) -- concat (map toEdges conflictList)+ | otherwise -- Debug.Trace.trace ("checkE1 "++ show (map te (e0:edges)) ++"\n"++ show (conflictList, consistent, i, firstE1, edgesE1First) ++ "\n") $+ =+ resolveConfs (left, up) edgesE1First (i + 1)+ where+ conflictList = map (conflict left e0) edges++ edgesE1First = e1 : (filter (\e -> e /= e0 && e /= e1) (concatMap toEdges conflictList))+ e1 = head (toEdges firstE1)+ firstE1 = myFromJust 511 (find e1Prevails conflictList)++ consistent = isConsistent conflictList+ checkE0 (E0Prevails True) = True+ checkE0 _ = False+ _checkE1 (E1Prevails True) = True+ _checkE1 _ = False+ checkNoIntersect (NoIntersect _) = True+ checkNoIntersect _ = False+ removeInferiorToE0 = rmdups $ concatMap toEdges (filter isNoIntersect conflictList)+ isNoIntersect (NoIntersect _) = True+ isNoIntersect _ = False+ e1Prevails (E1Prevails _) = True+ e1Prevails _ = False+ toEdges (E0Prevails e) = [e]+ toEdges (E1Prevails e) = [e]+ toEdges (NoIntersect (edge0, edge1)) = [edge0, edge1]++ _toEdges2 (E0Prevails (n0, n1)) = [te1 (n0, n1)]+ _toEdges2 (E1Prevails (n0, n1)) = [te1 (n0, n1)]+ _toEdges2 (NoIntersect ((n0, n1), (n2, n3))) = [te1 (n0, n1), te1 (n2, n3)]+ te1 (n0, n1) = (getN n0, getN n1)++-- resolveConfs _ _ _ = Debug.Trace.trace "error resolveConfs " []++-- | The resolveConflicts-algorithm has to be constructed in a consistent way+-- It should be impossible that edge e has priority to edge x (keeping e),+-- and another edge y has priority to edge e (deleting e). It would not be clear if e has to be deleted or not+isConsistent :: [EdgeTy (MYN, MYN)] -> EdgeTy Bool+isConsistent (NoIntersect _ : es) = isConsistent es+isConsistent [] = NoIntersect (True, True) -- will only be called if there is no E0Prevails or E1Prevails+isConsistent ((E0Prevails _) : es) = isAllE0OrNoIntersect es+ where+ isAllE0OrNoIntersect [] = E0Prevails True+ isAllE0OrNoIntersect ((E0Prevails _) : edges) = isAllE0OrNoIntersect edges+ isAllE0OrNoIntersect ((NoIntersect _) : edges) = isAllE0OrNoIntersect edges+ isAllE0OrNoIntersect (_ : _) = E0Prevails False -- not consistent+isConsistent ((E1Prevails _) : es) = isAllE1OrNoIntersect es+ where+ isAllE1OrNoIntersect [] = E1Prevails True+ isAllE1OrNoIntersect ((E1Prevails _) : edges) = isAllE1OrNoIntersect edges+ isAllE1OrNoIntersect ((NoIntersect _) : edges) = isAllE1OrNoIntersect edges+ isAllE1OrNoIntersect (_ : _) = E1Prevails False++conflict :: Bool -> (MYN, MYN) -> (MYN, MYN) -> EdgeTy (MYN, MYN)+conflict left (n0, n1) (n2, n3)+ | isIntersecting -- Debug.Trace.trace ("intersecting "++ show (n0,n1,n2,n3)) $+ =+ cases left (n0, n1) (n2, n3)+ | otherwise = NoIntersect ((n0, n1), (n2, n3))+ where+ isIntersecting -- two segments intersect+ =+ (getY left n0 <= getY left n2 && getY left n1 >= getY left n3)+ || (getY left n0 >= getY left n2 && getY left n1 <= getY left n3)++-- | Given two edges that intersect or connect, which one will prevail?+cases :: Bool -> (MYN, MYN) -> (MYN, MYN) -> EdgeTy (MYN, MYN)+cases left (n0, n1) (n2, n3)+ -- type 2 (one segment consists of two connection nodes and is preferred then)+ | connNode n0 && connNode n1 -- Debug.Trace.trace ("type2 0 "++ show (n0,n1,n2,n3)) $+ =+ E0Prevails (n0, n1)+ | connNode n2 && connNode n3 -- Debug.Trace.trace ("type2 1 "++ show (n0,n1,n2,n3)) $+ =+ E1Prevails (n2, n3)+ | (connNode n0 || connNode n1)+ && (connNode n2 || connNode n3) -- one connection node (type 2)+ =+ if (isMedian n0 || isMedian n1) && isSingle n2 && isSingle n3+ then -- Debug.Trace.trace ("type2 2 "++ show (n0,n1,n2,n3)) $+ E0Prevails (n0, n1)+ else E0Prevails (n2, n3)+ -- type 1 (non-inner segment crosses an inner segment)+ | (connNode n0 || connNode n1)+ && not (connNode n2)+ && not (connNode n3) -- Debug.Trace.trace ("type1 0"++ show (n0,n1,n2,n3)) $+ =+ E0Prevails (n0, n1)+ | (connNode n2 || connNode n3)+ && not (connNode n0)+ && not (connNode n1) -- Debug.Trace.trace ("type1 1"++ show (n0,n1,n2,n3)) $+ =+ E1Prevails (n2, n3)+ -- type 0 (a pair of non-inner segments)+ | not (connNode n0) && not (connNode n1)+ && not (connNode n2)+ && not (connNode n3) -- Debug.Trace.trace ("type0 "++ show (preferE0,n0,n1,n2,n3)) $+ =+ if preferE0+ then E0Prevails (n0, n1)+ else E1Prevails (n2, n3)+ | otherwise = Debug.Trace.trace "cases err" $ E0Prevails (n0, n1) -- correct? just to fix a warning+ where+ connNode (Single (_, (_, b))) = b+ connNode (Middle (_, (_, b))) = b+ connNode (UpLowMedian (_, (_, b0)) (_, (_, b1)))+ | left = b0+ | otherwise = b1+ isMedian (Single _) = False+ isMedian (Middle _) = True+ isMedian (UpLowMedian _n0 _n1) = True+ isSingle (Single _) = True+ isSingle _ = False+ preferE0+ | (isMedian n0 || isMedian n1) && not (isMedian n2) && not (isMedian n3) -- Debug.Trace.trace "p0"+ =+ True+ | (isMedian n2 || isMedian n3) && not (isMedian n0) && not (isMedian n1) -- Debug.Trace.trace "p1"+ =+ False+ | abs (getY left n0 - getY left n1) < abs (getY left n2 - getY left n3) -- Debug.Trace.trace "p2"+ =+ True+ | otherwise -- Debug.Trace.trace "p3"+ =+ False++getYN :: Bool -> MYN -> (Y, (UINode, Bool))+getYN _ (Single (y, (n, b))) = (y, (n, b))+getYN _ (Middle (y, (n, b))) = (y, (n, b))+getYN left (UpLowMedian (y0, (n0, b0)) (y1, (n1, b1)))+ | left = (y0, (n0, b0))+ | otherwise = (y1, (n1, b1))++getY :: Bool -> MYN -> Y+getY _ (Single (y, _)) = y+getY _ (Middle (y, _)) = y+getY left (UpLowMedian (y0, (_n0, _b0)) (y1, (_n1, _b1)))+ | left = y0+ | otherwise = y1++getN :: MYN -> [UINode]+getN (Single (_y, (n, _b))) = [n]+getN (Middle (_y, (n, _b))) = [n]+getN (UpLowMedian (_y0, (n0, _b0)) (_y1, (n1, _b1))) = [n0, n1]++ranksame :: [[UINode]] -> String+ranksame ls = "{ rank=same; " ++ intercalate "," (map show ls) ++ " }\n"++col :: Int -> UINode -> String+col i n = show n ++ " " ++ c (i `mod` 5) ++ "\n"+ where+ c m+ | m == 0 = "[color = red" ++ width+ | m == 1 = "[color = green" ++ width+ | m == 2 = "[color = blue" ++ width+ | m == 3 = "[color = yellow" ++ width+ | m == 4 = "[color = turquoise" ++ width+ c _ = "[color = black" ++ width+ width = ",penwidth=" ++ show (1 + (i `div` 2)) ++ "]"++--------------------------------------------------------------------------------------------------------------------++-- Similar to Brandes-Köpf but without arrays and no placement of blocks+-- The basic algorithm is longest path.+-- debugging can be done with graphviz, also uncomment line 533 in longestPath | otherwise+align :: EdgeClass e => CGraph n e -> [[UINode]] -> [(UINode, UINode)] -> (Bool, Bool) -> Map UINode (Int, Int)+align graph layers edges (_alignLeft, _up) =+ {-Debug.Trace.trace ("\nalign\ndigraph{\n"++ (concat $ map ranksame layers)+ ++ (concat (map ((++ "\n") . (intercalate " -> ") . (map show)) layers))+ ++ (graphviz "[color=red,penwidth=2];" edges)+ ++ (graphviz "" es) ++ "}\n"+ ++ show (startNs, map last (zipWith f [0..] layers))+ ++"\nblocks\n"++ show blocks ++ "\nnextInLayerMap" ++ show nextInLayerMap+ )-}+ mb2+ where+ -- | up = lp+ -- | otherwise = lpBackwards+ -- mb = Debug.Trace.trace ("lp\n" ++ show lp ++ "\nmb\n" ++ show (moveBlocks (Map.fromList lp))) $+ -- moveBlocks (Map.fromList lp)+ mb2 =+ -- Debug.Trace.trace ("lp\n" ++ show lp ++ "\nmb\n" ++ show (moveBlocks (Map.fromList lp), moveBlocksAgain (Map.fromList lp)) ++ "\n") $+ moveBlocksAgain (Map.fromList lp)+ lp = longestPath (map blockChildren startNs) [] 0+ -- globalYMin = minimum (map (snd . snd) lp)+ -- lpBackwards = longestPath (map blockChildren startNsBackwards) [] 0+ layerConnections = Map.fromList $ concatMap tuples layers+ reverseLayerConnections = Map.fromList $ concatMap (tuples . reverse) layers+ edgeMap = Map.fromList edges+ reverseBlocks = Map.fromList (map swap edges)+ _es = Map.keys (Graph.edgeLabels graph) \\ edges++ startNs = mapMaybe (nodeWithoutParent . last) (zipWith f [0 ..] layers)+ -- startNsBackwards = catMaybes $ map (nodeWithoutParent . head) (zipWith f [0..] layers)+ f i ns = map (i,) ns++ nodeWithoutParent (x, n)+ | isNothing (Map.lookup n reverseBlocks)+ && noParentInLayer (x, n) -- no parent in block+ =+ -- Debug.Trace.trace ("nodeWoPar0 "++ show (n, Map.lookup n reverseBlocks, noParentInLayer (x,n))) $+ Just (x, n)+ | otherwise =+ -- Debug.Trace.trace ("nodeWoPar1 "++ show (n, Map.lookup n reverseBlocks, noParentInLayer (x,n))) $+ Nothing+ where+ noParentInLayer root =+ -- Debug.Trace.trace ("noParInLayer "++ show (root, blockChildren root,+ -- map hasNoLayerParent (blockChildren root))) $+ all hasNoLayerParent (blockChildren root)++ hasNoLayerParent (_, _n) = isNothing (Map.lookup n layerConnections)++ blockChildren :: (X, UINode) -> [(X, UINode)]+ blockChildren (x, n)+ | isJust lu = (x, n) : blockChildren (x + 1, myFromJust 513 lu)+ | otherwise = [(x, n)]+ where+ lu = Map.lookup n edgeMap++ longestPath :: [[(X, UINode)]] -> [UINode] -> Int -> [(UINode, (Int, Int))]+ longestPath [] _ _ =+ -- Debug.Trace.trace "finish"+ []+ longestPath blockNodes used i+ | i > 100 -- Debug.Trace.trace ("reverseBlocks " ++ show (edges, reverseBlocks)) $+ =+ []+ | otherwise -- Debug.Trace.trace ((concat $ map (col i) blns) ++ "\n") $+ -- ++ "map layerChild " ++ show (map layerChild (concat blockNodes)) ++ "\n"+ -- ++ "nextLayerRoots " ++ show nextLayerRoots ++ "\n"+ -- ++ "map blockChildren nextLayerRoots " ++ show (map blockChildren nextLayerRoots) ++ "\n"+ -- ++ "blocksWithOnlyUsedParents " ++ show blocksWithOnlyUsedParents ++ "\n"+ -- ++ "newUsed " ++ show newUsed+ -- ) $+ =+ newLayer ++ longestPath blocksWithOnlyUsedParents newUsed (i + 1)+ where+ newLayer = concatMap (oneLayer i) blockNodes+ blocksWithOnlyUsedParents = rmdups $ filter noParentOrUsed (map blockChildren nextLayerRoots)+ -- | otherwise = rmdups $ filter noParentOrUsed (map blockChildren nextLayerRootsBackwards)+ nextLayerRoots = myNub2 (map findRoot nextPossibleLayerNodes)+ -- nextLayerRootsBackwards = myNub2 (map findRoot nextPossibleLayerNodesBackwards)+ nextPossibleLayerNodes = mapMaybe layerChild (concat blockNodes)+ -- nextPossibleLayerNodesBackwards = catMaybes (map layerParent (concat blockNodes))+ layerChild (x, n) = maybe Nothing (\node -> Just (x, node)) (Map.lookup n reverseLayerConnections)+ -- layerParent (x,n) = maybe Nothing (\node -> Just (x,node)) (Map.lookup n layerConnections)+ newUsed = used ++ blns+ blns = map snd (concat blockNodes)+ noParentOrUsed block =+ -- Debug.Trace.trace ("noParentOrUsed "++ show (block, map noParOrUsed block, newUsed)) $+ all noParOrUsed block+ noParOrUsed (_, n) =+ -- Debug.Trace.trace ("noParOrUsed "++ show (n,lu)) $+ isNothing lu || (isJust lu && elem (myFromJust 514 lu) newUsed)+ where+ lu = Map.lookup n layerConnections++ oneLayer :: Y -> [(X, UINode)] -> [(UINode, (Int, Int))]+ oneLayer y ns = map (\(x, n) -> (n, (x, -y))) ns++ findRoot :: (X, UINode) -> (X, UINode)+ findRoot (x, n)+ | isJust lu && x >= 0 -- Debug.Trace.trace ("findRoot " ++ show (x,n)) $+ =+ findRoot (x - 1, myFromJust 515 lu)+ | otherwise = (x, n)+ where+ lu = Map.lookup n reverseBlocks++ blocks = extr ++ (map (\x -> [x]) rest)+ where+ extr = extractBlocks edgeMap+ rest = (concat layers \\ allNodes) \\ concat extr+ allNodes = Map.keys edgeMap ++ Map.elems edgeMap++ extractBlocks :: Map UINode UINode -> [[UINode]]+ extractBlocks m+ | Map.null m = []+ | otherwise = oneBlock ++ extractBlocks newEdgeMap -- extract one block and remove keys from the edge map+ where+ newEdgeMap =+ -- Debug.Trace.trace ("oneBlock " ++ show oneBlock) $+ foldr Map.delete m (concat oneBlock)+ oneBlock =+ filter+ (not . null)+ ( merge1 (map (fst . snd) oneBlockWithVerts)+ ++ [map fst oneBlockWithVerts]+ ++ merge1 (map (snd . snd) oneBlockWithVerts)+ )+ merge1 [] = []+ merge1 xs = (map head fil) : (merge1 (map tail fil))+ where+ fil = filter (not . null) xs+ oneBlockWithVerts =+ -- Debug.Trace.trace ("oneBlock " ++ show (reverse (blockNodesDown (head ks)), tail (blockNodesUp (head ks)))) $+ reverse (blockNodesDown (head ks))+ ++ tail (blockNodesUp (head ks))++ ks = Map.keys m ++ Map.elems m++ blockNodesDown :: UINode -> [(UINode, ([UINode], [UINode]))]+ blockNodesDown n+ | isJust lu = (n, (vertup, vertdown)) : (blockNodesDown (myFromJust 513 lu))+ | otherwise = [(n, (vertup, vertdown))]+ where+ lu = Map.lookup n edgeMap+ vertup = VU.toList (parentsVertical graph n)+ vertdown = VU.toList (childrenVertical graph n)++ blockNodesUp :: UINode -> [(UINode, ([UINode], [UINode]))]+ blockNodesUp n+ | isJust lu = (n, (vertup, vertdown)) : (blockNodesUp (myFromJust 513 lu))+ | otherwise = [(n, (vertup, vertdown))]+ where+ lu = Map.lookup n reverseBlocks+ vertup = VU.toList (parentsVertical graph n)+ vertdown = VU.toList (childrenVertical graph n)++ moveBlocks m =+ -- Debug.Trace.trace ("blocks" ++ show blocks ++ "\nm\n" ++ show (foldr moveToShortestConnection m (reverse blocks)))+ foldr moveToShortestConnection m (reverse blocks)+ moveBlocksAgain m =+ -- Debug.Trace.trace ("blocks" ++ show blocks ++ "\nm\n" ++ show (foldr moveToShortestConnection m (reverse blocks)))+ foldr moveToShortestConnection (moveBlocks m) (reverse blocks)++ moveToShortestConnection block m+ | null bs = m+ | otherwise = -- Debug.Trace.trace ("\nblock " ++ show block +++ -- "\nbounds " ++ show bounds +++ -- "\nnewY " ++ show newY +++ -- "\nadjustY block newY m\n" ++ show (adjustY block newY m))+ adjustY block newY m+ where+ -- newY = ( (fromJust (fst (head bounds))) + (fromJust (snd (head bounds))) ) `div` 2+ bs = mapMaybe fst bounds+ newY = maximum bs + 1 -- TODO look at block connections+ bounds = map blockBound block+ blockBound b =+ -- Debug.Trace.trace ("blockBound " ++ show (b,n,(yTop,yBottom),m))+ (yTop, yBottom)+ where+ -- yTop = fmap snd (maybe (Just (0,globalYMin)) (\node -> Map.lookup node m) n)+ yTop = fmap snd (maybe Nothing (`Map.lookup` m) n)+ yBottom = fmap snd (Map.lookup b m)+ n = Map.lookup b nextInLayerMap++ nextInLayerMap = foldr addLayerEdges Map.empty layers+ where+ addLayerEdges layer m = foldr addEdge m (tuples layer)+ addEdge (from, to) m = Map.insert to from m++ adjustY block newY m = foldr adj m block+ where+ adj b mp = Map.adjust (\(x, _y) -> (x, newY)) b mp++---------------------------------------------------------------------------------------------------------++-- The idea behind the following heuristic:+-- Very frequent chaining of functions are obvious and need no attention, e.g. Data.Text.pack str+-- unusual chainings need the highest attention+-- a long path means it is the main path of activity, like a table of contents in a book that+-- is a guide where to go. This long path should be a straight line at the top of the page.++-- Sort nodes in the layers by:+-- Finding the longest path with the most infrequent connections, make these nodes appear+-- first (y=0) use dfs to find the second longest/infrequent path+-- longestinfrequentPaths :: CGraph -> [[Node]] -> [Node]++type YNode = (YPos, Channel, UINode, IsDummy)++type YPos = Word32++type IsDummy = Bool++data Dir = LeftToRight | RightToLeft deriving (Show)++leftToRight :: Dir -> Bool+leftToRight LeftToRight = True+leftToRight RightToLeft = False++longestinfrequentPaths :: EdgeClass e => NodeClass n => CGraph n e -> [[UINode]] -> Vector Int+longestinfrequentPaths _ [] = VU.empty+longestinfrequentPaths _ [_] = VU.empty+longestinfrequentPaths g (l0 : l1 : layers)+ | null r = VU.empty+ | otherwise = VU.take (length layers + 2) $ myhead 64 r+ where+ r = map (liPaths g (l1 : layers) []) (startNodes g l0 l1)++startNodes :: EdgeClass e => CGraph n e -> [Word32] -> [Word32] -> [Word32]+startNodes g l0 l1 = mapMaybe (nodeWithChildInLayer l1) l0+ where+ nodeWithChildInLayer layer1 node+ | VU.null $+ VU.filter+ (`elem` layer1)+ (childrenNoVertical g node) =+ Nothing+ | otherwise = Just node++liPaths :: EdgeClass e => NodeClass n => CGraph n e -> [[UINode]] -> [UINode] -> UINode -> Vector Int+liPaths _ [] ns node = VU.fromList (map fromIntegral (node : ns))+liPaths g (l0 : layers) ns node = VU.concatMap (liPaths g layers (node : ns)) cs+ where+ cs =+ VU.filter+ -- (\x -> (maybe False (not . isDummyLabel) (Graph.lookupNode x g)) && elem x l0)+ (\x -> not (isDummy g x) && elem x l0)+ (childrenNoVertical g node)++myNub :: Ord a => [a] -> [a]+myNub = map (myhead 65) . group . sort++myNub2 :: [(Int, UINode)] -> [(Int, UINode)]+myNub2 = map (myhead 66) . groupBy nnn . sortBy nn+ where+ nn (_, n0) (_, n1) = compare n0 n1+ nnn (_, n0) (_, n1) = n0 == n1++type UnconnectedChildren = [UINode]++-- | Every graph has a longest path, which is the center of attention for us+-- Return layers of node ids+-- This algorithm is a little bit more complicated because we can connect nodes "vertically",+-- so that they are guaranteed to be all in one vertical layer+-- All nodes before this vertical layer have to be placed in layers before we can proceed+longestPathAlgo :: (NodeClass n, EdgeClass e) => CGraph n e -> (CGraph n e, [[UINode]])+longestPathAlgo g =+ -- Debug.Trace.trace ("\nlongestPathAlgo\n" ++ show (g, newLayers, moveFinalNodesLeftToVert newLayers)) $+ -- Debug.Trace.trace ("\nlongestPathAlgo " ++ show g +++ -- "\nnewLayers" ++ show newLayers +++ -- "\nnodesWithoutChildren" ++ show nodesWithoutChildren +++ -- "\nverticalLayers" ++ show verticalLayers +++ -- "\noptionNodes" ++ show optionNodes +++ -- "\nnodesWithoutChildrenVertLayer" ++ show nodesWithoutChildrenVertLayer +++ -- "\n"++ showEdges g)+ (g, moveFinalNodesLeftToVert (map rmdups newLayers))+ where+ moveFinalNodesLeftToVert :: [[UINode]] -> [[UINode]]+ moveFinalNodesLeftToVert ls =+ -- Debug.Trace.trace ("nodesToMove "++ show (nodesToMove, nodesAndPrevious)) $+ (myhead 67 ls \\ nodesToMove) : (foldr insert (tail ls) nodesAndPrevious)+ where+ nodesToMove+ | length ls < 2 = []+ | otherwise = filter (notEl . VU.toList . parentsNoVertical g) (myhead 68 ls)+ notEl [n] = n `notElem` myhead 69 (tail ls)+ notEl _ = False+ insert (n, p) lays+ | null fpl = lays -- Debug.Trace.trace ("insert "++ show lays ++"\n\n"++ show (add lays (find p lays) n)) $+ | otherwise = add lays (head fpl) n+ where+ fpl = findn p lays+ nodesAndPrevious = zip nodesToMove (map (VU.head . parentsNoVertical g) nodesToMove)+ add list pos n = take (pos - 1) list ++ ((list !! (pos - 1)) ++ [n]) : (drop pos list)+ findn p l = [fst il | il <- zip [0 ..] l, p `elem` snd il]++ newLayers = layersrec (rmdups $ VU.toList nodesWithoutChildrenVertLayer) fil []+ fil = filter (not . null . sel2) verticalLayers+ sel1 (x, _, _) = x+ sel2 (_, y, _) = y+ ns = VU.map fromIntegral (VU.fromList (nodes g))+ nodesWithoutChildren = VU.filter (VU.null . cs) ns+ nodesWithoutChildrenVertLayer :: VU.Vector UINode+ nodesWithoutChildrenVertLayer =+ -- Debug.Trace.trace ("nwcvl "++ show (nodesWithoutChildren, nwcvl))+ nwcvl+ where+ nwcvl = VU.concatMap (findLayers (map sel1 verticalLayers)) nodesWithoutChildren+ findLayers :: [[UINode]] -> UINode -> VU.Vector UINode+ findLayers ls n+ | null ls = VU.singleton n+ | otherwise = VU.fromList (concat (map findL ls))+ where+ findL l+ | elem n l = l+ | otherwise = [n]+ cs node = Graph.children g node [dummyEdge Nothing 0]+ (_, optionNodes) = partitionNodes g ns -- nonOptionNodes+ verticalLayers =+ -- Debug.Trace.trace (show ("verticalLayers", VU.toList optionNodes, vLayers (VU.toList optionNodes))) $+ vLayers (VU.toList optionNodes)++ vLayers [] = []+ vLayers (n : ns1) =+ -- Debug.Trace.trace (show ("vLayers", n, newLayer, addUnconnectedChildren newLayer)) $+ (addUnconnectedChildren newLayer) : (vLayers (ns1 \\ newLayer))+ where+ newLayer = sort $ verticallyConnectedNodes g n+ addUnconnectedChildren :: [UINode] -> ([UINode], UnconnectedChildren, Bool)+ addUnconnectedChildren layer1 = (layer1, myNub $ VU.toList (VU.concat (map nonVertChildren layer1)), False)+ nonVertChildren node = childrenNoVertical g node++ -- the idea of this recursion is to go backwards from the final node and add non-vertical nodes that are fully connected at the input+ -- if there is only a vertical layer possible, add it+ layersrec :: [UINode] -> [([UINode], UnconnectedChildren, Bool)] -> [UINode] -> [[UINode]]+ layersrec curLayer vertLayers usedNodes+ | null curLayer -- Debug.Trace.trace "\n§§1 "+ =+ []+ | length usedNodes + length curLayer > length (nodes g) =+ Debug.Trace.trace+ ("\n§§2 " ++ show (curLayer, length usedNodes, usedNodes, length curLayer, length (nodes g)))+ [curLayer] -- should not happen+ | otherwise {-Debug.Trace.trace ("\n§§3 curLayer "++ show curLayer +++ "\nfullyConnectedVertNodes2 " ++ show fullyConnectedVertNodes2 +++ "\nnewCurLayerOrVert " ++ show newCurLayerOrVert +++ "\nusedNodes " ++ show usedNodes +++ "\nlayerParents curLayer " ++ show (layerParents curLayer) +++ "\nvertLayers " ++ show vertLayers +++ "\nnewVertLayers " ++ show newVertLayers +++ "\nfil" ++ show fil)-} =+ curLayer : (layersrec newCurLayerOrVert filtered (usedNodes ++ curLayer))+ where+ newVertLayers = map adjustConnected vertLayers+ adjustConnected (someLayer, unconnectedChildren, _) =+ -- Debug.Trace.trace ("adjustConnected " ++ show (someLayer, unconnectedChildren, newun, map (isNotMainFunctionArg g) someLayer)) $+ (someLayer, newun, null newun && all (isNotMainFunctionArg g) someLayer)+ where+ newun = unconnectedChildren \\ curLayer++ filtered+ | not (null newCurLayer) -- Debug.Trace.trace ("fil0 "++ show (newVertLayers)) $+ =+ filter (not . changed) newVertLayers+ | not (null fullyConnectedVertNodes2) -- Debug.Trace.trace ("fil1 "++ show (filter (not . isFullyConnected) newVertLayers)) $+ =+ filter (not . isFullyConnected) newVertLayers --remove fully connected vertical layers+ | otherwise -- Debug.Trace.trace ("fil2 "++ show (filter (not . isFullyConnected) newVertLayers)) $+ =+ filter (not . isFullyConnected) newVertLayers --remove fully connected vertical layers+ -- fullyConnectedVertNodes = concat (map fst (filter isFullyConnectedAndNotArg newVertLayers))+ fullyConnectedVertNodes2 = concatMap sel1 (filter isFullyConnected newVertLayers)+ -- isFullyConnectedAndNotArg (someLayer,unconnectedChildren) = Debug.Trace.trace ("isfully "++ show (null unconnectedChildren, map (isMainFunctionArg g) someLayer)) $+ -- null unconnectedChildren &&+ -- not (or (map (isMainFunctionArg g) someLayer))++ isFullyConnected (_someLayer, unconnectedChildren, _) = null unconnectedChildren++ newCurLayer =+ -- Debug.Trace.trace ("curParents"++ show (layerParents curLayer, filter shouldNodeBeAdded (layerParents curLayer))) $+ myNub (filter shouldNodeBeAdded (layerParents curLayer)) ++ concatMap sel1 (filter changed newVertLayers)+ changed (_, _, b) = b+ layerParents l = VU.toList (VU.concatMap (parentsNoVertical g) (VU.fromList l))+ newCurLayerOrVert+ | not (null newCurLayer) -- Debug.Trace.trace "not (null newCurLayer)" $ --prefer normal nodes to vertical nodes+ =+ myNub newCurLayer+ | not (null fullyConnectedVertNodes2) -- Debug.Trace.trace "not (null fullyConnectedVertNodes2)" $ --if no normal nodes are left+ =+ myNub fullyConnectedVertNodes2+ | otherwise = []+ shouldNodeBeAdded :: UINode -> Bool -- have all children been added, then node should be added+ shouldNodeBeAdded node+ | VU.null chs -- Debug.Trace.trace ("should0 "++ show (node, chs, VU.map isChildUsed chs)) $+ =+ False+ | otherwise -- Debug.Trace.trace ("should1 "++ show (node, chs, VU.map isChildUsed chs)) $+ =+ VU.and (VU.map isChildUsed chs)+ && not (isInVertLayer node)+ where+ chs = childrenNoVertical g node+ isChildUsed :: UINode -> Bool+ isChildUsed child = child `elem` (usedNodes ++ curLayer)+ isInVertLayer :: UINode -> Bool+ isInVertLayer n = any (elem n . sel1) vertLayers++-- Some functions don't have an input (e.g. True).+-- But a function without input can only appear directly after a case node+-- That's why we insert a connection node between this case node and the function node+addMissingInputNodes :: (NodeClass n, Show n, EdgeClass e) => CGraph n e -> CGraph n e+addMissingInputNodes graph =+ -- Debug.Trace.trace ("\naddConnectionNodes"++ show (foldl addConnNode graph (map fromIntegral (nodes graph)))) $+ foldl addConnNode graph (map fromIntegral (nodes graph))+ where+ addConnNode :: (NodeClass n, Show n, EdgeClass e) => CGraph n e -> UINode -> CGraph n e+ addConnNode g n+ | VU.null ps = g+ | isFunction graph n =+ -- && isCase graph (vhead 501 ps) -- Debug.Trace.trace ("caseconn"++ show (n, VU.head ps)) $++ insertConnNode g n (vhead 502 ps) Nothing 0+ | otherwise = g+ where+ ps = parentsNoVertical graph n++-- | partition nodes into non-vertically connected nodes and vertically connected nodes+partitionNodes :: EdgeClass e => CGraph n e -> VU.Vector UINode -> (VU.Vector UINode, VU.Vector UINode)+partitionNodes g =+ VU.partition+ ( \n ->+ VU.null (parentsVertical g n)+ && VU.null (childrenVertical g n)+ )++-- coffmanGrahamAlgo :: Graph -> [[Int]]+-- coffmanGrahamAlgo g =++addConnectionVertices :: (NodeClass n, Show n, EdgeClass e, Show e) => (CGraph n e, [[UINode]]) -> (CGraph n e, [[UINode]])+addConnectionVertices (g, ls) =+ -- Debug.Trace.trace ("acv"++ show (ls, addConnectionVs (g,ls))) $+ addConnectionVs (g, ls)++addConnectionVs :: (NodeClass n, Show n, EdgeClass e, Show e) => (CGraph n e, [[UINode]]) -> (CGraph n e, [[UINode]])+addConnectionVs (graph, []) = (graph, [])+addConnectionVs (graph, [l0]) = (graph, [l0])+addConnectionVs (graph, l0 : l1 : layers) = (fst adv, l0 : (snd adv))+ where+ adv = addConnectionVs (newGraph, (newLayer : layers))++ (newGraph, newLayer) = foldl dummyNodeEdge (graph, l1) (zip [(fromIntegral (m + 1)) ..] innerSs)+ m = maximum (nodes graph)++ innerSs = concatMap innerSegments l0+ innerSegments n =+ -- Debug.Trace.trace ("ps"++ show ps) $+ zip3 (repeat n) notInLayerL1Parents chans+ where+ notInLayerL1Parents = VU.toList (VU.filter isNotInLayerL1 ps)+ ps = parentsNoVertical graph n+ isNotInLayerL1 = not . (`elem` l1)+ chans = map (\e -> maybe (Nothing, 0) f e) edges+ f x = (channelNrIn (myhead 71 x), channelNrOut (myhead 72 x))+ edges = map (`lue` n) notInLayerL1Parents+ lue x y = Graph.lookupEdge (x, y) graph++ dummyNodeEdge :: (NodeClass n, Show n, EdgeClass e) => (CGraph n e, [UINode]) -> (UINode, (UINode, UINode, (Maybe Int, Int))) -> (CGraph n e, [UINode])+ dummyNodeEdge (g, l) (v, (from, to, (chanIn, chanOut))) =+ -- Debug.Trace.trace ("dummyNodeEdge"++ show (v,(from,to,chan)))+ (insertConnNode g from to chanIn chanOut, v : l)++insertConnNode :: (NodeClass n, Show n, EdgeClass e) => CGraph n e -> UINode -> UINode -> Maybe Channel -> Channel -> CGraph n e+insertConnNode graph from to chanIn chanOut =+ -- Debug.Trace.trace ("dummyNodeEdge"++ show (to, fromIntegral (m+1), chanIn, 0, chanOut, fromIntegral (m+1), from))+ Graph.deleteEdge (Just True) (to, from) $+ Graph.insertEdge (Just True) (to, fromIntegral (m + 1)) [dummyEdge chanIn 0] $+ Graph.insertEdge+ (Just True)+ (fromIntegral (m + 1), from)+ [dummyEdge Nothing chanOut]+ (Graph.insertNode (fromIntegral (m + 1)) connectionNode graph)+ where+ m = maximum (nodes graph)+ nest+ | isJust lu = Common.nestingFeatures (myFromJust 516 lu)+ | otherwise = Nothing+ lu = Graph.lookupNode from graph+ depth = maybe 1 Common.layer nest++-- UIEdge 2 1 "" Curly "#ff5863" "" i False False]++crossingReduction :: (NodeClass n, Show n, EdgeClass e, Show e) => Int -> Bool -> (CGraph n e, [[UINode]]) -> (CGraph n e, [[UINode]])+crossingReduction i longestP (graph, layers)+ | i > 0 -- Debug.Trace.trace ("crossingReduction\nlayers " ++ show layers +++ -- "\nc "++ show c +++ -- "\nnewlayers "++ show newLayers) $+ =+ crossingReduction (i - 1) longestP (graph, newLayers)+ | otherwise = (graph, layers)+ where+ -- nodes that are at the center of attention+ priorityNodes = VU.toList $ longestinfrequentPaths graph revLayers+ revLayers = reverse (map (map fromIntegral) layers)++ -- c = -- Debug.Trace.trace ("|r ") $ -- ++ show (layers, priorityNodes))+ -- (crossR graph LeftToRight (map (map fromIntegral) layers) priorityNodes longestP)+ -- newLayers = -- Debug.Trace.trace ("|l ") $ -- ++ show (layers, priorityNodes))+ -- map (map fromIntegral)+ -- (reverse (crossR graph RightToLeft (reverse c) (reverse priorityNodes) longestP))++ c =+ -- Debug.Trace.trace ("|l ") $ -- ++ show (layers, priorityNodes))+ reverse (crossR graph RightToLeft (reverse (map (map fromIntegral) layers)) (reverse priorityNodes) longestP)++ newLayers =+ -- Debug.Trace.trace ("|r ") $ -- ++ show (layers, priorityNodes))+ map+ (map fromIntegral)+ (crossR graph LeftToRight c priorityNodes longestP)++crossR :: (NodeClass n, Show n, EdgeClass e, Show e) => CGraph n e -> Dir -> [[Int]] -> [Int] -> Bool -> [[Int]]+crossR _ _ [] _ _ = []+crossR g dir (l0 : l1 : layers) (n0 : n1 : ns) longestP+ | crossings l0Enum bEnum <= crossings l0Enum l1Enum =+ -- Debug.Trace.trace ("a0 " ++ show (dir,l0p, b, l1p, (n0:n1:ns), crossings l0Enum bEnum, crossings l0Enum l1Enum,l0,l1)+ -- ++ "\n " ++ show (nl0,nl1)) $+ l0p : (crossR g dir (bv : layers) (n1 : ns) longestP)+ | otherwise -- map (lv g) $+ -- Debug.Trace.trace ("a1 " ++ show (dir,l0p, b, l1p,l0Enum,l1Enum,bEnum,crossings l0Enum bEnum,crossings l0Enum l1Enum)+ -- ++ "\n " ++ show (nl0,nl1)) $+ =+ l0p : (crossR g dir (l1p : layers) (n1 : ns) longestP)+ where+ nl0 = map fst (lv g l0)+ nl1 = map fst (lv g l1)+ -- isNoVert0 = not (or (map snd (lv g l0)))+ -- isNoVert1 = not (or (map snd (lv g l1)))+ b = barycenter g dir l0 l1 n1+ bv = map fst (lv g b)+ -- m = median g nl0 nl1+ l0p+ | isJust (vertNum n0) || longestP = nl0+ | otherwise = nl0 -- n0 : (delete n0 nl0)+ l1p+ | isJust (vertNum n1) || longestP = nl1+ | otherwise = nl1 -- n1 : (delete n1 nl1)+ getY1 ((_, _, _, _), (y1, chan, _, _)) = (fromIntegral y1) * 10 + chan+ crossings en0 en1 =+ -- Debug.Trace.trace (if nl0 == [9] then "ee " ++ show (lexicographicSort ee) +++ -- show (VU.map getY1 $ lexicographicSort ee) +++ -- show (primitiveInversionCount (VU.map getY1 $ lexifromJustcographicSort ee)) else "") $+ primitiveInversionCount (VU.map getY1 $ lexicographicSort ee)+ where+ ee = VU.fromList (edgesEnum en0 en1 g dir (map fromIntegral nl0))+ l0Enum = IM.fromList (zip nl0 [0 ..])+ l1Enum = IM.fromList (zip nl1 [0 ..])+ bEnum = IM.fromList (zip b [0 ..])+ -- mEnum = IM.fromList (zip m [0..])++ lu n = Graph.lookupNode (fromIntegral n) g+ vertNum n = maybe Nothing Common.verticalNumber (lu n)+crossR _ _ ls ns _ = ls++-- arrange vertical nodes directly below each other,+-- returns Nothing if there are no vertical nodes in this layer+lv :: EdgeClass e => CGraph n e -> [Int] -> [(Int, Bool)]+lv _ [] = []+lv g (l : ls) =+ -- Debug.Trace.trace ("vertConnected "++ show ((l,ls,ls \\ vertConnected),(goUp ps),l,(goDown cs))) $+ vertConnected ++ (lv g (ls \\ (map fst vertConnected)))+ where+ vertConnected :: [(Int, Bool)]+ vertConnected+ | null up && null down = [(l, False)]+ | otherwise = map tr (up ++ [l] ++ down)+ tr ll = (ll, True)+ up = goUp ps+ down = goDown cs+ ps = map fromIntegral $ VU.toList $ parentsVertical g (fromIntegral l)+ goUp :: [Int] -> [Int]+ goUp n+ | null n = []+ | otherwise = goUp (map fromIntegral $ VU.toList $ parentsVertical g (fromIntegral (head n))) ++ [fromIntegral (head n)]++ cs = map fromIntegral $ VU.toList $ childrenVertical g (fromIntegral l)+ goDown :: [Int] -> [Int]+ goDown n+ | null n = []+ | otherwise = (fromIntegral (head n)) : (goDown (map fromIntegral $ VU.toList $ childrenVertical g (fromIntegral (head n))))++-- type YNode = (YPos,Channel,UINode,IsDummy)+edgesEnum :: (NodeClass n, EdgeClass e, Show e) => IM.IntMap UINode -> IM.IntMap UINode -> CGraph n e -> Dir -> [UINode] -> [(YNode, YNode)]+edgesEnum en0 en1 gr dir l0 = catMaybes edges+ where+ edges :: [Maybe (YNode, YNode)]+ edges = map (edge en0 en1) (edgesOfLayer gr l0)+ edge :: IM.IntMap UINode -> IM.IntMap UINode -> (UINode, UINode) -> Maybe (YNode, YNode)+ edge e0 e1 (src, tgt)+ | isNothing s || isNothing t = Nothing+ | otherwise =+ Just+ ( (myFromJust 517 s, chanNr, src, isDummy gr src),+ (myFromJust 518 t, 0, tgt, isDummy gr tgt)+ )+ where+ s = IM.lookup (fromIntegral src) e0+ t = IM.lookup (fromIntegral tgt) e1+ chanNr+ | isJust lu && null (myFromJust 519 lu) = 0+ | isJust lu = channelNrOut (myhead 73 (myFromJust 520 lu))+ | otherwise = 0+ lu = Graph.lookupEdge (tgt, src) gr++ edgesOfLayer :: EdgeClass e => CGraph n e -> [UINode] -> [(UINode, UINode)]+ edgesOfLayer g l = concatMap (adjEdges g) l+ adjEdges :: EdgeClass e => CGraph n e -> Word32 -> [(UINode, UINode)]+ adjEdges g n+ | leftToRight dir = map (n,) (VU.toList (parentsNoVertical g n))+ | otherwise = map (n,) (VU.toList (childrenNoVertical g n))++-- type YPos = Int+-- type YNode = (YPos,Channel,UINode,IsDummy)++isNotMainFunctionArg :: NodeClass n => CGraph n e -> UINode -> Bool+isNotMainFunctionArg g node =+ -- not (maybe False isMainArg (Graph.lookupNode node g))+ not (isMainArg g node)++-- Assign every node in l1 a number thats the barycenter of its neighbours in l0, then sort.+-- If the node is marked as a vertical node with a number, this number has precedence in sorting+barycenter :: (NodeClass n, Show n, EdgeClass e, Show e) => CGraph n e -> Dir -> [Int] -> [Int] -> Int -> [Int]+barycenter g dir l0 l1 _ =+ -- Debug.Trace.trace ("bary " ++ show (map bc l1, sortOn snd (map bc l1))) $+ map fst (sortOn snd (map bc l1))+ where+ bc :: Int -> (Int, Double)+ bc node =+ -- Debug.Trace.trace ("bc" ++ show (dir, node, ps, cs, l0, l1, nodeWeight dir))+ (node, nodeWeight dir)+ where+ lenCs = VU.length cs+ lenPs = VU.length ps+ cs = VU.map fromIntegral (childrenNoVertical g (fromIntegral node))+ ps = VU.map fromIntegral (parentsNoVertical g (fromIntegral node))+ nodeWeight :: Dir -> Double+ nodeWeight LeftToRight+ | isJust vertNum -- Debug.Trace.trace ("bvert lr "++ show vertNum)+ =+ (fromIntegral (myFromJust 521 vertNum)) + (if VU.null cs then 0 else (subPos (VU.head cs)) * 10000)+ | lenCs == 0 =+ -- Debug.Trace.trace "bry -1"+ (-1)+ -- | node == prioNode = -2+ | otherwise =+ -- Debug.Trace.trace ("bsum lr "++ show (VU.map xpos cs)) $+ ((VU.sum (VU.map xpos cs)) / (fromIntegral lenCs)) + (if VU.null cs then 0 else (subPos (VU.head cs)) * 10000)+ nodeWeight RightToLeft+ | isJust vertNum =+ -- Debug.Trace.trace ("bvert rl "++ show vertNum)+ (fromIntegral (myFromJust 522 vertNum)) + (if VU.null cs then 0 else (subPos (VU.head cs)) * 10000)+ | lenPs == 0 -- Debug.Trace.trace "bry -1"+ =+ (-1)+ -- | node == prioNode = -2+ | otherwise -- Debug.Trace.trace ("bsum rl "++ show (VU.map xpos ps)) $+ =+ ((VU.sum (VU.map xpos ps)) / (fromIntegral lenPs)) + (if VU.null cs then 0 else (subPos (VU.head cs)) * 10000)++ lu = Graph.lookupNode (fromIntegral node) g+ vertNum = maybe Nothing Common.verticalNumber lu+ xpos :: Int -> Double+ xpos c =+ -- Debug.Trace.trace (show (c, l0,maybe 0 fromIntegral (elemIndex c l0), subPos c)) $+ (maybe 0 fromIntegral (elemIndex c l0))++ subPos :: Int -> Double+ subPos c =+ -- Debug.Trace.trace (show channel ++ " : " ++ show channels) $+ (fromIntegral channel) / (fromIntegral channels)+ where+ channel = maybe 0 channelNrOut edgeLabel+ channels = maybe 1 nrTypes (Graph.lookupNode (fromIntegral c) g)+ nrTypes x+ | isSubLabel x = subLabels x+ | otherwise = 1+ edgeLabel+ | isNothing (Graph.lookupEdge dEdge g) = Nothing+ | null (myFromJust 523 (Graph.lookupEdge dEdge g)) = Nothing+ | otherwise = fmap (myhead 74) (Graph.lookupEdge dEdge g)+ dEdge = (fromIntegral node, fromIntegral c)++-- Assign every node in l0 a number thats the median of its neighbours in l1, then sort+median :: EdgeClass e => CGraph n e -> [Int] -> [Int] -> [Int]+median g l0 l1 = map fst $ sortOn snd $ map bc l0+ where+ bc :: Int -> (Int, Int)+ bc node = (node, if len == 0 then 0 else m VU.! (len `div` 2))+ where+ len = VU.length cs+ cs :: Vector Int+ cs =+ VU.map+ (\x -> fromMaybe 0 (elemIndex (fromIntegral x) l1))+ (childrenNoVertical g (fromIntegral node))+ m = VU.modify I.sort cs++--TODO: radix sort+--https://hackage.haskell.org/package/uvector-algorithms-0.2/docs/Data-Array-Vector-Algorithms-Radix.html++-- Sort two edges lexicographically after their y-position in the layer+-- An edge has two points, each point has a y-position (e.g. e0y0)+-- and a node number (e.g. e0n0)+lexicographicSort :: Vector (YNode, YNode) -> VU.Vector (YNode, YNode)+lexicographicSort es = VU.modify (I.sortBy lexicographicOrdering) es+ where+ lexicographicOrdering+ ((e0y0, e0n0, _, _), (e0y1, e0n1, _, _))+ ((e1y0, e1n0, _, _), (e1y1, e1n1, _, _))+ | (e0y0 > e1y0)+ || (e0y0 == e1y0 && e0n0 > e1n0)+ || (e0y0 == e1y0 && e0n0 == e1n0 && e0y1 > e1y1)+ || (e0y0 == e1y0 && e0n0 == e1n0 && e0y1 == e1y1 && e0n1 > e1n1) =+ GT+ | e0y0 == e1y0 && e0n0 == e1n0 && e0y1 == e1y1 && e0n1 == e1n1 = EQ+ | otherwise = LT++-- | See: Simple and Efficient Bilayer Cross Counting by Barth, Mutzel, Jünger+primitiveInversionCount :: VU.Vector Int -> Int+primitiveInversionCount xs =+ sum+ [ if (xs VU.! i) > (xs VU.! j) then 1 else 0 | i <- [0 .. ((VU.length xs) - 1)], j <- [i .. ((VU.length xs) - 1)]+ ]++-- where l = VU.length xs++-- Modified merge sort for counting of edge crossings+-- which is the same as counting inversions (see)+-- http://www.geeksforgeeks.org/counting-inversions/++merge :: ([Int], Int) -> ([Int], Int) -> ([Int], Int)+merge ([], _) (ys, inv) = (ys, inv)+merge (xs, inv) ([], _) = (xs, inv)+merge (xs@(x : xt), inv0) (ys@(y : yt), inv1)+ | x <= y = (x : (fst (merge (xt, inv0) (ys, inv1))), inv0 + inv1)+ | otherwise = (y : (fst (merge (xs, inv0) (yt, inv1))), inv0 + inv1 + length xs)++split :: [a] -> ([a], [a])+split (x : y : zs) = let (xs, ys) = split zs in (x : xs, y : ys)+split [x] = ([x], [])+split [] = ([], [])++mergeSort :: ([Int], Int) -> ([Int], Int)+mergeSort ([], _) = ([], 0)+mergeSort ([x], _) = ([x], 0)+mergeSort (xs, _) =+ let (as, bs) = split xs -- num_inv+ in merge (mergeSort (as, 0)) (mergeSort (bs, 0))++-- https://hackage.haskell.org/package/splaytree+-- https://hackage.haskell.org/package/TreeStructures-0.0.1/docs/Data-Tree-Splay.html++fromAdj :: EdgeClass e => Map Word32 nl -> [(Word32, [Word32], [e])] -> Graph nl [e]+fromAdj nodesMap adj = foldl (newNodes nodesMap) Graph.empty adj+ where+ newNodes :: -- (Ord n, VU.Unbox n) =>+ EdgeClass e =>+ Map Word32 nl ->+ Graph nl [e] ->+ (Word32, [Word32], [e]) ->+ Graph nl [e]+ newNodes nm g (n, ns, eLabel) =+ Graph.insertEdges (Just True) edges $+ maybe id (Graph.insertNode (fromIntegral n)) (Map.lookup n nm) $+ Graph.insertNodes lookedUpNodes g+ where+ lookedUpNodes = mapMaybe addLabel ns+ addLabel n1 = fmap (n1,) (Map.lookup n nm)+ edges = zip es edgeLbls+ es = map (n,) ns+ edgeLbls = repeat eLabel++------------------------------------------------------------------------------------------------------------------------------++-- | To be able to jump vertically between nodes in an interactive ui+getColumns :: EdgeClass e => CGraphL n e -> (Map X [UINode], Map.Map Int [Column])+getColumns (gr, m) = (Map.fromList cols, Map.fromList (zip [0 ..] (divideTables cols)))+ where+ cols =+ map+ tupleWithX+ ( ( (map (sortBy sorty))+ . (groupBy groupx)+ . (sortBy sortx)+ )+ (map fromIntegral (Graph.nodes gr))+ )+ tupleWithX :: [UINode] -> (X, [UINode])+ tupleWithX ls = (maybe 0 fst (Map.lookup (myhead 504 ls) m), ls)+ groupx n0 n1 = maybe 0 fst (Map.lookup n0 m) == maybe 0 fst (Map.lookup n1 m)+ sortx n0 n1 = compare (maybe 0 fst (Map.lookup n0 m)) (maybe 0 fst (Map.lookup n1 m))+ sorty n0 n1 = compare (maybe 0 snd (Map.lookup n0 m)) (maybe 0 snd (Map.lookup n1 m))++ -- There can be several graphs on the screen that are connected with separating edges+ divideTables :: [Column] -> [[Column]]+ divideTables [] = []+ divideTables layers = layersWithoutSep : divideTables rest+ where+ (layersWithoutSep, rest) = sumLayers ([], layers)+ sumLayers :: ([Column], [Column]) -> ([Column], [Column])+ sumLayers (s, []) = (s, [])+ sumLayers (s, l : ls)+ | containsSeparatingEdge (snd l) = (s ++ [l], ls)+ | otherwise = sumLayers (s ++ [l], ls)+ containsSeparatingEdge ns = any cs ns+ cs n = VU.length (childrenSeparating gr n) > 0++-- | To be able to jump horizontally between nodes in an interactive ui+getRows :: CGraphL n e -> Map Y [UINode]+getRows (gr, m) =+ Map.fromList $+ map+ tupleWithY+ ( ( (map (sortBy sortx))+ . (groupBy groupy)+ . (sortBy sorty)+ )+ (map fromIntegral (Graph.nodes gr))+ )+ where+ tupleWithY :: [UINode] -> (Y, [UINode])+ tupleWithY ls = (maybe 0 snd (Map.lookup (myhead 504 ls) m), ls)+ groupy n0 n1 = maybe 0 snd (Map.lookup n0 m) == maybe 0 snd (Map.lookup n1 m)+ sortx n0 n1 = compare (maybe 0 fst (Map.lookup n0 m)) (maybe 0 fst (Map.lookup n1 m))+ sorty n0 n1 = compare (maybe 0 snd (Map.lookup n0 m)) (maybe 0 snd (Map.lookup n1 m))
+ src/Graph/SubGraphWindows.hs view
@@ -0,0 +1,241 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoMonomorphismRestriction #-}++module Graph.SubGraphWindows (subgraphWindows, getColumns, getRows) where++import qualified Data.IntMap as I+import Data.List (groupBy, sortBy, (\\))+import Data.Map (Map)+import qualified Data.Map as Map+import Data.Maybe (fromJust, fromMaybe, isJust)+import qualified Data.Vector.Unboxed as VU+import Debug.Trace ( trace )+import Graph.CommonGraph+ ( CGraph,+ CGraphL,+ Column,+ LayerFeatures (..),+ NodeClass (dummyNode, isArgLabel, updateLayer),+ EdgeClass,+ UINode,+ bb,+ childrenSeparating,+ layer,+ lb,+ lbb,+ ltb,+ mid,+ myhead,+ nestingFeatures,+ parentsVertical,+ rb,+ rbb,+ rtb,+ tb,+ )+import Graph.GraphDrawing (getColumns, getRows)+import Graph.IntMap (nodes)+import qualified Graph.IntMap as Graph++data Span = SpanLeftBorder | SpanMiddle | SpanRightBorder | SpanOutside deriving (Show)++data SpanInOut = Outside | Inside++type Layer = Int -- the nesting of the window:+-- 0 -> dummy node+-- 1 -> not part of a window+-- 2 -> first window layer++type X = Int++type Y = Int++type Min = Int++type Max = Int++subgraphWindows :: (EdgeClass e, NodeClass n, Show n, VU.Unbox UINode) => CGraphL n e -> CGraphL n e+subgraphWindows (graph, pos)+ | null ns = (graph, pos)+ | otherwise -- Debug.Trace.trace ("subgraphWindows "++ show (graph,pos,newGraph,normalisedPos) ++"\n") -- ++ -- show newGraph ++"\n"+++ -- show pos ++"\n"++ show (rows,zRows,spans zRows) ++"\n"++ show (fst columns,zColumns, spans zColumns)) $+ =+ (newGraph, normalisedPos)+ where+ newGraph =+ Graph.mapNodeWithKey+ changeNode+ filledGraph++ changeNode :: NodeClass n => I.Key -> n -> n+ changeNode n node = changeLayer nf n node+ where nf = nestingFeatures node++ changeLayer :: NodeClass n => Maybe LayerFeatures -> I.Key -> n -> n+ changeLayer Nothing n node+ | isArgLabel node = updateLayer (changeStyle l defaultFeatures) node+ | otherwise = updateLayer (changeStyle l defaultFeatures) node+ where+ l = highestLayer xy (spans zRows) (spans zColumns)+ xy = fromMaybe (0, 0) (Map.lookup (fromIntegral n) normalisedPos)+ _xy2 = fromMaybe (0, 0) (Map.lookup (fromIntegral root) normalisedPos)+ root = rootOf graph (fromIntegral n)++ changeLayer nestingFeats n node+ | isArgLabel node = updateLayer (changeStyle l (Just ((fromJust nestingFeats) {layer = fst l}))) node+ | otherwise = updateLayer (changeStyle l nestingFeats) node+ where+ l = highestLayer xy (spans zRows) (spans zColumns)+ xy = fromMaybe (0, 0) (Map.lookup (fromIntegral n) normalisedPos)++ rootOf :: EdgeClass e => CGraph n e -> UINode -> UINode+ rootOf gr node+ | VU.null psVert = node+ | otherwise = rootOf gr (VU.head psVert)+ where+ psVert = parentsVertical gr node++ defaultFeatures = Just (LayerFeatures 0 Nothing)++ changeStyle :: (Int, (Span, Span)) -> Maybe LayerFeatures -> Maybe LayerFeatures+ changeStyle (n, (SpanLeftBorder, SpanLeftBorder)) style = maybeReplace style n lbb -- LeftBottomBorder+ changeStyle (n, (SpanLeftBorder, SpanMiddle)) style -- n >= 2 = maybeReplace style n mid+ =+ maybeReplace style n bb -- BottomBorder+ changeStyle (n, (SpanLeftBorder, SpanRightBorder)) style -- n >= 2 = maybeReplace style n mid+ =+ maybeReplace style n rbb -- RightBottomBorder+ changeStyle (n, (SpanMiddle, SpanLeftBorder)) style = maybeReplace style n lb -- LeftBorder+ changeStyle (n, (SpanMiddle, SpanMiddle)) style = maybeReplace style n mid+ changeStyle (n, (SpanMiddle, SpanRightBorder)) style -- n >= 2 = maybeReplace style n mid+ =+ maybeReplace style n rb -- RightBorder+ changeStyle (n, (SpanRightBorder, SpanLeftBorder)) style -- n >= 4 = maybeReplace style n mid+ =+ maybeReplace style n ltb -- LeftTopBorder+ changeStyle (n, (SpanRightBorder, SpanMiddle)) style -- n >= 4 = maybeReplace style n mid+ =+ maybeReplace style n tb -- TopBorder+ changeStyle (n, (SpanRightBorder, SpanRightBorder)) style -- n >= 4 = maybeReplace style n mid+ =+ maybeReplace style n rtb -- RightTopBorder+ changeStyle (_, _) style = style++ maybeReplace :: Maybe LayerFeatures -> Int -> (Int -> Maybe LayerFeatures) -> Maybe LayerFeatures+ maybeReplace (Just (LayerFeatures 0 _)) n lf = lf n+ maybeReplace (Just (LayerFeatures x _)) _ lf = lf x+ maybeReplace _ n lf = Debug.Trace.trace "_" lf n++ filledGraph = Graph.insertNodes (map fr newNodes) graph+ fr (n, nl) = (fromIntegral n, nl)+ normalisedPos = Map.map (\(x, y) -> (x - minX, y - minY)) (Map.union pos newPos)++ newNodes = zipWith dNode [(m + 1) ..] holes+ newPos = Map.fromList (zip (map fromIntegral [(m + 1) ..]) holes)+ dNode n _ = (n, dummyNode 1)+ m = maximum (nodes graph)++ holes :: [(Int, Int)]+ holes = [(x, y) | x <- [minX .. maxX], y <- [minY .. maxY]] \\ nodePositions++ nodePositions = Map.elems pos++ ns = I.toList (Graph.nodeLabels graph)++ minX = minimum (map fst nodePositions)+ minY = minimum (map snd nodePositions)+ maxX = maximum (map fst nodePositions)+ maxY = maximum (map snd nodePositions)++ rows :: Map Y [UINode]+ rows = getRows (filledGraph, normalisedPos)+ columns = getColumns (filledGraph, normalisedPos)++ maxZCoord = maximum $ map (\(_, nl) -> zOfNode nl) ns+ zOfNode nl = maybe 0 layer (nestingFeatures nl)++ zLayers :: [(X, [UINode])] -> [(Layer, [(X, [Layer])])]+ zLayers xs = map getLayer (reverse [1 .. maxZCoord])+ where+ getLayer l = (l, map zOfNodes xs)+ where+ zOfNodes (x, ns1) = (x, map zLayer ns1)+ zLayer n+ | isJust lu && zOfNode (fromJust lu) >= l = l+ | otherwise = 0+ where+ lu = Graph.lookupNode n graph++ zRows :: [(Layer, [(X, [Layer])])]+ zRows = zLayers (Map.toList rows)+ zColumns = zLayers (Map.toList (fst columns))++ spans :: [(Layer, [(X, [Layer])])] -> Map Layer (Map X [(Min, Max)])+ spans ls = Map.fromList (map zSpans ls)+ where+ zSpans (z, rs) = (z, Map.fromList (map rowsColums rs))+ where+ rowsColums (x, layers) = (x, minMax layers 0 0 Outside)++ minMax :: [Layer] -> Int -> Int -> SpanInOut -> [(Min, Max)]+ minMax [] start i Inside = [(start, i - 1)]+ minMax [] _ _ _ = []+ minMax (l : layers) start i Outside+ | l == z = minMax layers i (i + 1) Inside+ | otherwise = minMax layers start (i + 1) Outside+ minMax (l : layers) start i Inside+ | l == z = minMax layers start (i + 1) Inside+ | otherwise = (start, i - 1) : minMax layers start (i + 1) Outside++ highestLayer ::+ (X, Y) ->+ Map Layer (Map X [(Min, Max)]) ->+ Map Layer (Map X [(Min, Max)]) ->+ (Layer, (Span, Span))+ highestLayer (x, y) hrows cols = findFirstWindow maxZCoord+ where+ findFirstWindow 0 = (0, (SpanOutside, SpanOutside))+ findFirstWindow z+ | found c r = (z, (c, r))+ | otherwise = findFirstWindow (z - 1)+ where+ c = maybe SpanOutside (overlappedByNeighbouringSpans (x, y) True) (Map.lookup z cols)+ r = maybe SpanOutside (overlappedByNeighbouringSpans (x, y) False) (Map.lookup z hrows)+ found SpanOutside _ = False+ found _ SpanOutside = False+ found _ _ = True++ -- Is there at least one neighboring row/column that includes the X/Y coordinate in its span+ overlappedByNeighbouringSpans :: (X, Y) -> Bool -> Map X [(Min, Max)] -> Span+ overlappedByNeighbouringSpans (x, y) isColumn nspans+ | isColumn = maybe SpanOutside spanPositionColumn (minmax (goLeft x ++ goRight (x + 1)))+ | otherwise = maybe SpanOutside spanPositionRow (minmax (goLeft y ++ goRight (y + 1)))+ where+ spanPositionColumn (smin, smax)+ | y == smin = SpanRightBorder+ | y == smax = SpanLeftBorder+ | y > smin && y < smax = SpanMiddle+ | otherwise = SpanOutside+ spanPositionRow (smin, smax)+ | x == smin = SpanLeftBorder+ | x == smax = SpanRightBorder+ | x > smin && x < smax = SpanMiddle+ | otherwise = SpanOutside+ minmax xs+ | null xs = Nothing+ | otherwise = Just (minimum (map fst xs), maximum (map snd xs))++ goLeft p+ | null mm = mm+ | otherwise = mm ++ goLeft (p - 1)+ where+ mm = fromMaybe [] (Map.lookup p nspans)++ goRight p+ | null mm = mm+ | otherwise = mm ++ goRight (p + 1)+ where+ mm = fromMaybe [] (Map.lookup p nspans)