diff options
author | Karl Berry <karl@freefriends.org> | 2014-02-27 00:01:50 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2014-02-27 00:01:50 +0000 |
commit | 90ae413a94da014dbf7a6a8ab584f7c668483a5a (patch) | |
tree | 4e0528a2b2fb2004988e7fc804ea2f1354f1f5d5 /Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control | |
parent | 800cc56380d1edfe567a6a66bef13e8950eb2b0c (diff) |
pgf 3.0.0
git-svn-id: svn://tug.org/texlive/trunk@33057 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control')
13 files changed, 4154 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/Anchoring.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/Anchoring.lua new file mode 100644 index 00000000000..e4364d695cd --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/Anchoring.lua @@ -0,0 +1,104 @@ +-- Copyright 2012 by Till Tantau +-- +-- This file may be distributed an/or modified +-- +-- 1. under the LaTeX Project Public License and/or +-- 2. under the GNU Public License +-- +-- See the file doc/generic/pgf/licenses/LICENSE for more information + +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/control/Anchoring.lua,v 1.6 2013/05/23 20:01:25 tantau Exp $ + + + +--- +-- @section subsection {Anchoring a Graph} +-- +-- \label{subsection-library-graphdrawing-anchoring} +-- +-- A graph drawing algorithm must compute positions of the nodes of a +-- graph, but the computed positions are only \emph{relative} (``this +-- node is left of this node, but above that other node''). It is not +-- immediately obvious where the ``the whole graph'' should be placed +-- \emph{absolutely} once all relative positions have been computed. In +-- case that the graph consists of several unconnected components, the +-- situation is even more complicated. +-- +-- The order in which the algorithm layer determines the node at which +-- the graph should be anchored: +-- +-- \begin{enumerate} +-- \item If the |anchor node=|\meta{node name} option given to the graph +-- as a whole, the graph is anchored at \meta{node name}, provided +-- there is a node of this name in the graph. (If there is no node of +-- this name or if it is misspelled, the effect is the same as if this +-- option had not been given at all.) +-- \item Otherwise, if there is a node where the |anchor here| option is +-- specified, the first node with this option set is used. +-- \item Otherwise, if there is a node where the |desired at| option is +-- set (perhaps implicitly through keys like |x|), the first such node +-- is used. +-- \item Finally, in all other cases, the first node is used. +-- \end{enumerate} +-- +-- In the above description, the ``first'' node refers to the node first +-- encountered in the specification of the graph. +-- +-- Once the node has been determined, the graph is shifted so that +-- this node lies at the poistion specified by |anchor at|. +-- +-- @end + + + +local Anchoring = {} + + +-- Namespace +require("pgf.gd.control").Anchoring = Anchoring + + +-- Imports +local Coordinate = require("pgf.gd.model.Coordinate") +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare + + + +--- +declare { + key = "desired at", + type = "coordinate", + documentation_in = "pgf.gd.doc.control.Anchoring" +} + +--- +declare { + key = "anchor node", + type = "string", + documentation_in = "pgf.gd.doc.control.Anchoring" +} + + +--- +declare { + key = "anchor at", + type = "canvas coordinate", + initial = "(0pt,0pt)", + documentation_in = "pgf.gd.doc.control.Anchoring" +} + + +--- +declare { + key = "anchor here", + type = "boolean", + documentation_in = "pgf.gd.doc.control.Anchoring" +} + + + + + +-- Done + +return Anchoring
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/ComponentAlign.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/ComponentAlign.lua new file mode 100644 index 00000000000..f5475227245 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/ComponentAlign.lua @@ -0,0 +1,521 @@ +-- Copyright 2012 by Till Tantau +-- +-- This file may be distributed an/or modified +-- +-- 1. under the LaTeX Project Public License and/or +-- 2. under the GNU Public License +-- +-- See the file doc/generic/pgf/licenses/LICENSE for more information + +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/control/ComponentAlign.lua,v 1.4 2013/05/23 20:01:25 tantau Exp $ + + +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare + + +--- +-- @section subsubsection {Aligning Components} +-- +-- When components are placed next to each from left to right, it +-- is not immediately clear how the components should be aligned +-- vertically. What happens is that in each component a horizontal line is +-- determined and then all components are shifted vertically so that the +-- lines are aligned. There are different strategies for choosing these +-- ``lines'', see the description of the options described later on. +-- When the |component direction| option is used to change the direction +-- in which components are placed, it certainly make no longer sense to +-- talk about ``horizontal'' and ``vertical'' lines. Instead, what +-- actually happens is that the alignment does not consider +-- ``horizontal'' lines, but lines that go in the direction specified by +-- |component direction| and aligns them by moving components along a +-- line that is perpendicular to the line. For these reasons, let us call +-- the line in the component direction the \emph{alignment line} and a +-- line that is perpendicular to it the \emph{shift line}. +-- +-- The first way of specifying through which point of a component the +-- alignment line should get is to use the option |align here|. +-- In many cases, however, you will not wish to specify an alignment node +-- manually in each component. Instead, you will use the +-- |component align| key to specify a \emph{strategy} that should be used to +-- automatically determine such a node. +-- +-- Using a combination of |component direction| and |component align|, +-- numerous different packing strategies can be configured. However, +-- since names like |counterclockwise| are a bit hard to remember and to +-- apply in practice, a number of easier-to-remember keys are predefined +-- that combine an alignment and a direction. +-- +-- @end + + +--- + +declare { + key = "align here", + type = "boolean", + + summary = [[" + When this option is given to a node, this alignment line will go + through the origin of this node. If this option is passed to more + than one node of a component, the node encountered first in the + component is used. + "]], + examples = [[" + \tikz \graph [binary tree layout, nodes={draw}] + { a, b -- c[align here], d -- e[second, align here] -- f }; + "]] +} + +--- + +declare { + key = "component align", + type = "string", + initial = "first node", + + summary = [[" + Specifies a ``strategy'' for the alignment of components. + "]], + documentation = [[" + The following values are permissible: + \begin{itemize} + \item \declare{|first node|} + In each component, the alignment line goes through the center of + the first node of the component encountered during specification + of the component. +\begin{codeexample}[] +\tikz \graph [binary tree layout, nodes={draw}, + component align=first node] + { a, b -- c, d -- e[second] -- f }; +\end{codeexample} + \item \declare{|center|} + + The nodes of the component are projected onto the shift line. The + alignment line is now chosen so that it is exactly in the middle + between the maximum and minimum value that the projected nodes + have on the shift line. +\begin{codeexample}[] +\tikz \graph [binary tree layout, nodes={draw}, + component align=center] + { a, b -- c, d -- e[second] -- f }; +\end{codeexample} +\begin{codeexample}[] +\tikz \graph [binary tree layout, nodes={draw}, + component direction=90, + component align=center] + { a, b -- c, d -- e[second] -- f }; +\end{codeexample} + \item \declare{|counterclockwise|} + + As for |center|, we project the nodes of the component onto the + shift line. The alignment line is now chosen so that it goes + through the center of the node whose center has the highest + projected value. +\begin{codeexample}[] +\tikz \graph [binary tree layout, nodes={draw}, + component align=counterclockwise] + { a, b -- c, d -- e[second] -- f }; +\end{codeexample} +\begin{codeexample}[] +\tikz \graph [binary tree layout, nodes={draw}, + component direction=90, + component align=counterclockwise] + { a, b -- c, d -- e[second] -- f }; +\end{codeexample} + The name |counterclockwise| is intended to indicate that the align + line goes through the node that comes last if we go from the + alignment direction in a counter-clockwise direction. + \item \declare{|clockwise|} + + Works like |counterclockwise|, only in the other direction: +\begin{codeexample}[] +\tikz \graph [binary tree layout, nodes={draw}, + component align=clockwise] + { a, b -- c, d -- e[second] -- f }; +\end{codeexample} +\begin{codeexample}[] +\tikz \graph [binary tree layout, nodes={draw}, + component direction=90, + component align=clockwise] + { a, b -- c, d -- e[second] -- f }; +\end{codeexample} + \item \declare{|counterclockwise bounding box|} + + This method is quite similar to |counterclockwise|, only the + alignment line does not go through the center of the node with a + maximum projected value on the shift line, but through the maximum + value of the projected bounding boxes. For a left-to-right + packing, this means that the components are aligned so that the + bounding boxes of the components are aligned at the top. +\begin{codeexample}[] +\tikz \graph [tree layout, nodes={draw, align=center}, + component sep=0pt, + component align=counterclockwise] + { a, "high\\node" -- b};\quad +\tikz \graph [tree layout, nodes={draw, align=center}, + component sep=0pt, + component align=counterclockwise bounding box] + { a, "high\\node" -- b}; +\end{codeexample} + \item \declare{|clockwise bounding box|} + + Works like |counterclockwise bounding box|. + \end{itemize} + "]] +} + +--- + +declare { + key = "components go right top aligned", + use = { + { key = "component direction", value = 0}, + { key = "component align", value = "counterclockwise"}, + }, + + summary = [[" + Shorthand for |component direction=right| and + |component align=counterclockwise|. This means that, as the name + suggest, the components will be placed left-to-right and they are + aligned such that their top nodes are in a line. + "]], + examples = [[" + \tikz \graph [tree layout, nodes={draw, align=center}, + components go right top aligned] + { a, "high\\node" -- b}; + "]] +} + +--- + +declare { + key = "components go right absolute top aligned", + use = { + { key = "component direction", value=0}, + { key = "component align", value = "counterclockwise bounding box"}, + }, + + summary = [[" + Like |components go right top aligned|, but with + |component align| set to |counterclockwise| |bounding box|. This means that the + components will be aligned with their bounding boxed being + top-aligned. + "]], + examples = [[" + \tikz \graph [tree layout, nodes={draw, align=center}, + components go right absolute top aligned] + { a, "high\\node" -- b}; + "]] +} + +--- + +declare { + key = "components go right bottom aligned", + use = { + { key = "component direction", value=0}, + { key = "component align", value = "clockwise"}, + }, + + summary = "See the other |components go ...| keys." +} + +--- +-- + +declare { + key = "components go right absolute bottom aligned", + use = { + { key = "component direction", value=0}, + { key = "component align", value = "clockwise bounding box"}, + }, + + summary = "See the other |components go ...| keys." +} + + +--- + +declare { + key = "components go right center aligned", + use = { + { key = "component direction", value=0}, + { key = "component align", value = "center"}, + }, + + summary = "See the other |components go ...| keys." +} + + +--- + +declare { + key = "components go right", + use = { + { key = "component direction", value=0}, + { key = "component align", value = "first node"}, + }, + + summary = [[" + Shorthand for |component direction=right| and + |component align=first node|. + "]] + } + + +--- + +declare { + key = "components go left top aligned", + use = { + { key = "component direction", value=180}, + { key = "component align", value = "clockwise"}, + }, + + summary = "See the other |components go ...| keys.", + + examples = [[" + \tikz \graph [tree layout, nodes={draw, align=center}, + components go left top aligned] + { a, "high\\node" -- b}; + "]] +} + +--- +-- + +declare { + key = "components go left absolute top aligned", + use = { + { key = "component direction", value=180}, + { key = "component align", value = "clockwise bounding box"}, + }, + + summary = "See the other |components go ...| keys." +} + + +--- +-- + +declare { + key = "components go left bottom aligned", + use = { + { key = "component direction", value=180}, + { key = "component align", value = "counterclockwise"}, + }, + + summary = "See the other |components go ...| keys." +} + + +--- +-- + +declare { + key = "components go left absolute bottom aligned", + use = { + { key = "component direction", value=180}, + { key = "component align", value = "counterclockwise bounding box"}, + }, + + summary = "See the other |components go ...| keys." +} + + +--- +-- + +declare { + key = "components go left center aligned", + use = { + { key = "component direction", value=180}, + { key = "component align", value = "center"}, + }, + summary = "See the other |components go ...| keys." +} + + +--- +-- + +declare { + key = "components go left", + use = { + { key = "component direction", value=180}, + { key = "component align", value = "first node"}, + }, + summary = "See the other |components go ...| keys." +} + + + +--- + +declare { + key = "components go down right aligned", + use = { + { key = "component direction", value=270}, + { key = "component align", value = "counterclockwise"}, + }, + summary = "See the other |components go ...| keys.", + + examples = { + [[" + \tikz \graph [tree layout, nodes={draw, align=center}, + components go down left aligned] + { a, hello -- {world,s} }; + "]], + [[" + \tikz \graph [tree layout, nodes={draw, align=center}, + components go up absolute left aligned] + { a, hello -- {world,s}}; + "]] + } +} + +--- +-- + +declare { + key = "components go down absolute right aligned", + use = { + { key = "component direction", value=270}, + { key = "component align", value = "counterclockwise bounding box"}, + }, + summary = "See the other |components go ...| keys." +} + + +--- +-- + +declare { + key = "components go down left aligned", + use = { + { key = "component direction", value=270}, + { key = "component align", value = "clockwise"}, + }, + summary = "See the other |components go ...| keys." +} + + +--- +-- + +declare { + key = "components go down absolute left aligned", + use = { + { key = "component direction", value=270}, + { key = "component align", value = "clockwise bounding box"}, + }, + summary = "See the other |components go ...| keys." +} + + +--- +-- + +declare { + key = "components go down center aligned", + use = { + { key = "component direction", value=270}, + { key = "component align", value = "center"}, + }, + summary = "See the other |components go ...| keys." +} + + +--- +-- + +declare { + key = "components go down", + use = { + { key = "component direction", value=270}, + { key = "component align", value = "first node"}, + }, + summary = "See the other |components go ...| keys." +} + +--- +-- + +declare { + key = "components go up right aligned", + use = { + { key = "component direction", value=90}, + { key = "component align", value = "clockwise"}, + }, + summary = "See the other |components go ...| keys." +} + + +--- +-- + +declare { + key = "components go up absolute right aligned", + use = { + { key = "component direction", value=90}, + { key = "component align", value = "clockwise bounding box"}, + }, + summary = "See the other |components go ...| keys." +} + + +--- +-- + +declare { + key = "components go up left aligned", + use = { + { key = "component direction", value=90}, + { key = "component align", value = "counterclockwise"}, + }, + summary = "See the other |components go ...| keys." +} + + +--- +-- + +declare { + key = "components go up absolute left aligned", + use = { + { key = "component direction", value=90}, + { key = "component align", value = "counterclockwise bounding box"}, + }, + summary = "See the other |components go ...| keys." +} + + +--- +-- + +declare { + key = "components go up center aligned", + use = { + { key = "component direction", value=90}, + { key = "component align", value = "center"}, + }, + summary = "See the other |components go ...| keys." +} + + +--- +-- + +declare { + key = "components go up", + use = { + { key = "component direction", value=90}, + { key = "component align", value = "first node"}, + }, + summary = "See the other |components go ...| keys." +} + + + + +return Components
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/ComponentDirection.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/ComponentDirection.lua new file mode 100644 index 00000000000..39df30ab248 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/ComponentDirection.lua @@ -0,0 +1,61 @@ +-- Copyright 2012 by Till Tantau +-- +-- This file may be distributed an/or modified +-- +-- 1. under the LaTeX Project Public License and/or +-- 2. under the GNU Public License +-- +-- See the file doc/generic/pgf/licenses/LICENSE for more information + +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/control/ComponentDirection.lua,v 1.2 2013/05/23 20:01:25 tantau Exp $ + + +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare + + +--- +-- @section subsubsection {Arranging Components in a Certain Direction} +-- +-- @end + +--- + +declare { + key = "component direction", + type = "direction", + initial = "0", + + summary = [[" + The \meta{angle} is used to determine the relative position of each + component relative to the previous one. The direction need not be a + multiple of |90|. As usual, you can use texts like |up| or + |right| instead of a number. + "]], + documentation = [[" + As the examples show, the direction only has an influence on the + relative positions of the components, not on the direction of growth + inside the components. In particular, the components are not rotated + by this option in any way. You can use the |grow| option or |orient| + options to orient individual components. + "]], + examples = { + [[" + \tikz \graph [tree layout, nodes={inner sep=1pt,draw,circle}, + component direction=left] + { a, b, c -- d -- e, f -- g }; + "]], + [[" + \tikz \graph [tree layout, nodes={inner sep=1pt,draw,circle}, + component direction=10] + { a, b, c -- d -- e, f -- g }; + "]], + [[" + \tikz \graph [tree layout, nodes={inner sep=1pt,draw,circle}, + component direction=up] + { a, b, c [grow=right] -- d -- e, f[grow=45] -- g }; + "]] + } +} + + +return Components
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/ComponentDistance.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/ComponentDistance.lua new file mode 100644 index 00000000000..30f5630ce52 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/ComponentDistance.lua @@ -0,0 +1,102 @@ +-- Copyright 2012 by Till Tantau +-- +-- This file may be distributed an/or modified +-- +-- 1. under the LaTeX Project Public License and/or +-- 2. under the GNU Public License +-- +-- See the file doc/generic/pgf/licenses/LICENSE for more information + +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/control/ComponentDistance.lua,v 1.2 2013/05/23 20:01:25 tantau Exp $ + + +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare + + +--- +-- @section subsubsection {The Distance Between Components} +-- +-- Once the components of a graph have been oriented, sorted, aligned, +-- and a direction has been chosen, it remains to determine the distance +-- between adjacent components. Two methods are available for computing +-- this distance, as specified by the following option: +-- +-- @end + + +--- + +declare { + key = "component packing", + type = "string", + initial = "skyline", + + documentation = [[" + Given two components, their distance is computed as follows in + depencende of \meta{method}: + \begin{itemize} + \item \declare{|rectangular|} + + Imagine a bounding box to be drawn around both components. They + are then shifted such that the padding (separating distance) + between the two boxes is the current value of |component sep|. + \begin{codeexample}[] + \tikz \graph [tree layout, nodes={draw}, component sep=0pt, + component packing=rectangular] + { a -- long text, longer text -- b}; + \end{codeexample} + \item \declare{|skyline|} + + The ``skyline method'' is used to compute the distance. It works + as follows: For simplicity, assume that the component direction is + right (other case work similary, only everything is + rotated). Imaging the second component to be placed far right + beyond the first component. Now start moving the second component + back to the left until one of the nodes of the second component + touches a node of the first component, and stop. Again, the + padding |component sep| can be used to avoid the nodes actually + touching each other. + + \begin{codeexample}[] + \tikz \graph [tree layout, nodes={draw}, component sep=0pt, + level distance=1.5cm, + component packing=skyline] + { a -- long text, longer text -- b}; + \end{codeexample} + + In order to avoid nodes of the second component ``passing through + a hole in the first component,'' the actual algorithm is a bit + more complicated: For both components, a ``skyline'' is + computed. For the first component, consider an arbitrary + horizontal line. If there are one or more nodes on this line, the + rightmost point on any of the bounding boxes of these nodes will + be the point on the skyline of the first component for this + line. Similarly, for the second component, for each horizontal + level the skyline is given by the leftmost point on any of the + bounding boxes intersecting the line. + + Now, the interesting case are horizontal lines that do not + intersect any of the nodes of the first and/or second + component. Such lines represent ``holes'' in the skyline. For + them, the following rule is used: Move the horizontal line upward + and downward as little as possible until a height is reached where + there is a skyline defined. Then the skyline position on the + original horizontal line is the skyline position at the reached + line, minus (or, for the second component, plus) the distance by + which the line was moved. This means that the holes are ``filled + up by slanted roofs.'' + \begin{codeexample}[] + \begin{tikzpicture} + \graph [tree layout, nodes={draw}, component sep=0pt, + component packing=skyline] + { a -- long text, longer text -- b}; + \draw[red] (long text.north east) -- ++(north west:1cm); + \end{tikzpicture} + \end{codeexample} + + \end{itemize} + "]] +} + + +return Components
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/ComponentOrder.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/ComponentOrder.lua new file mode 100644 index 00000000000..63a8896e8a4 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/ComponentOrder.lua @@ -0,0 +1,107 @@ +-- Copyright 2012 by Till Tantau +-- +-- This file may be distributed an/or modified +-- +-- 1. under the LaTeX Project Public License and/or +-- 2. under the GNU Public License +-- +-- See the file doc/generic/pgf/licenses/LICENSE for more information + +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/control/ComponentOrder.lua,v 1.3 2013/05/23 20:01:25 tantau Exp $ + + +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare + + + + +--- +-- @section subsubsection {Ordering the Components} +-- +-- The different connected components of the graph are collected in a +-- list. The ordering of the nodes in this list can be configured using +-- the following key. +-- +-- @end + + +--- + +declare { + key = "component order", + type = "string", + initial = "by first specified node", + + summary = [[" + Selects a ``strategy'' for ordering the components. By default, + they are ordered in the way they appear in the input. + "]], + documentation = [[" + The following values are permissible for \meta{strategy} + \begin{itemize} + \item \declare{|by first specified node|} + + The components are ordered ``in the way they appear in the input + specification of the graph''. More precisely, for each component + consider the node that is first encountered in the description + of the graph. Order the components in the same way as these nodes + appear in the graph description. + + \item \declare{|increasing node number|} + + The components are ordered by increasing number of nodes. For + components with the same number of nodes, the first node in each + component is considered and they are ordered according to the + sequence in which these nodes appear in the input. + + \item \declare{|decreasing node number|} + As above, but in decreasing order. + \end{itemize} + "]], + examples = { + [[" + \tikz \graph [tree layout, nodes={inner sep=1pt,draw,circle}, + component order=by first specified node] + { a, b, c, f -- g, c -- d -- e }; + "]], + [[" + \tikz \graph [tree layout, nodes={inner sep=1pt,draw,circle}, + component order=increasing node number] + { a, b, c -- d -- e, f -- g }; + "]] + } +} + + +--- + +declare { + key = "small components first", + use = { + { key = "component order", value = "increasing node number" } + }, + + summary = [[" + A shorthand for |component order=increasing node number|. + "]] + } + +--- + +declare { + key = "large components first", + use = { + { key = "component order", value = "decreasing node number" }, + }, + summary = [[" + A shorthand for |component order=decreasing node number|. + "]], + examples = [[" + \tikz \graph [tree layout, nodes={inner sep=1pt,draw,circle}, + large components first] + { a, b, c -- d -- e, f -- g }; + "]] +} + + +return Components
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/Components.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/Components.lua new file mode 100644 index 00000000000..0d1d2b1bf48 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/Components.lua @@ -0,0 +1,129 @@ +-- Copyright 2012 by Till Tantau +-- +-- This file may be distributed an/or modified +-- +-- 1. under the LaTeX Project Public License and/or +-- 2. under the GNU Public License +-- +-- See the file doc/generic/pgf/licenses/LICENSE for more information + +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/control/Components.lua,v 1.3 2013/05/23 20:01:25 tantau Exp $ + + +-- Imports + +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare + + +--- +-- @section subsection {Packing of Connected Components} +-- +-- \label{subsection-gd-component-packing} +-- \label{section-gd-packing} +-- +-- Graphs may be composed of subgraphs or \emph{components} that are not +-- connected to each other. In order to draw these nicely, most graph +-- drawing algorithms split them into separate graphs, compute +-- their layouts with the same graph drawing algorithm independently and, +-- in a postprocessing step, arrange them next to each other. Note, +-- however, that some graph drawing algorithms can also arrange the nodes +-- of the graph in a uniform way even for unconnected components (the +-- |simple necklace layout| is a case in point); for such algorithms you can +-- choose whether they should be applied to each component individually +-- or not (if not, the following options do not apply). To configure +-- which is the case, use the |componentwise| key. +-- +-- The default method for placing the different components works as +-- follows: +-- +-- \begin{enumerate} +-- \item For each component, a layout is determined and the component is +-- oriented as described +-- Section~\ref{subsection-library-graphdrawing-standard-orientation} +-- on the orientation of graphs. +-- \item The components are sorted as prescribed by the +-- |component order| key. +-- \item The first component is now placed (conceptually) at the +-- origin. (The final position of this and all other components will be +-- determined later, namely in the anchoring phase, but let us imagine +-- that the first component lies at the origin at this point.) +-- \item The second component is now positioned relative to the first +-- component. The ``direction'' in which the next component is placed +-- relative to the first one is determined by the |component direction| +-- key, so components can be placed from left to right or up to down or +-- in any other direction (even something like $30^\circ$). However, +-- both internally and in the following description, we assume that the +-- components are placed from left to right; other directions are +-- achieved by doing some (clever) rotating of the arrangement achieved +-- in this way. +-- +-- So, we now wish to place the second component to the right of the +-- first component. The component is first shifted vertically according +-- to some alignment strategy. For instance, it can be shifted so that +-- the topmost node of the first component and the topmost node of the +-- second component have the same vertical position. Alternatively, we +-- might require that certain ``alignment nodes'' in both components +-- have the same vertical position. There are several other strategies, +-- which can be configured using the |component align| key. +-- +-- One the vertical position has been fixed, the horizontal position is +-- computed. Here, two different strategies are available: First, image +-- rectangular bounding boxed to be drawn around both components. Then +-- we shift the second component such that the right border of the +-- bounding box of the first component touches the left border of the +-- bounding box of the second component. Instead of having the bounding +-- boxes ``touch,'' we can also have a padding of |component sep| +-- between them. The second strategy is more involved and also known as +-- a ``skyline'' strategy, where (roughly) the components are +-- ``moved together as near as possible so that nodes do not touch''. +-- \item +-- After the second component has been placed, the third component is +-- considered and positioned relative to the second one, and so on. +-- \item +-- At the end, as hinted at earlier, the whole arrangement is rotate so +-- that instead of ``going right'' the component go in the direction of +-- |component direction|. Note, however, that this rotation applies only +-- to the ``shift'' of the components; the components themselves are +-- not rotated. Fortunately, this whole rotation process happens in the +-- background and the result is normally exactly what you would expect. +-- \end{enumerate} +-- +-- In the following, we go over the different keys that can be used to +-- configure the component packing. +-- +-- @end + + +--- + +declare { + key = "componentwise", + type = "boolean", + + summary = [[" + For algorithms that also support drawing unconnected graphs, use + this key to enforce that the components of the graph are, + nevertheless, laid out individually. For algorithms that do not + support laying out unconnected graphs, this option has no effect; + rather it works as if this option were always set. + "]], + examples = { + [[" + \tikz \graph [simple necklace layout] + { + a -- b -- c -- d -- a, + 1 -- 2 -- 3 -- 1 + }; + "]], + [[", + \tikz \graph [simple necklace layout, componentwise] + { + a -- b -- c -- d -- a, + 1 -- 2 -- 3 -- 1 + }; + "]] + } +} + + + diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/Distances.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/Distances.lua new file mode 100644 index 00000000000..046940d84f3 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/Distances.lua @@ -0,0 +1,461 @@ +-- Copyright 2012 by Till Tantau +-- +-- This file may be distributed an/or modified +-- +-- 1. under the LaTeX Project Public License and/or +-- 2. under the GNU Public License +-- +-- See the file doc/generic/pgf/licenses/LICENSE for more information + +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/control/Distances.lua,v 1.5 2013/05/23 20:01:25 tantau Exp $ + + +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare +local lib = require "pgf.gd.lib" + +--- +-- @section subsection {Padding and Node Distances} +-- +-- \label{subsection-gd-dist-pad} +-- +-- In many drawings, you may wish to specify how ``near'' two nodes should +-- be placed by a graph drawing algorithm. Naturally, this depends +-- strongly on the specifics of the algorithm, but there are a number of +-- general keys that will be used by many algorithms. +-- +-- There are different kinds of objects for which you can specify +-- distances and paddings: +-- +-- \begin{itemize} +-- \item You specify the ``natural'' distance between nodes +-- connected by an edge using |node distance|, which is also available in +-- normal \tikzname\ albeit for a slightly different purpose. However, +-- not every algorithm will (or can) honour the key; see the description +-- of each algorithm what it will ``make of this option''. +-- \item A number of graph drawing algorithms arrange nodes in layers +-- (or levels); we refer +-- to the nodes on the same layer as siblings (although, in a tree, +-- siblings are only nodes with the same parent; nevertheless we use +-- ``sibling'' loosely also for nodes that are more like ``cousins''). +-- \item When a graph consists of several connected component, many graph +-- drawing algorithms will layout these components individually. The +-- different components will then be arranged next to each other, see +-- Section~\ref{section-gd-packing} for the details, such that between +-- the nodes of any two components a padding is avaiable. +-- \end{itemize} +-- +-- @end + + + + +--- + +declare { + key = "node distance", + type = "length", + initial = "1cm", + + summary = [[" + This is minimum distance that the centers of nodes connected by an + edge should have. It will not always be possible to satisfy this + desired distance, for instance in case the nodes are too big. In + this case, the \meta{length} is just considered as a lower bound. + "]], + examples = [[" + \begin{tikzpicture} + \graph [simple necklace layout, node distance=1cm, node sep=0pt, + nodes={draw,circle,as=.}] + { + 1 -- 2 [minimum size=2cm] -- 3 -- + 4 -- 5 -- 6 -- 7 --[orient=up] 8 + }; + \draw [red,|-|] (1.center) -- ++(0:1cm); + \draw [red,|-|] (5.center) -- ++(180:1cm); + \end{tikzpicture} + "]] +} + + +--- + +declare { + key = "node pre sep", + type = "length", + initial = ".333em", + + summary = [[" + This is a minimum ``padding'' or ``separation'' between the border + of nodes connected by an edge. Thus, if nodes are so big that nodes + with a distance of |node distance| would overlap (or + just come with \meta{dimension} distance of one another), their + distance is enlarged so that this distance is still satisfied. + The |pre| means that the padding is added to the node ``at the + front.'' This make sense only for some algorithms, like for a + simple necklace layout. + "]], + examples = { + [[" + \tikz \graph [simple necklace layout, node distance=0cm, nodes={circle,draw}] + { 1--2--3--4--5--1 }; + "]], + [[" + \tikz \graph [simple necklace layout, node distance=0cm, node sep=0mm, + nodes={circle,draw}] + { 1--2--3[node pre sep=5mm]--4--5[node pre sep=1mm]--1 }; + "]] + } +} + +--- + +declare { + key = "node post sep", + type = "length", + initial = ".333em", + + summary = [[" + Works like |node pre sep|. + "]] +} + + + +--- +-- @param length A length. + +declare { + key = "node sep", + type = "length", + use = { + { key = "node pre sep", value = function(v) return v/2 end }, + { key = "node post sep", value = function(v) return v/2 end }, + }, + summary = [[" + A shorthand for setting both |node pre sep| and |node post sep| to + $\meta{length}/2$. + "]] +} + + +--- + +declare { + key = "level distance", + type = "length", + initial = "1cm", + + summary = [[" + This is minimum distance that the centers of nodes on one + level should have from the centers of nodes on the next level. It + will not always be possible to satisfy this desired distance, for + instance in case the nodes are too big. In this case, the + \meta{length} is just considered as a lower bound. + "]], + examples = [[" + \begin{tikzpicture}[inner sep=2pt] + \draw [help lines] (0,0) grid (3.5,2); + \graph [layered layout, level distance=1cm, level sep=0] + { 1 [x=1,y=2] -- 2 -- 3 -- 1 }; + \graph [layered layout, level distance=5mm, level sep=0] + { 1 [x=3,y=2] -- 2 -- 3 -- 1, 3 -- {4,5} -- 6 -- 3 }; + \end{tikzpicture} + "]] +} + +--- +declare { + key = "layer distance", + type = "length", + use = { + { key = "level distance", value = lib.id }, + }, + summary = "An alias for |level distance|" +} + +--- +declare { + key = "level pre sep", + type = "length", + initial = ".333em", + + summary = [[" + This is a minimum ``padding'' or ``separation'' between the border + of the nodes on a level to any nodes on the previous level. Thus, if + nodes are so big that nodes on consecutive levels would overlap (or + just come with \meta{lrngth} distance of one another), their + distance is enlarged so that this distance is still satisfied. + If a node on the previous level also has a |level post sep|, this + post padding and the \meta{dimension} add up. Thus, these keys + behave like the ``padding'' keys rather + than the ``margin'' key of cascading style sheets. + "]], + examples = [[" + \begin{tikzpicture}[inner sep=2pt, level sep=0pt, sibling distance=0pt] + \draw [help lines] (0,0) grid (3.5,2); + \graph [layered layout, level distance=0cm, nodes=draw] + { 1 [x=1,y=2] -- {2,3[level pre sep=1mm],4[level pre sep=5mm]} -- 5 }; + \graph [layered layout, level distance=0cm, nodes=draw] + { 1 [x=3,y=2] -- {2,3,4} -- 5[level pre sep=5mm] }; + \end{tikzpicture} + "]] +} + +--- + +declare { + key = "level post sep", + type = "length", + initial = ".333em", + + summary = [[" + Works like |level pre sep|. + "]] +} + +--- +declare { + key = "layer pre sep", + type = "length", + use = { + { key = "level pre sep", value = lib.id }, + }, + summary = "An alias for |level pre sep|." +} + +--- +declare { + key = "layer post sep", + type = "length", + use = { + { key = "level post sep", value = lib.id }, + }, + summary = "An alias for |level post sep|." +} + + + + +--- +-- @param length A length + +declare { + key = "level sep", + type = "length", + use = { + { key = "level pre sep", value = function (v) return v/2 end }, + { key = "level post sep", value = function (v) return v/2 end }, + }, + + summary = [[" + A shorthand for setting both |level pre sep| and |level post sep| to + $\meta{length}/2$. Note that if you set |level distance=0| and + |level sep=1em|, you get a layout where any two consecutive layers + are ``spaced apart'' by |1em|. + "]] +} + + +--- +declare { + key = "layer sep", + type = "number", + use = { + { key = "level sep", value = lib.id }, + }, + summary = "An alias for |level sep|." +} + + +--- +declare { + key = "sibling distance", + type = "length", + initial = "1cm", + + summary = [[" + This is minimum distance that the centers of node should have to the + center of the next node on the same level. As for levels, this is + just a lower bound. + For some layouts, like a simple necklace layout, the \meta{length} is + measured as the distance on the circle. + "]], + examples = { + [[" + \tikz \graph [tree layout, sibling distance=1cm, nodes={circle,draw}] + { 1--{2,3,4,5} }; + "]], + [[" + \tikz \graph [tree layout, sibling distance=0cm, sibling sep=0pt, + nodes={circle,draw}] + { 1--{2,3,4,5} }; + "]], + [[" + \tikz \graph [tree layout, sibling distance=0cm, sibling sep=0pt, + nodes={circle,draw}] + { 1--{2,3[sibling distance=1cm],4,5} }; + "]] + } +} + + +--- + +declare { + key = "sibling pre sep", + type = "length", + initial = ".333em", + + summary = [[" + Works like |level pre sep|, only for siblings. + "]], + examples = [[" + \tikz \graph [tree layout, sibling distance=0cm, nodes={circle,draw}, + sibling sep=0pt] + { 1--{2,3[sibling pre sep=1cm],4,5} }; + "]] +} + +--- + +declare { + key = "sibling post sep", + type = "length", + initial = ".333em", + + summary = [[" + Works like |sibling pre sep|. + "]] + } + + + +--- +-- @param length A length + +declare { + key = "sibling sep", + type = "length", + use = { + { key = "sibling pre sep", value = function(v) return v/2 end }, + { key = "sibling post sep", value = function(v) return v/2 end }, + }, + + summary = [[" + A shorthand for setting both |sibling pre sep| and |sibling post sep| to + $\meta{length}/2$. + "]] +} + + + + + + +--- +declare { + key = "part distance", + type = "length", + initial = "1.5cm", + + summary = [[" + This is minimum distance between the centers of ``parts'' of a + graph. What a ``part'' is depends on the algorithm. + "]] +} + + +--- + +declare { + key = "part pre sep", + type = "length", + initial = "1em", + summary = "A pre-padding for parts." +} + +--- + +declare { + key = "part post sep", + type = "length", + initial = "1em", + summary = "A post-padding for pars." + } + + + +--- +-- @param length A length + +declare { + key = "part sep", + type = "length", + use = { + { key = "part pre sep", value = function(v) return v/2 end }, + { key = "part post sep", value = function(v) return v/2 end }, + }, + + summary = [[" + A shorthand for setting both |part pre sep| and |part post sep| to + $\meta{length}/2$. + "]] +} + + + + +--- + +declare { + key = "component sep", + type = "length", + initial = "1.5em", + + summary = [[" + This is pading between the bounding boxes that nodes of different + connected components will have when they are placed next to each + other. + "]], + examples = { + [[" + \tikz \graph [binary tree layout, sibling distance=4mm, level distance=8mm, + components go right top aligned, + component sep=1pt, nodes=draw] + { + 1 -> 2 -> {3->4[second]->5,6,7}; + a -> b[second] -> c[second] -> d -> e; + x -> y[second] -> z -> u[second] -> v; + }; + "]], + [[" + \tikz \graph [binary tree layout, sibling distance=4mm, level distance=8mm, + components go right top aligned, + component sep=1em, nodes=draw] + { + 1 -> 2 -> {3->4[second]->5,6,7}; + a -> b[second] -> c[second] -> d -> e; + x -> y[second] -> z -> u[second] -> v; + }; + "]] + } +} + + + +--- + +declare { + key = "component distance", + type = "length", + initial = "2cm", + + summary = [[" + This is the minimum distance between the centers of bounding + boxes of connected components when they are placed next to each + other. (Not used, currently.) + "]] +} + + +return Distances
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/FineTune.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/FineTune.lua new file mode 100644 index 00000000000..bb2fd065b24 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/FineTune.lua @@ -0,0 +1,164 @@ +-- Copyright 2012 by Till Tantau +-- +-- This file may be distributed an/or modified +-- +-- 1. under the LaTeX Project Public License and/or +-- 2. under the GNU Public License +-- +-- See the file doc/generic/pgf/licenses/LICENSE for more information + +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/control/FineTune.lua,v 1.3 2013/05/23 20:01:25 tantau Exp $ + + +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare + +local Coordinate = require "pgf.gd.model.Coordinate" +local lib = require "pgf.gd.lib" + +--- +-- @section subsection {Fine-Tuning Positions of Nodes} +-- +-- @end + + + +--- +declare { + key = "nudge", + type = "canvas coordinate", + + summary = [[" + This option allows you to slightly ``nudge'' (move) nodes after + they have been positioned by the given offset. The idea is that + this nudging is done after the position of the node has been + computed, so nudging has no influence on the actual graph + drawing algorithms. This, in turn, means that you can use + nudging to ``correct'' or ``optimize'' the positioning of nodes + after the algorithm has computed something. + "]], + + examples = [[" + \tikz \graph [edges=rounded corners, nodes=draw, + layered layout, sibling distance=0] { + a -- {b, c, d[nudge=(up:2mm)]} -- e -- a; + }; + "]] +} + + +--- +-- @param distance A distance by which the node is nudges. + +declare { + key = "nudge up", + type = "length", + use = { + { key = "nudge", value = function (v) return Coordinate.new(0,v) end } + }, + + summary = "A shorthand for nudging a node upwards.", + examples = [[" + \tikz \graph [edges=rounded corners, nodes=draw, + layered layout, sibling distance=0] { + a -- {b, c, d[nudge up=2mm]} -- e -- a; + }; + "]] +} + + +--- +-- @param distance A distance by which the node is nudges. + +declare { + key = "nudge down", + type = "length", + use = { + { key = "nudge", value = function (v) return Coordinate.new(0,-v) end } + }, + + summary = "Like |nudge up|, but downwards." +} + +--- +-- @param distance A distance by which the node is nudges. + +declare { + key = "nudge left", + type = "length", + use = { + { key = "nudge", value = function (v) return Coordinate.new(-v,0) end } + }, + + summary = "Like |nudge up|, but left.", + examples = [[" + \tikz \graph [edges=rounded corners, nodes=draw, + layered layout, sibling distance=0] { + a -- {b, c, d[nudge left=2mm]} -- e -- a; + }; + "]] +} + +--- +-- @param distance A distance by which the node is nudges. + +declare { + key = "nudge right", + type = "length", + use = { + { key = "nudge", value = function (v) return Coordinate.new(v,0) end } + }, + + summary = "Like |nudge left|, but right." +} + + +--- +declare { + key = "regardless at", + type = "canvas coordinate", + + summary = [[" + Using this option you can provide a position for a node to wish + it will be forced after the graph algorithms have run. So, the node + is positioned normally and the graph drawing algorithm does not know + about the position specified using |regardless at|. However, + afterwards, the node is placed there, regardless of what the + algorithm has computed (all other nodes are unaffected). + "]], + examples = [[" + \tikz \graph [edges=rounded corners, nodes=draw, + layered layout, sibling distance=0] { + a -- {b,c,d[regardless at={(1,0)}]} -- e -- a; + }; + "]] +} + + + + +--- +-- @param pos A canvas position (a coordinate). + +declare { + key = "nail at", + type = "canvas coordinate", + use = { + { key = "desired at", value = lib.id }, + { key = "regardless at", value = lib.id }, + }, + + summary = [[" + This option combines |desired at| and |regardless at|. Thus, the + algorithm is ``told'' about the desired position. If it fails to place + the node at the desired position, it will be put there + regardless. The name of the key is intended to remind one of a node + being ``nailed'' to the canvas. + "]], + examples = [[" + \tikz \graph [edges=rounded corners, nodes=draw, + layered layout, sibling distance=0] { + a -- {b,c,d[nail at={(1,0)}]} -- e[nail at={(1.5,-1)}] -- a; + }; + "]] +} + diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/LayoutPipeline.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/LayoutPipeline.lua new file mode 100644 index 00000000000..c32f9d084f6 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/LayoutPipeline.lua @@ -0,0 +1,1282 @@ +-- Copyright 2012 by Till Tantau +-- +-- This file may be distributed an/or modified +-- +-- 1. under the LaTeX Project Public License and/or +-- 2. under the GNU Public License +-- +-- See the file doc/generic/pgf/licenses/LICENSE for more information + +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/control/LayoutPipeline.lua,v 1.11 2013/10/09 19:46:59 tantau Exp $ + + +--- +-- This class controls the running of graph drawing algorithms on +-- graphs. In particular, it performs pre- and posttransformations and +-- also invokes the collapsing of sublayouts. +-- +-- You do not call any of the methods of this class directly, the +-- whole class is included only for documentation purposes. +-- +-- Before an algorithm is applied, a number of transformations will +-- have been applied, depending on the algorithm's |preconditions| +-- field: +-- +-- \begin{itemize} +-- \item |connected| +-- +-- If this property is set for an algorithm (that is, in the +-- |declare| statement for the algorithm the |predconditions| field +-- has the entry |connected=true| set), then the graph will be +-- decomposed into connected components. The algorithm is run on each +-- component individually. +-- \item |tree| +-- +-- When set, the field |spanning_tree| of the algorithm will be set +-- to a spanning tree of the graph. This option implies |connected|. +-- \item |loop_free| +-- +-- When set, all loops (arcs from a vertex to itself) will have been +-- removed when the algorithm runs. +-- +-- \item |at_least_two_nodes| +-- +-- When explicitly set to |false| (this precondition is |true| by +-- default), the algorithm will even be run if there is only a +-- single vertex in the graph. +-- \end{itemize} +-- +-- Once the algorithm has run, the algorithm's |postconditions| will +-- be processed: +-- +-- \begin{itemize} +-- \item |upward_oriented| +-- +-- When set, the algorithm tells the layout pipeline that the graph +-- has been laid out in a layered manner with each layer going from +-- left to right and layers at a whole going upwards (positive +-- $y$-coordinates). The graph will then be rotated and possibly +-- swapped in accordance with the |grow| key set by the user. +-- \item |fixed| +-- +-- When set, no rotational postprocessing will be done after the +-- algorithm has run. Usually, a graph is rotated to meet a user's +-- |orient| settings. However, when the algorithm has already +-- ``ideally'' rotated the graph, set this postcondition. +-- \end{itemize} +-- + +local LayoutPipeline = {} + + +-- Namespace +require("pgf.gd.control").LayoutPipeline = LayoutPipeline + + +-- Imports +local Direct = require "pgf.gd.lib.Direct" +local Storage = require "pgf.gd.lib.Storage" +local Simplifiers = require "pgf.gd.lib.Simplifiers" +local LookupTable = require "pgf.gd.lib.LookupTable" +local Transform = require "pgf.gd.lib.Transform" + +local Arc = require "pgf.gd.model.Arc" +local Vertex = require "pgf.gd.model.Vertex" +local Digraph = require "pgf.gd.model.Digraph" +local Coordinate = require "pgf.gd.model.Coordinate" +local Path = require "pgf.gd.model.Path" + +local Sublayouts = require "pgf.gd.control.Sublayouts" + +local lib = require "pgf.gd.lib" + +local InterfaceCore = require "pgf.gd.interface.InterfaceCore" + + + + +-- Forward definitions + +local prepare_events + + + +-- The main ``graph drawing pipeline'' that handles the pre- and +-- postprocessing for a graph. This method is called by the diplay +-- interface. +-- +-- @param scope A graph drawing scope. + +function LayoutPipeline.run(scope) + + -- The pipeline... + + -- Step 1: Preparations + + -- Prepare events + prepare_events(scope.events) + + -- Step 2: Recursively layout the graph, starting with the root layout + local root_layout = assert(scope.collections[InterfaceCore.sublayout_kind][1], "no layout in scope") + + scope.syntactic_digraph = + Sublayouts.layoutRecursively (scope, + root_layout, + LayoutPipeline.runOnLayout, + { root_layout }) + + -- Step 3: Anchor the graph + LayoutPipeline.anchor(scope.syntactic_digraph, scope) + + -- Step 4: Apply regardless transforms + Sublayouts.regardless(scope.syntactic_digraph) + + -- Step 5: Cut edges + LayoutPipeline.cutEdges(scope.syntactic_digraph) + +end + + + +-- +-- This method is called by the sublayout rendering pipeline when the +-- algorithm should be invoked for an individual graph. At this point, +-- the sublayouts will already have been collapsed. +-- +-- @param scope The graph drawing scope. +-- @param algorithm_class The to-be-applied algorithm class. +-- @param layout_graph A subgraph of the syntactic digraph which is +-- restricted to the current layout and in which sublayouts have +-- been contracted to single nodes. +-- @param layout The layout to which the graph belongs. +-- +function LayoutPipeline.runOnLayout(scope, algorithm_class, layout_graph, layout) + + if #layout_graph.vertices < 1 then + return + end + + -- The involved main graphs: + local layout_copy = Digraph.new (layout_graph) --Direct.digraphFromSyntacticDigraph(layout_graph) + for _,a in ipairs(layout_graph.arcs) do + local new_a = layout_copy:connect(a.tail,a.head) + new_a.syntactic_edges = a.syntactic_edges + end + + -- Step 1: Decompose the graph into connected components, if necessary: + local syntactic_components + if algorithm_class.preconditions.tree or algorithm_class.preconditions.connected or layout_graph.options.componentwise then + syntactic_components = LayoutPipeline.decompose(layout_copy) + LayoutPipeline.sortComponents(layout_graph.options['component order'], syntactic_components) + else + -- Only one component: The graph itself... + syntactic_components = { layout_copy } + end + + -- Step 2: For all components do: + for i,syntactic_component in ipairs(syntactic_components) do + + -- Step 2.1: Reset random number generator to make sure that the + -- same graph is always typeset in the same way. + math.randomseed(layout_graph.options['random seed']) + + local digraph = Direct.digraphFromSyntacticDigraph(syntactic_component) + + -- Step 2.3: If requested, remove loops + if algorithm_class.preconditions.loop_free then + for _,v in ipairs(digraph.vertices) do + digraph:disconnect(v,v) + end + end + + -- Step 2.4: Precompute the underlying undirected graph + local ugraph = Direct.ugraphFromDigraph(digraph) + + -- Step 2.5: Create an algorithm object + local algorithm = algorithm_class.new{ + digraph = digraph, + ugraph = ugraph, + scope = scope, + layout = layout, + layout_graph = layout_graph, + syntactic_component = syntactic_component, + } + + -- Step 2.7: Compute a spanning tree, if necessary + if algorithm_class.preconditions.tree then + local spanning_algorithm_class = syntactic_component.options.algorithm_phases["spanning tree computation"] + algorithm.spanning_tree = + spanning_algorithm_class.new{ + ugraph = ugraph, + events = scope.events + }:run() + end + + -- Step 2.8: Compute growth-adjusted sizes + algorithm.rotation_info = LayoutPipeline.prepareRotateAround(algorithm.postconditions, syntactic_component) + algorithm.adjusted_bb = Storage.newTableStorage() + LayoutPipeline.prepareBoundingBoxes(algorithm.rotation_info, algorithm.adjusted_bb, syntactic_component, syntactic_component.vertices) + + -- Step 2.9: Finally, run algorithm on this component! + if #digraph.vertices > 1 or algorithm_class.run_also_for_single_node + or algorithm_class.preconditions.at_least_two_nodes == false then + -- Main run of the algorithm: + if algorithm_class.old_graph_model then + LayoutPipeline.runOldGraphModel(scope, digraph, algorithm_class, algorithm) + else + algorithm:run () + end + end + + -- Step 2.10: Sync the graphs + digraph:sync() + ugraph:sync() + if algorithm.spanning_tree then + algorithm.spanning_tree:sync() + end + + -- Step 2.11: Orient the graph + LayoutPipeline.orient(algorithm.rotation_info, algorithm.postconditions, syntactic_component, scope) + end + + -- Step 3: Packing: + LayoutPipeline.packComponents(layout_graph, syntactic_components) +end + + + + + + +--- +-- This function is called internally to perform the graph anchoring +-- procedure described in +-- Section~\ref{subsection-library-graphdrawing-anchoring}. These +-- transformations are always performed. +-- +-- @param graph A graph +-- @param scope The scope + +function LayoutPipeline.anchor(graph, scope) + + -- Step 1: Find anchor node: + local anchor_node + + local anchor_node_name = graph.options['anchor node'] + if anchor_node_name then + anchor_node = scope.node_names[anchor_node_name] + end + + if not graph:contains(anchor_node) then + anchor_node = + lib.find (graph.vertices, function (v) return v.options['anchor here'] end) or + lib.find (graph.vertices, function (v) return v.options['desired at'] end) or + graph.vertices[1] + end + + -- Sanity check + assert(graph:contains(anchor_node), "anchor node is not in graph!") + + local desired = anchor_node.options['desired at'] or graph.options['anchor at'] + local delta = desired - anchor_node.pos + + -- Step 3: Shift nodes + for _,v in ipairs(graph.vertices) do + v.pos:shiftByCoordinate(delta) + end + for _,a in ipairs(graph.arcs) do + if a.path then a.path:shiftByCoordinate(delta) end + for _,e in ipairs(a.syntactic_edges) do + e.path:shiftByCoordinate(delta) + end + end +end + + + +--- +-- This method tries to determine in which direction the graph is supposed to +-- grow and in which direction the algorithm will grow the graph. These two +-- pieces of information togehter produce a necessary rotation around some node. +-- This rotation is returned in a table. +-- +-- Note that this method does not actually cause a rotation to happen; this is +-- left to other method. +-- +-- @param postconditions The algorithm's postconditions. +-- @param graph An undirected graph +-- @return A table containing the computed information. + +function LayoutPipeline.prepareRotateAround(postconditions, graph) + + -- Find the vertex from which we orient + local swap = true + + local v,_,grow = lib.find (graph.vertices, function (v) return v.options["grow"] end) + + if not v and graph.options["grow"] then + v,grow,swap = graph.vertices[1], graph.options["grow"], true + end + + if not v then + v,_,grow = lib.find (graph.vertices, function (v) return v.options["grow'"] end) + swap = false + end + + if not v and graph.options["grow'"] then + v,grow,swap = graph.vertices[1], graph.options["grow'"], false + end + + if not v then + v, grow, swap = graph.vertices[1], -90, true + end + + -- Now compute the rotation + local info = {} + local growth_direction = (postconditions.upward_oriented and 90) or (postconditions.upward_oriented_swapped and 90) + + if postconditions.upward_oriented_swapped then + swap = not swap + end + + if growth_direction == "fixed" then + info.angle = 0 -- no rotation + elseif growth_direction then + info.from_node = v + info.from_angle = growth_direction/360*2*math.pi + info.to_angle = grow/360*2*math.pi + info.swap = swap + info.angle = info.to_angle - info.from_angle + else + info.from_node = v + local other = lib.find_min( + graph:outgoing(v), + function (a) + if a.head ~= v and a:eventIndex() then + return a, a:eventIndex() + end + end) + info.to_node = (other and other.head) or + (graph.vertices[1] == v and graph.vertices[2] or graph.vertices[1]) + info.to_angle = grow/360*2*math.pi + info.swap = swap + info.angle = info.to_angle - math.atan2(info.to_node.pos.y - v.pos.y, info.to_node.pos.x - v.pos.x) + end + + return info +end + + + +--- +-- Compute growth-adjusted node sizes. +-- +-- For each node of the graph, compute bounding box of the node that +-- results when the node is rotated so that it is in the correct +-- orientation for what the algorithm assumes. +-- +-- The ``bounding box'' actually consists of the fields +-- \begin{itemize} +-- \item |sibling_pre|, +-- \item |sibling_post|, +-- \item |layer_pre|, and +-- \item |layer_post|, +-- \end{itemize} +-- which correspond to ``min x'', ``min y'', ``min y'', and ``max y'' +-- for a tree growing up. +-- +-- The computation of the ``bounding box'' treats a centered circle in +-- a special way, all other shapes are currently treated like a +-- rectangle. +-- +-- @param rotation_info The table computed by the function prepareRotateAround +-- @param packing_storage A storage in which the computed distances are stored. +-- @param graph An graph +-- @param vertices An array of to-be-prepared vertices inside graph + +function LayoutPipeline.prepareBoundingBoxes(rotation_info, adjusted_bb, graph, vertices) + + local angle = assert(rotation_info.angle, "angle field missing") + local swap = rotation_info.swap + + for _,v in ipairs(vertices) do + local bb = adjusted_bb[v] + local a = angle + + if v.shape == "circle" then + a = 0 -- no rotation for circles. + end + + -- Fill the bounding box field, + bb.sibling_pre = math.huge + bb.sibling_post = -math.huge + bb.layer_pre = math.huge + bb.layer_post = -math.huge + + local c = math.cos(angle) + local s = math.sin(angle) + for _,p in ipairs(v.path:coordinates()) do + local x = p.x*c + p.y*s + local y = -p.x*s + p.y*c + + bb.sibling_pre = math.min (bb.sibling_pre, x) + bb.sibling_post = math.max (bb.sibling_post, x) + bb.layer_pre = math.min (bb.layer_pre, y) + bb.layer_post = math.max (bb.layer_post, y) + end + + -- Flip sibling per and post if flag: + if swap then + bb.sibling_pre, bb.sibling_post = -bb.sibling_post, -bb.sibling_pre + end + end +end + + + + + +-- +-- Rotate the whole graph around a point +-- +-- Causes the graph to be rotated around \meta{around} so that what +-- used to be the |from_angle| becomes the |to_angle|. If the flag |swap| +-- is set, the graph is additionally swapped along the |to_angle|. +-- +-- @param graph The to-be-rotated (undirected) graph +-- @param around_x The $x$-coordinate of the point around which the graph should be rotated +-- @param around_y The $y$-coordinate +-- @param from An ``old'' angle +-- @param to A ``new'' angle +-- @param swap A boolean that, when true, requests that the graph is +-- swapped (flipped) along the new angle + +function LayoutPipeline.rotateGraphAround(graph, around_x, around_y, from, to, swap) + + -- Translate to origin + local t = Transform.new_shift(-around_x, -around_y) + + -- Rotate to zero degrees: + t = Transform.concat(Transform.new_rotation(-from), t) + + -- Swap + if swap then + t = Transform.concat(Transform.new_scaling(1,-1), t) + end + + -- Rotate to from degrees: + t = Transform.concat(Transform.new_rotation(to), t) + + -- Translate back + t = Transform.concat(Transform.new_shift(around_x, around_y), t) + + for _,v in ipairs(graph.vertices) do + v.pos:apply(t) + end + for _,a in ipairs(graph.arcs) do + for _,p in ipairs(a:pointCloud()) do + p:apply(t) + end + end +end + + + +-- +-- Orient the whole graph using two nodes +-- +-- The whole graph is rotated so that the line from the first node to +-- the second node has the given angle. If swap is set to true, the +-- graph is also flipped along this line. +-- +-- @param graph +-- @param first_node +-- @param seond_node +-- @param target_angle +-- @param swap + +function LayoutPipeline.orientTwoNodes(graph, first_node, second_node, target_angle, swap) + if first_node and second_node then + -- Compute angle between first_node and second_node: + local x = second_node.pos.x - first_node.pos.x + local y = second_node.pos.y - first_node.pos.y + + local angle = math.atan2(y,x) + LayoutPipeline.rotateGraphAround(graph, first_node.pos.x, + first_node.pos.y, angle, target_angle, swap) + end +end + + + +--- +-- Performs a post-layout orientation of the graph by performing the +-- steps documented in Section~\ref{subsection-library-graphdrawing-standard-orientation}. +-- +-- @param rotation_info The info record computed by the function |prepareRotateAround|. +-- @param postconditions The algorithm's postconditions. +-- @param graph A to-be-oriented graph. +-- @param scope The graph drawing scope. + +function LayoutPipeline.orient(rotation_info, postconditions, graph, scope) + + -- Sanity check + if #graph.vertices < 2 then return end + + -- Step 1: Search for global graph orient options: + local function f (orient, tail, head, flag) + if orient and head and tail then + local n1 = scope.node_names[tail] + local n2 = scope.node_names[head] + if graph:contains(n1) and graph:contains(n2) then + LayoutPipeline.orientTwoNodes(graph, n1, n2, orient/360*2*math.pi, flag) + return true + end + end + end + if f(graph.options["orient"], graph.options["orient tail"],graph.options["orient head"], false) then return end + if f(graph.options["orient'"], graph.options["orient tail"],graph.options["orient head"], true) then return end + local tail, head = string.match(graph.options["horizontal"] or "", "^(.*) to (.*)$") + if f(0, tail, head, false) then return end + local tail, head = string.match(graph.options["horizontal'"] or "", "^(.*) to (.*)$") + if f(0, tail, head, true) then return end + local tail, head = string.match(graph.options["vertical"] or "", "^(.*) to (.*)$") + if f(-90, tail, head, false) then return end + local tail, head = string.match(graph.options["vertical'"] or "", "^(.*) to (.*)$") + if f(-90, tail, head, true) then return end + + -- Step 2: Search for a node with the orient option: + for _, v in ipairs(graph.vertices) do + local function f (key, flag) + local orient = v.options[key] + local head = v.options["orient head"] + local tail = v.options["orient tail"] + + if orient and head then + local n2 = scope.node_names[head] + if graph:contains(n2) then + LayoutPipeline.orientTwoNodes(graph, v, n2, orient/360*2*math.pi, flag) + return true + end + elseif orient and tail then + local n1 = scope.node_names[tail] + if graph:contains(n1) then + LayoutPipeline.orientTwoNodes(graph, n1, v, orient/360*2*math.pi, flag) + return true + end + end + end + if f("orient", false) then return end + if f("orient'", true) then return end + end + + -- Step 3: Search for an edge with the orient option: + for _, a in ipairs(graph.arcs) do + if a:options("orient",true) then + return LayoutPipeline.orientTwoNodes(graph, a.tail, a.head, a:options("orient")/360*2*math.pi, false) + end + if a:options("orient'",true) then + return LayoutPipeline.orientTwoNodes(graph, a.tail, a.head, a:options("orient'")/360*2*math.pi, true) + end + end + + -- Step 4: Search two nodes with a desired at option: + local first, second, third + + for _, v in ipairs(graph.vertices) do + if v.options['desired at'] then + if first then + if second then + third = v + break + else + second = v + end + else + first = v + end + end + end + + if second then + local a = first.options['desired at'] + local b = second.options['desired at'] + return LayoutPipeline.orientTwoNodes(graph, first, second, math.atan2(b.y-a.y,b.x-a.x), false) + end + + -- Computed during preprocessing: + if rotation_info.from_node and postconditions.fixed ~= true then + local x = rotation_info.from_node.pos.x + local y = rotation_info.from_node.pos.y + local from_angle = rotation_info.from_angle or math.atan2(rotation_info.to_node.pos.y - y, rotation_info.to_node.pos.x - x) + + LayoutPipeline.rotateGraphAround(graph, x, y, from_angle, rotation_info.to_angle, rotation_info.swap) + end +end + + + + +--- +-- This internal function is called to decompose a graph into its +-- components. Whether or not this function is called depends on +-- whether the precondition |connected| is set for the algorithm class +-- and whether the |componentwise| key is used. +-- +-- @param graph A to-be-decomposed graph +-- +-- @return An array of graph objects that represent the connected components of the graph. + +function LayoutPipeline.decompose (digraph) + + -- The list of connected components (node sets) + local components = {} + + -- Remember, which graphs have already been visited + local visited = {} + + for _,v in ipairs(digraph.vertices) do + if not visited[v] then + -- Start a depth-first-search of the graph, starting at node n: + local stack = { v } + local component = Digraph.new { + syntactic_digraph = digraph.syntactic_digraph, + options = digraph.options + } + + while #stack >= 1 do + local tos = stack[#stack] + stack[#stack] = nil -- pop + + if not visited[tos] then + + -- Visit pos: + component:add { tos } + visited[tos] = true + + -- Push all unvisited neighbors: + for _,a in ipairs(digraph:incoming(tos)) do + local neighbor = a.tail + if not visited[neighbor] then + stack[#stack+1] = neighbor -- push + end + end + for _,a in ipairs(digraph:outgoing(tos)) do + local neighbor = a.head + if not visited[neighbor] then + stack[#stack+1] = neighbor -- push + end + end + end + end + + -- Ok, vertices will now contain all vertices reachable from n. + components[#components+1] = component + end + end + + if #components < 2 then + return { digraph } + end + + for _,c in ipairs(components) do + table.sort (c.vertices, function (u,v) return u.event.index < v.event.index end) + for _,v in ipairs(c.vertices) do + for _,a in ipairs(digraph:outgoing(v)) do + local new_a = c:connect(a.tail, a.head) + new_a.syntactic_edges = a.syntactic_edges + end + for _,a in ipairs(digraph:incoming(v)) do + local new_a = c:connect(a.tail, a.head) + new_a.syntactic_edges = a.syntactic_edges + end + end + end + + return components +end + + + + +-- Handling of component order +-- +-- LayoutPipeline are ordered according to a function that is stored in +-- a key of the |LayoutPipeline.component_ordering_functions| table +-- whose name is the graph option |component order|. +-- +-- @param component_order An ordering method +-- @param subgraphs A list of to-be-sorted subgraphs + +function LayoutPipeline.sortComponents(component_order, subgraphs) + if component_order then + local f = LayoutPipeline.component_ordering_functions[component_order] + if f then + table.sort (subgraphs, f) + end + end +end + + +-- Right now, we hardcode the functions here. Perhaps make this +-- dynamic in the future. Could easily be done on the tikzlayer, +-- acutally. + +LayoutPipeline.component_ordering_functions = { + ["increasing node number"] = + function (g,h) + if #g.vertices == #h.vertices then + return g.vertices[1].event.index < h.vertices[1].event.index + else + return #g.vertices < #h.vertices + end + end, + ["decreasing node number"] = + function (g,h) + if #g.vertices == #h.vertices then + return g.vertices[1].event.index < h.vertices[1].event.index + else + return #g.vertices > #h.vertices + end + end, + ["by first specified node"] = nil, +} + + + + +local function compute_rotated_bb(vertices, angle, sep, bb) + + local r = Transform.new_rotation(-angle) + + for _,v in ipairs(vertices) do + -- Find the rotated bounding box field, + local t = Transform.concat(r,Transform.new_shift(v.pos.x, v.pos.y)) + + local min_x = math.huge + local max_x = -math.huge + local min_y = math.huge + local max_y = -math.huge + + for _,e in ipairs(v.path) do + if type(e) == "table" then + local c = e:clone() + c:apply(t) + + min_x = math.min (min_x, c.x) + max_x = math.max (max_x, c.x) + min_y = math.min (min_y, c.y) + max_y = math.max (max_y, c.y) + end + end + + -- Enlarge by sep: + min_x = min_x - sep + max_x = max_x + sep + min_y = min_y - sep + max_y = max_y + sep + + local _,_,_,_,c_x,c_y = v:boundingBox() + local center = Coordinate.new(c_x,c_y) + + center:apply(t) + + bb[v].min_x = min_x + bb[v].max_x = max_x + bb[v].min_y = min_y + bb[v].max_y = max_y + bb[v].c_y = center.y + end +end + + + +--- +-- This internal function packs the components of a graph. See +-- Section~\ref{subsection-gd-component-packing} for details. +-- +-- @param graph The graph +-- @param components A list of components + +function LayoutPipeline.packComponents(syntactic_digraph, components) + + local vertices = Storage.newTableStorage() + local bb = Storage.newTableStorage() + + -- Step 1: Preparation, rotation to target direction + local sep = syntactic_digraph.options['component sep'] + local angle = syntactic_digraph.options['component direction']/180*math.pi + + local mark = {} + for _,c in ipairs(components) do + + -- Setup the lists of to-be-considered nodes + local vs = {} + for _,v in ipairs(c.vertices) do + vs [#vs + 1] = v + end + + for _,a in ipairs(c.arcs) do + for _,p in ipairs(a:pointCloud()) do + vs [#vs + 1] = Vertex.new { pos = p } + end + end + vertices[c] = vs + + compute_rotated_bb(vs, angle, sep/2, bb) + end + + local x_shifts = { 0 } + local y_shifts = {} + + -- Step 2: Vertical alignment + for i,c in ipairs(components) do + local max_max_y = -math.huge + local max_center_y = -math.huge + local min_min_y = math.huge + local min_center_y = math.huge + + for _,v in ipairs(c.vertices) do + local info = bb[v] + max_max_y = math.max(info.max_y, max_max_y) + max_center_y = math.max(info.c_y, max_center_y) + min_min_y = math.min(info.min_y, min_min_y) + min_center_y = math.min(info.c_y, min_center_y) + end + + -- Compute alignment line + local valign = syntactic_digraph.options['component align'] + local line + if valign == "counterclockwise bounding box" then + line = max_max_y + elseif valign == "counterclockwise" then + line = max_center_y + elseif valign == "center" then + line = (max_max_y + min_min_y) / 2 + elseif valign == "clockwise" then + line = min_center_y + elseif valign == "first node" then + line = bb[c.vertices[1]].c_y + else + line = min_min_y + end + + -- Overruled? + for _,v in ipairs(c.vertices) do + if v.options['align here'] then + line = bb[v].c_y + break + end + end + + -- Ok, go! + y_shifts[i] = -line + + -- Adjust nodes: + for _,v in ipairs(vertices[c]) do + local info = bb[v] + info.min_y = info.min_y - line + info.max_y = info.max_y - line + info.c_y = info.c_y - line + end + end + + -- Step 3: Horizontal alignment + local y_values = {} + + for _,c in ipairs(components) do + for _,v in ipairs(vertices[c]) do + local info = bb[v] + y_values[#y_values+1] = info.min_y + y_values[#y_values+1] = info.max_y + y_values[#y_values+1] = info.c_y + end + end + + table.sort(y_values) + + local y_ranks = {} + local right_face = {} + for i=1,#y_values do + y_ranks[y_values[i]] = i + right_face[i] = -math.huge + end + + + + for i=1,#components-1 do + -- First, update right_face: + local touched = {} + + for _,v in ipairs(vertices[components[i]]) do + local info = bb[v] + local border = info.max_x + + for i=y_ranks[info.min_y],y_ranks[info.max_y] do + touched[i] = true + right_face[i] = math.max(right_face[i], border) + end + end + + -- Fill up the untouched entries: + local right_max = -math.huge + for i=1,#y_values do + if not touched[i] then + -- Search for next and previous touched + local interpolate = -math.huge + for j=i+1,#y_values do + if touched[j] then + interpolate = math.max(interpolate,right_face[j] - (y_values[j] - y_values[i])) + break + end + end + for j=i-1,1,-1 do + if touched[j] then + interpolate = math.max(interpolate,right_face[j] - (y_values[i] - y_values[j])) + break + end + end + right_face[i] = math.max(interpolate,right_face[i]) + end + right_max = math.max(right_max, right_face[i]) + end + + -- Second, compute the left face + local touched = {} + local left_face = {} + for i=1,#y_values do + left_face[i] = math.huge + end + for _,v in ipairs(vertices[components[i+1]]) do + local info = bb[v] + local border = info.min_x + + for i=y_ranks[info.min_y],y_ranks[info.max_y] do + touched[i] = true + left_face[i] = math.min(left_face[i], border) + end + end + + -- Fill up the untouched entries: + local left_min = math.huge + for i=1,#y_values do + if not touched[i] then + -- Search for next and previous touched + local interpolate = math.huge + for j=i+1,#y_values do + if touched[j] then + interpolate = math.min(interpolate,left_face[j] + (y_values[j] - y_values[i])) + break + end + end + for j=i-1,1,-1 do + if touched[j] then + interpolate = math.min(interpolate,left_face[j] + (y_values[i] - y_values[j])) + break + end + end + left_face[i] = interpolate + end + left_min = math.min(left_min, left_face[i]) + end + + -- Now, compute the shift. + local shift = -math.huge + + if syntactic_digraph.options['component packing'] == "rectangular" then + shift = right_max - left_min + else + for i=1,#y_values do + shift = math.max(shift, right_face[i] - left_face[i]) + end + end + + -- Adjust nodes: + x_shifts[i+1] = shift + for _,v in ipairs(vertices[components[i+1]]) do + local info = bb[v] + info.min_x = info.min_x + shift + info.max_x = info.max_x + shift + end + end + + -- Now, rotate shifts + for i,c in ipairs(components) do + local x = x_shifts[i]*math.cos(angle) - y_shifts[i]*math.sin(angle) + local y = x_shifts[i]*math.sin(angle) + y_shifts[i]*math.cos(angle) + + for _,v in ipairs(vertices[c]) do + v.pos.x = v.pos.x + x + v.pos.y = v.pos.y + y + end + end +end + + + + + + + +-- +-- Store for each begin/end event the index of +-- its corresponding end/begin event +-- +-- @param events An event list + +prepare_events = + function (events) + local stack = {} + + for i=1,#events do + if events[i].kind == "begin" then + stack[#stack + 1] = i + elseif events[i].kind == "end" then + local tos = stack[#stack] + stack[#stack] = nil -- pop + + events[tos].end_index = i + events[i].begin_index = tos + end + end + end + + + +--- +-- Cut the edges. This function handles the ``cutting'' of edges. The +-- idea is that every edge is a path going from the center of the from +-- node to the center of the target node. Now, we intersect this path +-- with the path of the start node and cut away everything before this +-- intersection. Likewise, we intersect the path with the head node +-- and, again, cut away everything following the intersection. +-- +-- These cuttings are not done if appropriate options are set. + +function LayoutPipeline.cutEdges(graph) + + for _,a in ipairs(graph.arcs) do + for _,e in ipairs(a.syntactic_edges) do + local p = e.path + p:makeRigid() + local orig = p:clone() + + if e.options['tail cut'] and e.tail.options['cut policy'] == "as edge requests" + or e.tail.options['cut policy'] == "all" then + + local vpath = e.tail.path:clone() + vpath:shiftByCoordinate(e.tail.pos) + + local x = p:intersectionsWith (vpath) + + if #x > 0 then + p:cutAtBeginning(x[1].index, x[1].time) + end + end + + if e.options['head cut'] and e.head.options['cut policy'] == "as edge requests" + or e.head.options['cut policy'] == "all" then + + local vpath = e.head.path:clone() + vpath:shiftByCoordinate(e.head.pos) + x = p:intersectionsWith (vpath) + if #x > 0 then + p:cutAtEnd(x[#x].index, x[#x].time) + else + -- Check whether there was an intersection with the original + --path: + local x2 = orig:intersectionsWith (vpath) + if #x2 > 0 then + -- Ok, after cutting the tail vertex, there is no longer + -- an intersection with the head vertex, but there used to + -- be one. This means that the vertices overlap and the + -- path should be ``inside'' them. Hmm... + if e.options['allow inside edges'] and #p > 1 then + local from = p[2] + local to = x2[1].point + p:clear() + p:appendMoveto(from) + p:appendLineto(to) + else + p:clear() + end + end + end + end + end + end +end + + + + + + +-- Deprecated stuff + +local Node = require "pgf.gd.deprecated.Node" +local Graph = require "pgf.gd.deprecated.Graph" +local Edge = require "pgf.gd.deprecated.Edge" +local Cluster = require "pgf.gd.deprecated.Cluster" + + + + + +local unique_count = 0 + +local function compatibility_digraph_to_graph(scope, g) + local graph = Graph.new() + + -- Graph options + graph.options = g.options + graph.orig_digraph = g + + -- Events + for i,e in ipairs(scope.events) do + graph.events[i] = e + end + + -- Nodes + for _,v in ipairs(g.vertices) do + if not v.name then + -- compat needs unique name + v.name = "auto generated node nameINTERNAL" .. unique_count + unique_count = unique_count + 1 + end + local minX, minY, maxX, maxY = v:boundingBox() + local node = Node.new{ + name = v.name, + tex = { + tex_node = v.tex and v.tex.stored_tex_box_number, + shape = v.shape, + minX = minX, + maxX = maxX, + minY = minY, + maxY = maxY, + }, + options = v.options, + event_index = v.event.index, + index = v.event.index, + orig_vertex = v, + } + graph:addNode(node) + graph.events[v.event.index or (#graph.events+1)] = { kind = 'node', parameters = node } + end + + -- Edges + local mark = Storage.new() + for _,a in ipairs(g.arcs) do + local da = g.syntactic_digraph:arc(a.tail, a.head) + if da then + for _,m in ipairs(da.syntactic_edges) do + if not mark[m] then + mark[m] = true + local from_node = graph:findNode(da.tail.name) + local to_node = graph:findNode(da.head.name) + local edge = graph:createEdge(from_node, to_node, m.direction, nil, m.options, nil) + edge.event_index = m.event.index + edge.orig_m = m + graph.events[m.event.index] = { kind = 'edge', parameters = edge } + end + end + end + local da = g.syntactic_digraph:arc(a.head, a.tail) + if da then + for _,m in ipairs(da.syntactic_edges) do + if not mark[m] then + mark[m] = true + local from_node = graph:findNode(da.tail.name) + local to_node = graph:findNode(da.head.name) + local edge = graph:createEdge(from_node, to_node, m.direction, nil, m.options, nil) + edge.event_index = m.event.index + edge.orig_m = m + graph.events[m.event.index] = { kind = 'edge', parameters = edge } + end + end + end + end + + table.sort(graph.edges, function(e1,e2) return e1.event_index < e2.event_index end) + for _,n in ipairs (graph.nodes) do + table.sort(n.edges, function(e1,e2) return e1.event_index < e2.event_index end) + end + + + -- Clusters + for _, c in ipairs(scope.collections['same layer'] or {}) do + cluster = Cluster.new("cluster" .. unique_count) + unique_count = unique_count+1 + graph:addCluster(cluster) + for _,v in ipairs(c.vertices) do + if g:contains(v) then + cluster:addNode(graph:findNode(v.name)) + end + end + end + + return graph +end + + +local function compatibility_graph_to_digraph(graph) + for _,n in ipairs(graph.nodes) do + n.orig_vertex.pos.x = n.pos.x + n.orig_vertex.pos.y = n.pos.y + end + for _,e in ipairs(graph.edges) do + if #e.bend_points > 0 then + local c = {} + for _,x in ipairs(e.bend_points) do + c[#c+1] = Coordinate.new (x.x, x.y) + end + e.orig_m:setPolylinePath(c) + end + end +end + + + + + +function LayoutPipeline.runOldGraphModel(scope, digraph, algorithm_class, algorithm) + + local graph = compatibility_digraph_to_graph(scope, digraph) + + algorithm.graph = graph + graph:registerAlgorithm(algorithm) + + -- If requested, remove loops + if algorithm_class.preconditions.loop_free then + Simplifiers:removeLoopsOldModel(algorithm) + end + + -- If requested, collapse multiedges + if algorithm_class.preconditions.simple then + Simplifiers:collapseMultiedgesOldModel(algorithm) + end + + if #graph.nodes > 1 then + -- Main run of the algorithm: + algorithm:run () + end + + -- If requested, expand multiedges + if algorithm_class.preconditions.simple then + Simplifiers:expandMultiedgesOldModel(algorithm) + end + + -- If requested, restore loops + if algorithm_class.preconditions.loop_free then + Simplifiers:restoreLoopsOldModel(algorithm) + end + + compatibility_graph_to_digraph(graph) +end + + + + +-- Done + +return LayoutPipeline
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/NodeAnchors.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/NodeAnchors.lua new file mode 100644 index 00000000000..b159f21eef2 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/NodeAnchors.lua @@ -0,0 +1,174 @@ +-- Copyright 2012 by Till Tantau +-- +-- This file may be distributed an/or modified +-- +-- 1. under the LaTeX Project Public License and/or +-- 2. under the GNU Public License +-- +-- See the file doc/generic/pgf/licenses/LICENSE for more information + +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/control/NodeAnchors.lua,v 1.3 2013/10/08 09:21:17 tantau Exp $ + +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare + + + +--- +-- @section subsection {Anchoring Edges} +-- +-- \label{section-gd-anchors} +-- +-- When a graph has been layed out completely, the edges between the +-- nodes must be drawn. Conceptually, an edge is ``between two +-- nodes,'' but when we actually draw the node, we do not really want +-- the edge's path to start ``in the middle'' of the node; rather, we +-- want it to start ``on the border'' and also end there. +-- +-- Normally, computing such border positions for nodes is something we +-- would leave to the so-called display layer (which is typically +-- \tikzname\ and \tikzname\ is reasonably good at computing border +-- positions). However, different display layers may behave +-- differently here and even \tikzname\ fails when the node shapes are +-- very involved and the paths also. +-- +-- For these reasons, computing the anchor positions where edges start +-- and end is done inside the graph drawing system. As a user, you +-- specify a |tail anchor| and a |head anchor|, which are points +-- inside the tail and head nodes, respectively. The edge path will +-- then start and end at these points, however, they will usually be +-- shortened so that they actually start and end on the intersection +-- of the edge's path with the nodes' paths. + + +--- + +declare { + key = "tail anchor", + type = "string", + initial = "", + + summary = [[" + Specifies where in the tail vertex the edge should start. + "]], + + documentation = [[" + This is either a string or a number, interpreted as an angle + (with 90 meaning ``up''). If it is a string, when the start of + the edge is computed, we try to look up the anchor in the tail + vertex's table of anchors (some anchors get installed in this + table by the display system). If it is not found, we test + whether it is one of the special ``direction anchors'' like + |north| or |south east|. If so, we convert them into points on + the border of the node that lie in the direction of a line + starting at the center to a point on the bounding box of the + node in the designated direction. Finally, if the anchor is a + number, we use a point on the border of the node that is on a + line from the center in the specified direction. + + If the anchor is set to the empty string (which is the default), + the anchor is interpreted as the |center| anchor inside the + graph drawing system. However, a display system may choose to + make a difference between the |center| anchor and an empty + anchor (\tikzname\ does: for options like |bend left| if the + anchor is empty, the bend line starts at the border of the node, + while for the anchor set explicitly to |center| it starts at the + center). + + Note that graph drawing algorithms need not take the + setting of this option into consideration. However, the final + rendering of the edge will always take it into consideration + (only, the setting may not be very sensible if the algorithm + ignored it). + "]] +} + +--- + +declare { + key = "head anchor", + type = "string", + initial = "", + + summary = "See |tail anchor|" +} + + +--- + +declare { + key = "tail cut", + type = "boolean", + initial = true, + + summary = [[" + Decides whether the tail of an edge is ``cut'', meaning + that the edge's path will be shortened at the beginning so that + it starts only of the node's border rather than at the exact + position of the |tail anchor|, which may be inside the node. + "]] +} + + +--- + +declare { + key = "head cut", + type = "boolean", + initial = true, + + summary = "See |tail cut|." +} + + +--- + +declare { + key = "cut policy", + type = "string", + initial = "as edge requests", + + summary = "The policy for cutting edges entering or leaving a node.", + + documentation = [[" + This option is important for nodes only. It can have three + possible values: + \begin{itemize} + \item |as edge requests| Whether or not an edge entering or + leaving the node is cut depends on the setting of the edge's + |tail cut| and |head cut| options. This is the default. + \item |all| All edges entering or leaving the node are cut, + regardless of the edges' cut values. + \item |none| No edge entering or leaving the node is cut, + regardless of the edges' cut values. + \end{itemize} + "]] +} + + +--- +declare { + key = "allow inside edges", + type = "boolean", + initial = "true", + + summary = "Decides whether an edge between overlapping nodes should be drawn.", + + documentation = [[" + If two vertices overlap, it may happen that when you draw an + edge between them, this edges would be completely inside the two + vertices. In this case, one could either not draw them or one + could draw a sort of ``inside edge''. + "]], + + examples = { [[" + \tikz \graph [no layout, nodes={draw, minimum size=20pt}] { + a [x=0, y=0] -- b [x=15pt, y=10pt] -- c[x=40pt] + }; + "]], [[" + \tikz \graph [no layout, nodes={draw, minimum size=20pt}, + allow inside edges=false] { + a [x=0, y=0] -- b [x=15pt, y=10pt] -- c[x=40pt] + }; + "]] + } +}
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/Orientation.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/Orientation.lua new file mode 100644 index 00000000000..a3205a06dbe --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/Orientation.lua @@ -0,0 +1,314 @@ +-- Copyright 2012 by Till Tantau +-- +-- This file may be distributed an/or modified +-- +-- 1. under the LaTeX Project Public License and/or +-- 2. under the GNU Public License +-- +-- See the file doc/generic/pgf/licenses/LICENSE for more information + +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/control/Orientation.lua,v 1.2 2013/05/23 20:01:26 tantau Exp $ + + + + +--- +-- @section subsection {Orienting a Graph} +-- +-- \label{subsection-library-graphdrawing-standard-orientation} +-- +-- Just as a graph drawing algorithm cannot know \emph{where} a graph +-- should be placed on a page, it is also often unclear which +-- \emph{orientation} it should have. Some graphs, like trees, have a +-- natural direction in which they ``grow'', but for an ``arbitrary'' +-- graph the ``natural orientation'' is, well, arbitrary. +-- +-- There are two ways in which you can specify an orientation: First, +-- you can specify that the line from a certain vertex to another +-- vertex should have a certain slope. How these vertices and slopes +-- are specified in explained momentarily. Second, you can specify a +-- so-called ``growth direction'' for trees. +-- +-- @end + + +-- Namespace +require("pgf.gd.control").Orientation = Orientation + + + +-- Imports +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare + + +--- + +declare { + key = "orient", + type = "direction", + default = 0, + + summary = [[" + This key specifies that the straight line from the |orient tail| to + the |orient head| should be at an angle of \meta{direction} relative to + the right-going $x$-axis. Which vertices are used as tail an head + depends on where the |orient| option is encountered: When used with + an edge, the tail is the edge's tail und the head is the edge's + head. When used with a node, the tail or the head must be specified + explicitly and the node is used as the node missing in the + specification. When used with a graph as a whole, both the head and + tail nodes must be specified explicitly. + "]], + documentation = [[" + Note that the \meta{direction} is independent of the actual to-path + of an edge, which might define a bend or more complicated shapes. For + instance, a \meta{angle} of |45| requests that the end node is ``up + and right'' relative to the start node. + + You can also specify the standard direction texts |north| or |south east| + and so forth as \meta{direction} and also |up|, |down|, |left|, and |right|. Also, you + can specify |-| for ``right'' and \verb!|! for ``down.'' + "]], + examples = { + [[" + \tikz \graph [spring layout] + { + a -- { b, c, d, e -- {f, g, h} }; + h -- [orient=30] a; + }; + "]], + [[" + \tikz \graph [spring layout] + { + a -- { b, c, d[> orient=right], e -- {f, g, h} }; + h -- a; + }; + "]] + } +} + + +--- + +declare { + key = "orient'", + type = "direction", + default = 0, + + summary = [[" + Same as |orient|, only the rest of the graph should be + flipped relative to the connection line. + "]], + examples = [[" + \tikz \graph [spring layout] + { + a -- { b, c, d[> orient'=right], e -- {f, g, h} }; + h -- a; + }; + "]] +} + +--- + +declare { + key = "orient tail", + type = "string", + + summary = [[" + Specifies the tail vertex for the orientation of a graph. See + |orient| for details. + "]], + examples = { + [[" + \tikz \graph [spring layout] { + a [orient=|, orient tail=f] -- { b, c, d, e -- {f, g, h} }; + { h, g } -- a; + }; + "]], + [[" + \tikz \graph [spring layout] { + a [orient=down, orient tail=h] -- { b, c, d, e -- {f, g, h} }; + { h, g } -- a; + }; + "]] + } +} + + + + + +--- + +declare { + key = "orient head", + type = "string", + + summary = [[" + Specifies the head vertex for the orientation of a graph. See + |orient| for details. + "]], + examples = { + [[" + \tikz \graph [spring layout] + { + a [orient=|, orient head=f] -- { b, c, d, e -- {f, g, h} }; + { h, g } -- a; + }; + "]], + [[" + \tikz \graph [spring layout] { a -- b -- c -- a }; + \tikz \graph [spring layout, orient=10, + orient tail=a, orient head=b] { a -- b -- c -- a }; + "]] + } +} + +--- + +declare { + key = "horizontal", + type = "string", + + summary = [[" + A shorthand for specifying |orient tail|, |orient head| and + |orient=0|. The tail will be everything before the part ``| to |'' + and the head will be everything following it. + "]], + examples = [[" + \tikz \graph [spring layout] { a -- b -- c -- a }; + \tikz \graph [spring layout, horizontal=a to b] { a -- b -- c -- a }; + "]] +} + + + + +--- + +declare { + key = "horizontal'", + type = "string", + + summary = [[" + Like |horizontal|, but with a flip. + "]] +} + + + + + + + +--- + +declare { + key = "vertical", + type = "string", + + summary = [[" + A shorthand for specifying |orient tail|, |orient head| and + |orient=-90|. + "]], + examples = [[" + \tikz \graph [spring layout] { a -- b -- c -- a }; + \tikz \graph [spring layout, vertical=a to b] { a -- b -- c -- a }; + "]] +} + + + + + +--- + +declare { + key = "vertical'", + type = "string", + + summary = [[" + Like |vertical|, but with a flip. + "]] +} + + + +--- + +declare { + key = "grow", + type = "direction", + + summary = [[" + This key specifies in which direction the neighbors of a node + ``should grow.'' For some graph drawing algorithms, especially for + those that layout trees, but also for those that produce layered + layouts, there is a natural direction in which the ``children'' of + a node should be placed. For instance, saying |grow=down| will cause + the children of a node in a tree to be placed in a left-to-right + line below the node (as always, you can replace the \meta{angle} + by direction texts). The children are requested to be placed in a + counter-clockwise fashion, the |grow'| key will place them in a + clockwise fashion. + "]], + documentation = [[" + Note that when you say |grow=down|, it is not necessarily the case + that any particular node is actually directly below the current + node; the key just requests that the direction of growth is + downward. + + In principle, you can specify the direction of growth for each node + individually, but do not count on graph drawing algorithms to + honour these wishes. + + When you give the |grow=right| key to the graph as a whole, it will + be applied to all nodes. This happens to be exactly what you want: + "]], + examples = { + [[" + \tikz \graph [layered layout, sibling distance=5mm] + { + a [grow=right] -- { b, c, d, e -- {f, g, h} }; + { h, g } -- a; + }; + "]], + [[" + \tikz \graph [layered layout, grow=right, sibling distance=5mm] + { + a -- { b, c, d, e -- {f, g, h} }; + { h, g } -- a; + }; + "]], + [[" + \tikz + \graph [layered layout, grow=-80] + { + {a,b,c} --[complete bipartite] {e,d,f} + --[complete bipartite] {g,h,i}; + }; + "]] + } +} + + +--- + +declare { + key = "grow'", + type = "direction", + + summary = "Same as |grow|, only with the children in clockwise order.", + examples = [[" + \tikz \graph [layered layout, sibling distance=5mm] + { + a [grow'=right] -- { b, c, d, e -- {f, g, h} }; + { h, g } -- a; + }; + "]] +} + + +-- Done + +return Orientation
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/Sublayouts.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/Sublayouts.lua new file mode 100644 index 00000000000..c10df7a0696 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/Sublayouts.lua @@ -0,0 +1,536 @@ +-- Copyright 2012 by Till Tantau +-- +-- This file may be distributed an/or modified +-- +-- 1. under the LaTeX Project Public License and/or +-- 2. under the GNU Public License +-- +-- See the file doc/generic/pgf/licenses/LICENSE for more information + +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/control/Sublayouts.lua,v 1.4 2013/09/23 20:04:26 tantau Exp $ + +local function full_print(g, pref) + local s = "" + + for _,v in ipairs(g.vertices) do + s = s .. tostring(v) .. "[" .. tostring(v.pos) .. "]\n " + end + + s = s .. "\n" + + for _,a in ipairs(g.arcs) do + for _,e in ipairs(a.syntactic_edges) do + s = s .. tostring(e) .. "(" .. tostring(e.path) .. ")\n" + end + end + + pgf.debug((pref or "") .. s) +end + + +--- +-- The |Sublayouts| module handles graphs for which multiple layouts are defined. +-- +-- Please see Section~\ref{section-gd-sublayouts} for an overview of +-- sublayouts. +-- + +local Sublayouts = {} + +-- Namespace +require("pgf.gd.control").Sublayouts = Sublayouts + + +-- Includes + +local Digraph = require "pgf.gd.model.Digraph" +local Vertex = require "pgf.gd.model.Vertex" +local Coordinate = require "pgf.gd.model.Coordinate" +local Path = require "pgf.gd.model.Path" + +local lib = require "pgf.gd.lib" + +local InterfaceCore = require "pgf.gd.interface.InterfaceCore" + +local Storage = require "pgf.gd.lib.Storage" + + + +-- Storages + +local subs = Storage.newTableStorage() +local already_nudged = Storage.new() +local positions = Storage.newTableStorage() + + + + +-- Offset a node by an offset. This will \emph{also} offset all +-- subnodes, which arise from sublayouts. +-- +-- @param vertex A vertex +-- @param pos A offset +-- +local function offset_vertex(v, delta) + v.pos:shiftByCoordinate(delta) + for _,sub in ipairs(subs[v]) do + offset_vertex(sub, delta) + end +end + + +-- Nudge positioning. You can call this function several times on the +-- same graph; nudging will be done only once. +-- +-- @param graph A graph +-- +local function nudge(graph) + for _,v in ipairs(graph.vertices) do + local nudge = v.options['nudge'] + if nudge and not already_nudged[v] then + offset_vertex(v, nudge) + already_nudged[v] = true + end + end +end + + + +-- Create subgraph nodes +-- +-- @param scope A scope +-- @param syntactic_digraph The syntactic digraph. +-- @param test Only for vertices whose subgraph collection passes this test will we create subgraph nodes +local function create_subgraph_node(scope, syntactic_digraph, vertex) + + local subgraph_collection = vertex.subgraph_collection + local binding = InterfaceCore.binding + + local cloud = {} + -- Add all points of n's collection, except for v itself, to the cloud: + for _,v in ipairs(subgraph_collection.vertices) do + if vertex ~= v then + assert(syntactic_digraph:contains(v), "the layout must contain all nodes of the subgraph") + for _,p in ipairs(v.path) do + if type(p) == "table" then + cloud[#cloud+1] = p + v.pos + end + end + end + end + for _,e in ipairs(subgraph_collection.edges) do + for _,p in ipairs(e.path) do + if type(p) == "table" then + cloud[#cloud+1] = p:clone() + end + end + end + local x_min, y_min, x_max, y_max, c_x, c_y = Coordinate.boundingBox(cloud) + + -- Shift the graph so that it is centered on the origin: + for _,p in ipairs(cloud) do + p:unshift(c_x,c_y) + end + + local o = vertex.subgraph_info.generated_options + + o[#o+1] = { key = "subgraph point cloud", value = table.concat(lib.imap(cloud, tostring)) } + o[#o+1] = { key = "subgraph bounding box height", value = tostring(y_max-y_min) .. "pt" } + o[#o+1] = { key = "subgraph bounding box width", value = tostring(x_max-x_min) .. "pt" } + + -- And now, the "grand call": + binding:createVertex(vertex.subgraph_info) + + -- Shift it were it belongs + vertex.pos:shift(c_x,c_y) + + -- Remember all the subnodes for nudging and regardless + -- positioning + local s = {} + for _,v in ipairs(subgraph_collection.vertices) do + if v ~= vertex then + s[#s+1] = v + end + end + + subs[vertex] = s +end + + +-- Tests whether two graphs have a vertex in common +local function intersection(g1, g2) + for _,v in ipairs(g1.vertices) do + if g2:contains(v) then + return v + end + end +end + +-- Tests whether a graph is a set is a subset of another +local function special_vertex_subset(vertices, graph) + for _,v in ipairs(vertices) do + if not graph:contains(v) and not (v.kind == "subgraph node") then + return false + end + end + return true +end + + + +--- +-- The layout recursion. See \ref{section-gd-sublayouts} for details. +-- +-- @param scope The graph drawing scope +-- @param layout The to-be-laid-out collection +-- @param fun The to-be-called function for laying out the graph. +-- processed. This stack is important when a new syntactic vertex is +-- added by the algorithm: In this case, this vertex is added to all +-- layouts on this stack. +-- +-- @return A laid out graph. + +function Sublayouts.layoutRecursively(scope, layout, fun) + + -- Step 1: Iterate over all sublayouts of the current layout: + local resulting_graphs = {} + local loc = Storage.new() + + -- Now, iterate over all sublayouts + for i,child in ipairs(layout:childrenOfKind(InterfaceCore.sublayout_kind)) do + resulting_graphs[i] = Sublayouts.layoutRecursively(scope, child, fun) + loc[resulting_graphs[i]] = child + end + + -- Step 2: Run the merge process: + local merged_graphs = {} + + while #resulting_graphs > 0 do + + local n = #resulting_graphs + + -- Setup marked array: + local marked = {} + for i=1,n do + marked[i] = false + end + + -- Mark first graph and copy everything from there + marked[1] = true + local touched = Storage.new() + for _,v in ipairs(resulting_graphs[1].vertices) do + v.pos = positions[v][resulting_graphs[1]] + touched[v] = true + end + + -- Repeatedly find a node that is connected to a marked node: + local i = 1 + while i <= n do + if not marked[i] then + for j=1,n do + if marked[j] then + local v = intersection(resulting_graphs[i], resulting_graphs[j]) + if v then + -- Aha, they intersect at vertex v + + -- Mark the i-th graph: + marked[i] = true + connected_some_graph = true + + -- Shift the i-th graph: + local x_offset = v.pos.x - positions[v][resulting_graphs[i]].x + local y_offset = v.pos.y - positions[v][resulting_graphs[i]].y + + for _,u in ipairs(resulting_graphs[i].vertices) do + if not touched[u] then + touched[u] = true + u.pos = positions[u][resulting_graphs[i]]:clone() + u.pos:shift(x_offset, y_offset) + + for _,a in ipairs(resulting_graphs[i]:outgoing(u)) do + for _,e in ipairs(a.syntactic_edges) do + for _,p in ipairs(e.path) do + if type(p) == "table" then + p:shift(x_offset, y_offset) + end + end + end + end + end + end + + -- Restart + i = 0 + break + end + end + end + end + i = i + 1 + end + + -- Now, we can collapse all marked graphs into one graph: + local merge = Digraph.new {} + merge.syntactic_digraph = merge + local remaining = {} + + -- Add all vertices and edges: + for i=1,n do + if marked[i] then + merge:add (resulting_graphs[i].vertices) + for _,a in ipairs(resulting_graphs[i].arcs) do + local ma = merge:connect(a.tail,a.head) + for _,e in ipairs(a.syntactic_edges) do + ma.syntactic_edges[#ma.syntactic_edges+1] = e + end + end + else + remaining[#remaining + 1] = resulting_graphs[i] + end + end + + -- Remember the first layout this came from: + loc[merge] = loc[resulting_graphs[1]] + + -- Restart with rest: + merged_graphs[#merged_graphs+1] = merge + + resulting_graphs = remaining + end + + -- Step 3: Run the algorithm on the layout: + + local class = layout.options.algorithm_phases.main + assert (type(class) == "table", "algorithm selection failed") + + local algorithm = class + local uncollapsed_subgraph_nodes = lib.imap( + scope.collections[InterfaceCore.subgraph_node_kind] or {}, + function (c) + if c.parent_layout == layout then + return c.subgraph_node + end + end) + + + -- Create a new syntactic digraph: + local syntactic_digraph = Digraph.new { + options = layout.options + } + + syntactic_digraph.syntactic_digraph = syntactic_digraph + + -- Copy all vertices and edges from the collection... + syntactic_digraph:add (layout.vertices) + for _,e in ipairs(layout.edges) do + syntactic_digraph:add {e.head, e.tail} + local arc = syntactic_digraph:connect(e.tail, e.head) + arc.syntactic_edges[#arc.syntactic_edges+1] = e + end + + -- Find out which subgraph nodes can be created now and make them part of the merged graphs + for i=#uncollapsed_subgraph_nodes,1,-1 do + local v = uncollapsed_subgraph_nodes[i] + local vertices = v.subgraph_collection.vertices + -- Test, if all vertices of the subgraph are in one of the merged graphs. + for _,g in ipairs(merged_graphs) do + if special_vertex_subset(vertices, g) then + -- Ok, we can create a subgraph now + create_subgraph_node(scope, syntactic_digraph, v) + -- Make it part of the collapse! + g:add{v} + -- Do not consider again + uncollapsed_subgraph_nodes[i] = false + break + end + end + end + + -- Collapse the nodes that are part of a merged_graph + local collapsed_vertices = {} + for _,g in ipairs(merged_graphs) do + + local intersection = {} + for _,v in ipairs(g.vertices) do + if syntactic_digraph:contains(v) then + intersection[#intersection+1] = v + end + end + if #intersection > 0 then + -- Compute bounding box of g (this should actually be the convex + -- hull) Hmm...: + local array = {} + for _,v in ipairs(g.vertices) do + local min_x, min_y, max_x, max_y = v:boundingBox() + array[#array+1] = Coordinate.new(min_x + v.pos.x, min_y + v.pos.y) + array[#array+1] = Coordinate.new(max_x + v.pos.x, max_y + v.pos.y) + end + for _,a in ipairs(g.arcs) do + for _,e in ipairs(a.syntactic_edges) do + for _,p in ipairs(e.path) do + if type(p) == "table" then + array[#array+1] = p + end + end + end + end + local x_min, y_min, x_max, y_max, c_x, c_y = Coordinate.boundingBox(array) + + -- Shift the graph so that it is centered on the origin: + for _,v in ipairs(g.vertices) do + v.pos:unshift(c_x,c_y) + end + for _,a in ipairs(g.arcs) do + for _,e in ipairs(a.syntactic_edges) do + for _,p in ipairs(e.path) do + if type(p) == "table" then + p:unshift(c_x,c_y) + end + end + end + end + + x_min = x_min - c_x + x_max = x_max - c_x + y_min = y_min - c_y + y_max = y_max - c_y + + local index = loc[g].event.index + + local v = Vertex.new { + -- Standard stuff + shape = "none", + kind = "node", + path = Path.new { + "moveto", + x_min, y_min, + x_min, y_max, + x_max, y_max, + x_max, y_min, + "closepath" + }, + options = {}, + event = scope.events[index] + } + + -- Update node_event + scope.events[index].parameters = v + + local collapse_vertex = syntactic_digraph:collapse( + intersection, + v, + nil, + function (new_arc, arc) + for _,e in ipairs(arc.syntactic_edges) do + new_arc.syntactic_edges[#new_arc.syntactic_edges+1] = e + end + end) + + syntactic_digraph:remove(intersection) + collapsed_vertices[#collapsed_vertices+1] = collapse_vertex + end + end + + -- Sort the vertices + table.sort(syntactic_digraph.vertices, function(u,v) return u.event.index < v.event.index end) + + -- Should we "hide" the subgraph nodes? + local hidden_node + if not algorithm.include_subgraph_nodes then + local subgraph_nodes = lib.imap (syntactic_digraph.vertices, + function (v) if v.kind == "subgraph node" then return v end end) + + if #subgraph_nodes > 0 then + hidden_node = Vertex.new {} + syntactic_digraph:collapse(subgraph_nodes, hidden_node) + syntactic_digraph:remove (subgraph_nodes) + syntactic_digraph:remove {hidden_node} + end + end + + -- Now, we want to call the actual algorithm. This call may modify + -- the layout's vertices and edges fields, namely when new vertices + -- and edges are created. We then need to add these to our local + -- syntactic digraph. So, we remember the length of these fields + -- prior to the call and then add everything ``behind'' these + -- positions later on. + + -- Ok, everything setup! Run the algorithm... + fun(scope, algorithm, syntactic_digraph, layout) + + if hidden_node then + syntactic_digraph:expand(hidden_node) + end + + -- Now, we need to expand the collapsed vertices once more: + for i=#collapsed_vertices,1,-1 do + syntactic_digraph:expand( + collapsed_vertices[i], + function (c, v) + v.pos:shiftByCoordinate(c.pos) + end, + function (a, v) + for _,e in ipairs(a.syntactic_edges) do + for _,p in ipairs(e.path) do + if type(p) == "table" then + p:shiftByCoordinate(v.pos) + end + end + end + end + ) + for _,a in ipairs(syntactic_digraph:outgoing(collapsed_vertices[i])) do + for _,e in ipairs(a.syntactic_edges) do + for _,p in ipairs(e.path) do + if type(p) == "table" then + p:shiftByCoordinate(a.tail.pos) + p:unshiftByCoordinate(e.tail.pos) + end + end + end + end + end + syntactic_digraph:remove(collapsed_vertices) + + -- Step 4: Create the layout node if necessary + for i=#uncollapsed_subgraph_nodes,1,-1 do + if uncollapsed_subgraph_nodes[i] then + create_subgraph_node(scope, syntactic_digraph, uncollapsed_subgraph_nodes[i]) + end + end + + -- Now seems like a good time to nudge and do regardless positioning + nudge(syntactic_digraph) + + -- Step 5: Cleanup + -- Push the computed position into the storage: + for _,v in ipairs(syntactic_digraph.vertices) do + positions[v][syntactic_digraph] = v.pos:clone() + end + + return syntactic_digraph +end + + + + + +--- +-- Regardless positioning. +-- +-- @param graph A graph +-- +function Sublayouts.regardless(graph) + for _,v in ipairs(graph.vertices) do + local regardless = v.options['regardless at'] + if regardless then + offset_vertex(v, regardless - v.pos) + end + end +end + + + +-- Done + +return Sublayouts
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/library.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/library.lua new file mode 100644 index 00000000000..fbb3a33857d --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/library.lua @@ -0,0 +1,199 @@ +-- Copyright 2012 by Till Tantau +-- +-- This file may be distributed an/or modified +-- +-- 1. under the LaTeX Project Public License and/or +-- 2. under the GNU Public License +-- +-- See the file doc/generic/pgf/licenses/LICENSE for more information + +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/control/library.lua,v 1.9 2013/05/23 20:01:26 tantau Exp $ + + +-- Load declarations from: + +require "pgf.gd.control.FineTune" +require "pgf.gd.control.Anchoring" +require "pgf.gd.control.Sublayouts" +require "pgf.gd.control.Orientation" +require "pgf.gd.control.Distances" +require "pgf.gd.control.Components" +require "pgf.gd.control.ComponentAlign" +require "pgf.gd.control.ComponentDirection" +require "pgf.gd.control.ComponentDistance" +require "pgf.gd.control.ComponentOrder" +require "pgf.gd.control.NodeAnchors" + + +local InterfaceCore = require "pgf.gd.interface.InterfaceCore" +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare +local lib = require "pgf.gd.lib" + + + +--- + +declare { + key = "nodes behind edges", + type = "boolean", + + summary = "Specifies, that nodes should be drawn behind the edges", + documentation = [[" + Once a graph drawing algorithm has determined positions for the nodes, + they are drawn \emph{before} the edges are drawn; after + all, it is hard to draw an edge between nodes when their positions + are not yet known. However, we typically want the nodes to be + rendered \emph{after} or rather \emph{on top} of the edges. For + this reason, the default behaviour is that the nodes at their + final positions are collected in a box that is inserted into the + output stream only after the edges have been drawn -- which has + the effect that the nodes will be placed ``on top'' of the + edges. + + This behaviour can be changed using this option. When the key is + invoked, nodes are placed \emph{behind} the edges. + "]], + examples = [[" + \tikz \graph [simple necklace layout, nodes={draw,fill=white}, + nodes behind edges] + { subgraph K_n [n=7], 1 [regardless at={(0,-1)}] }; + "]] +} + + +--- + +declare { + key = "edges behind nodes", + use = { + { key = "nodes behind edges", value = "false" }, + }, + + summary = [[" + This is the default placemenet of edges: Behind the nodes. + "]], + examples = [[" + \tikz \graph [simple necklace layout, nodes={draw,fill=white}, + edges behind nodes] + { subgraph K_n [n=7], 1 [regardless at={(0,-1)}] }; + "]] +} + +--- +declare { + key = "random seed", + type = "number", + initial = "42", + + summary = [[" + To ensure that the same is always shown in the same way when the + same algorithm is applied, the random is seed is reset on each call + of the graph drawing engine. To (possibly) get different results on + different runs, change this value. + "]] +} + + +--- +declare { + key = "variation", + type = "number", + use = { + { key = "random seed", value = lib.id }, + }, + summary = "An alias for |random seed|." +} + + +--- +declare { + key = "weight", + type = "number", + initial = 1, + + summary = [[" + Sets the ``weight'' of an edge or a node. For many algorithms, this + number tells the algorithm how ``important'' the edge or node is. + For instance, in a |layered layout|, an edge with a large |weight| + will be as short as possible. + "]], + examples = { + [[" + \tikz \graph [layered layout] { + a -- {b,c,d} -- e -- a; + }; + "]], + [[" + \tikz \graph [layered layout] { + a -- {b,c,d} -- e --[weight=3] a; + }; + "]] + } +} + + + +--- +declare { + key = "length", + type = "length", + initial = 1, + + summary = [[" + Sets the ``length'' of an edge. Algorithms may take this value + into account when drawing a graph. + "]], + examples = { + [[" + \tikz \graph [phylogenetic tree layout] { + a --[length=2] b --[length=1] {c,d}; + a --[length=3] e + }; + "]], + } +} + + +--- + +declare { + key = "radius", + type = "number", + initial = "0", + + summary = [[" + The radius of a circular object used in graph drawing. + "]] +} + +--- + +declare { + key = "no layout", + algorithm = { + run = + function (self) + for _,v in ipairs(self.digraph.vertices) do + if v.options['desired at'] then + v.pos.x = v.options['desired at'].x + v.pos.y = v.options['desired at'].y + end + end + end }, + summary = "This layout does nothing.", +} + + + +-- The following collection kinds are internal + +declare { + key = InterfaceCore.sublayout_kind, + layer = 0 +} + +declare { + key = InterfaceCore.subgraph_node_kind, + layer = 0 +} + |