diff options
author | Karl Berry <karl@freefriends.org> | 2015-08-08 22:54:29 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2015-08-08 22:54:29 +0000 |
commit | 531d43fafa269c546d587eaca6cd14adcd11914f (patch) | |
tree | 1883933af984c60254e6d9d1bd955a76748cb827 /Master/texmf-dist/tex/generic/pgf/graphdrawing | |
parent | 877e963d44f039783cb9227d90c911866c780961 (diff) |
pgf (8aug15)
git-svn-id: svn://tug.org/texlive/trunk@38079 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/generic/pgf/graphdrawing')
48 files changed, 4371 insertions, 75 deletions
diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/bindings/BindingToPGF.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/bindings/BindingToPGF.lua index 5a1337a1327..0ad475fbf3e 100644 --- a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/bindings/BindingToPGF.lua +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/bindings/BindingToPGF.lua @@ -7,7 +7,7 @@ -- -- See the file doc/generic/pgf/licenses/LICENSE for more information --- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/bindings/BindingToPGF.lua,v 1.10 2013/12/20 14:44:46 tantau Exp $ +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/bindings/BindingToPGF.lua,v 1.11 2015/06/08 21:58:24 tantau Exp $ @@ -75,7 +75,7 @@ local table_in_pgf_syntax function BindingToPGF:resumeGraphDrawingCoroutine(text) tex.print(text) - tex.print("\\directlua{pgf.gd.interface.InterfaceToDisplay.resumeGraphDrawingCoroutine()}") + tex.print("\\pgfgdresumecoroutinetrue") end diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/circular/Tantau2012.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/circular/Tantau2012.lua index 774bdc907be..1966a3066c5 100644 --- a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/circular/Tantau2012.lua +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/circular/Tantau2012.lua @@ -7,12 +7,14 @@ -- -- See the file doc/generic/pgf/licenses/LICENSE for more information --- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/circular/Tantau2012.lua,v 1.7 2013/05/23 20:01:25 tantau Exp $ +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/circular/Tantau2012.lua,v 1.9 2014/03/19 09:20:57 tantau Exp $ -- Imports local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare +local routing = require("pgf.gd.routing") + -- The algorithm class local Tantau2012 = {} @@ -25,7 +27,7 @@ declare { upward_oriented = true }, - documentation_in = "pgf.gd.doc.circular.Tantau2012" + documentation_in = "pgf.gd.circular.doc" } @@ -33,14 +35,17 @@ declare { -- Imports local Coordinate = require "pgf.gd.model.Coordinate" +local Hints = require "pgf.gd.routing.Hints" + +local lib = require "pgf.gd.lib" + -local lib = require "pgf.gd.lib" -- The implementation function Tantau2012:run() - local g = self.digraph + local g = self.ugraph local vertices = g.vertices local n = #vertices @@ -75,6 +80,10 @@ function Tantau2012:run() vertex.pos.x = radius * math.cos(2 * math.pi * (positions[i] / length + 1/4)) vertex.pos.y = -radius * math.sin(2 * math.pi * (positions[i] / length + 1/4)) end + + -- Add routing infos + local necklace = lib.icopy({g.vertices[1]}, lib.icopy(g.vertices)) + Hints.addNecklaceCircleHint(g, necklace, nil, true) end diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/doc/circular/Tantau2012.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/circular/doc.lua index e40a0bbdb1f..d3c267394c4 100644 --- a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/doc/circular/Tantau2012.lua +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/circular/doc.lua @@ -7,7 +7,7 @@ -- -- See the file doc/generic/pgf/licenses/LICENSE for more information --- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/doc/circular/Tantau2012.lua,v 1.1 2013/03/05 23:44:30 tantau Exp $ +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/circular/doc.lua,v 1.1 2014/03/19 09:20:57 tantau Exp $ local key = require 'pgf.gd.doc'.key local documentation = require 'pgf.gd.doc'.documentation 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 index e4364d695cd..a3f5de83194 100644 --- 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 @@ -7,7 +7,7 @@ -- -- 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 $ +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/control/Anchoring.lua,v 1.7 2014/03/19 09:20:58 tantau Exp $ @@ -68,14 +68,14 @@ local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare declare { key = "desired at", type = "coordinate", - documentation_in = "pgf.gd.doc.control.Anchoring" + documentation_in = "pgf.gd.control.doc" } --- declare { key = "anchor node", type = "string", - documentation_in = "pgf.gd.doc.control.Anchoring" + documentation_in = "pgf.gd.control.doc" } @@ -84,7 +84,7 @@ declare { key = "anchor at", type = "canvas coordinate", initial = "(0pt,0pt)", - documentation_in = "pgf.gd.doc.control.Anchoring" + documentation_in = "pgf.gd.control.doc" } @@ -92,7 +92,7 @@ declare { declare { key = "anchor here", type = "boolean", - documentation_in = "pgf.gd.doc.control.Anchoring" + documentation_in = "pgf.gd.control.doc" } 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 index c32f9d084f6..7a6a910dfa0 100644 --- 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 @@ -7,7 +7,7 @@ -- -- 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 $ +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/control/LayoutPipeline.lua,v 1.13 2014/03/19 09:20:58 tantau Exp $ --- @@ -65,6 +65,33 @@ -- ``ideally'' rotated the graph, set this postcondition. -- \end{itemize} -- +-- +-- In addition to the above-described always-present and automatic +-- transformations, users may also specify additional pre- and +-- posttransformations. This happens when users install additional +-- algorithms in appropriate phases. In detail, the following happens +-- in order: +-- +-- \begin{enumerate} +-- \item If specified, the graph is decomposed into connected +-- components and the following steps are applied to each component +-- individually. +-- \item All algorithms in the phase stack for the phase +-- |preprocessing| are applied to the component. These algorithms are +-- run one after the other in the order they appear in the phase +-- stack. +-- \item If necessary, the spanning tree is now computed and +-- rotational information is gathered. +-- \item The single algorithm in phase |main| is called. +-- \item All algorithms in the phase stack for the phase +-- |edge routing| are run. +-- \item All algorithms in the phase stack for phase |postprocessing| +-- are run. +-- \item Edge syncing, orientation, and anchoring are applied. +-- \end{enumerate} +-- +-- If sublayouts are used, all of the above (except for anchoring) +-- happens for each sublayout. local LayoutPipeline = {} @@ -130,7 +157,7 @@ function LayoutPipeline.run(scope) -- Step 4: Apply regardless transforms Sublayouts.regardless(scope.syntactic_digraph) - + -- Step 5: Cut edges LayoutPipeline.cutEdges(scope.syntactic_digraph) @@ -162,7 +189,7 @@ function LayoutPipeline.runOnLayout(scope, algorithm_class, layout_graph, layout 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 @@ -191,6 +218,18 @@ function LayoutPipeline.runOnLayout(scope, algorithm_class, layout_graph, layout -- Step 2.4: Precompute the underlying undirected graph local ugraph = Direct.ugraphFromDigraph(digraph) + + -- Step 2.4a: Run preprocessor + for _,class in ipairs(layout_graph.options.algorithm_phases["preprocessing stack"]) do + class.new{ + digraph = digraph, + ugraph = ugraph, + scope = scope, + layout = layout, + layout_graph = layout_graph, + syntactic_component = syntactic_component, + }:run() + end -- Step 2.5: Create an algorithm object local algorithm = algorithm_class.new{ @@ -228,6 +267,30 @@ function LayoutPipeline.runOnLayout(scope, algorithm_class, layout_graph, layout end end + -- Step 2.9a: Run edge routers + for _,class in ipairs(layout_graph.options.algorithm_phases["edge routing stack"]) do + class.new{ + digraph = digraph, + ugraph = ugraph, + scope = scope, + layout = layout, + layout_graph = layout_graph, + syntactic_component = syntactic_component, + }:run() + end + + -- Step 2.9b: Run postprocessor + for _,class in ipairs(layout_graph.options.algorithm_phases["postprocessing stack"]) do + class.new{ + digraph = digraph, + ugraph = ugraph, + scope = scope, + layout = layout, + layout_graph = layout_graph, + syntactic_component = syntactic_component, + }:run() + end + -- Step 2.10: Sync the graphs digraph:sync() ugraph:sync() @@ -241,6 +304,7 @@ function LayoutPipeline.runOnLayout(scope, algorithm_class, layout_graph, layout -- Step 3: Packing: LayoutPipeline.packComponents(layout_graph, syntactic_components) + end diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/doc/control/Anchoring.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/doc.lua index 0a80baf0ca5..ac2b186f5d5 100644 --- a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/doc/control/Anchoring.lua +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/control/doc.lua @@ -7,7 +7,7 @@ -- -- See the file doc/generic/pgf/licenses/LICENSE for more information --- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/doc/control/Anchoring.lua,v 1.1 2013/03/05 23:44:30 tantau Exp $ +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/control/doc.lua,v 1.1 2014/03/19 09:20:58 tantau Exp $ local key = require 'pgf.gd.doc'.key local documentation = require 'pgf.gd.doc'.documentation diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/algorithms/FruchtermanReingold.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/algorithms/FruchtermanReingold.lua new file mode 100644 index 00000000000..6c9677aeabb --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/algorithms/FruchtermanReingold.lua @@ -0,0 +1,118 @@ +-- Copyright 2014 by Ida Bruhns +-- +-- This file may be distributed and/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 + +local SpringElectricNoCoarsenClass = {} + +-- Imports +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare +local ForceController = require "pgf.gd.force.jedi.base.ForceController" +local ForceCanvasDistance = require "pgf.gd.force.jedi.forcetypes.ForceCanvasDistance" +local ForceGraphDistance = require "pgf.gd.force.jedi.forcetypes.ForceGraphDistance" +local Storage = require "pgf.gd.lib.Storage" + +--- +declare { + key = "spring electric no coarsen layout", + algorithm = SpringElectricNoCoarsenClass, + preconditions = { connected = true }, + postconditions = {fixed = true}, + + summary = [[This layout uses the algorithm proposed by Fruchterman and Reingold to draw graphs."]], + + documentation = + [[The Fruchterman-Reingold algorithm is one if the oldest methods + for force-based graph drawing. It is described in: + \begin{itemize} + \item + Thomas M.~J.~ Fruchterman and Edward M.~ Reingold, + \newblock Graph Drawing by Force-directed Placement, + \newblock \emph{Software -- practice and experience,} + 21(1 1), 1129-1164, 1991. + \end{itemize} + Fruchterman and Reingold had to principles in graph drawing: + \begin{enumerate} + \item Vertices connected by an edge should be drawn close toa another and + \item in general, vertices should not be drawn too close to each other. + \end{itemize} + The spring electric no coarsen layout uses spring forces as attractive + forces influecing vertex pairs connected by an edge and electric forces + as repulsive forces between all vertex pairs. The original algorithm + also contained a frame that stopped the vertices from drifting too far + apart, but this concept was not implemented. This algorithm will not be affected + by coarsening. This layout was implemented + by using the Jedi framework. + ]], + + example = + [[ + \tikz + \graph[spring electric no coarsen layout, speed = 0.35, node distance = 2.5cm, nodes={as=,circle, draw, inner sep=3pt,outer sep=0pt}, coarsen = true, maximum step = 1]{ + a -- {b, c, d, e, f, g, h, i, j}, + b -- {c, d, e, f, g, h, i, j}, + c -- {d, e, f, g, h, i, j}, + d -- {e, f, g, h, i, j}, + e -- {f, g, h, i, j}, + f -- {g, h, i, j}, + g -- {h, i, j}, + h -- {i, j}, + i -- j + }; + ]], + + example = + [[ + \graph[spring electric no coarsen layout, speed = 0.25, node distance = 0.25cm, horizontal = c to l, nodes={as=,circle, draw, inner sep=3pt,outer sep=0pt}, coarsen = false, maximum step = 1]{ + a -> b -> c -> {d1 -> e -> f -> g -> h -> i -> {j1 -> e, j2 -> l}, d2 -> l -> m}, m -> a + }; + ]] +} + + + + +-- Implementation starts here + +--define a local time function +local time_fun_1 +function time_fun_1 (t_total, t_now) + if t_now/t_total <= 0.5 then + return 0.5 + else + return 2 + end +end + +-- define storage table to add attributes if wanted +local fw_attributes = Storage.newTableStorage() + +function SpringElectricNoCoarsenClass:run() + -- add options to storage table + fw_attributes.options = self.ugraph.options + + --Generate new force class + local spring_electric_no_coarsen = ForceController.new(self.ugraph) + + spring_electric_no_coarsen:addForce{ + force_type = ForceCanvasDistance, + fun_u = function (data) return data.k*data.k/(data.d) end, + time_fun = time_fun_1, + epoch = {"after expand"} + } + spring_electric_no_coarsen:addForce{ + force_type = ForceGraphDistance, + fun_u = function (data) return -data.d*data.d/(data.k) end, + n = 1, + epoch = {"after expand"} + } + + -- run algorithm + spring_electric_no_coarsen:run() +end + +return SpringElectricNoCoarsenClass
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/algorithms/HuSpringElectricalFW.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/algorithms/HuSpringElectricalFW.lua new file mode 100644 index 00000000000..08ccad1308f --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/algorithms/HuSpringElectricalFW.lua @@ -0,0 +1,93 @@ +-- Copyright 2014 by Ida Bruhns +-- +-- This file may be distributed and/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 + +local HuClass = {} + +-- Imports +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare +local ForceController = require "pgf.gd.force.jedi.base.ForceController" +local ForceCanvasDistance = require "pgf.gd.force.jedi.forcetypes.ForceCanvasDistance" +local ForceGraphDistance = require "pgf.gd.force.jedi.forcetypes.ForceGraphDistance" + +--- +declare { + key = "jedi spring electric layout", + algorithm = HuClass, + documentation_in = "documentation_hu_layout", + preconditions = { connected = true }, + postconditions = {fixed = true}, + + summary = "This layout uses the spring electric algorithm proposed by Hu to draw graphs.", + + documentation = + [[The spring electric algorithm by Hu uses two kinds of forces and coarsening. + It is described in: + \begin{itemize} + \item + Yifan Hu, + \newblock Efficient, high quality force-directed graph drawing, + \newblock \emph{The Mathematica Journal,} + 10(1), 37-71, 2006. + \end{itemize} + This algorithm uses spring forces as attractive forces between vertices + connected by an edge and electric forces as repulsive forces between + all vertex pairs. Hu introduces coarsening, a procedure which repeatedly + merges vertices in order to obtain a smaller version of the graph, to + overcome local minima. He also uses the Barnes-Hut algorithm to enhance + the runtime of his algorithms. This algorithm is not used in this + implementation. This layout was implemented by using the Jedi framework. + ]], + + example = + [[ + \tikz + \graph[spring electric fw layout, speed = 0.35, node distance = 5cm, nodes={as=,circle, draw, inner sep=3pt,outer sep=0pt}, maximum displacement per step = 10]{ + a -- {b, c, d, e}, + b -- {c, d, e}, + c -- {d, e}, + d --e + }; + ]], + + example = + [[ + \tikz + \graph[spring electric fw layout, speed = 0.35, node distance = 1cm, horizontal = c to l, nodes={as=,circle, draw, inner sep=3pt,outer sep=0pt}, maximum displacement per step = 10]{ + a -> b -> c -> {d1 -> e -> f -> g -> h -> i -> {j1 -> e, j2 -> l}, d2 -> l -> m}, m -> a + }; + ]] +} + + + + +-- Implementation starts here: + +function HuClass:run() + -- Generate new force class + local hu = ForceController.new(self.ugraph) + + -- add all required forces + hu:addForce{ + force_type = ForceCanvasDistance, + fun_u = function (data) return (data.k*data.k)/data.d end, + epoch = {"during expand", "after expand"} + } + hu:addForce{ + force_type = ForceGraphDistance, + fun_u = function (data) return -(data.d*data.d)/data.k end, + n = 1, + epoch = {"during expand", "after expand"} + } + + -- run algorithm + hu:run() +end + +return HuClass
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/algorithms/SimpleSpring.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/algorithms/SimpleSpring.lua new file mode 100644 index 00000000000..20a1be5de0e --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/algorithms/SimpleSpring.lua @@ -0,0 +1,75 @@ +-- Copyright 2014 by Ida Bruhns +-- +-- This file may be distributed and/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 + +local SimpleSpringClass = {} + +-- Imports +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare +local ForceController = require 'pgf.gd.force.jedi.base.ForceController' +local ForceGraphDistance = require "pgf.gd.force.jedi.forcetypes.ForceGraphDistance" + +--- +declare { + key = "trivial spring layout", + algorithm = SimpleSpringClass, + documentation_in = "pgf.gd.doc.jedi.algorithms.SimpleSpringLayout", + preconditions = { connected = true }, + postconditions = {fixed = true}, + + summary = "This layout uses only spring forces to draw graphs.", + + documentation = + [[The simple spring algorithm only uses one force kind: A spring force + that serves as both attracitve and repuslive force. The edges are modeled as + springs and act according to Hoke's law: They have an ideal length and will + expand if they are contracted below this length, pushing the adjacent + vertices away from each other, and contract if it is stretched, pulling the + adjacent vertices towards each other. This ideal length is given by the + parameter |node distance|. There is no force repelling vertices that are not + connected to each other, which can lead to vertices being placed at the same + point. It is not a very powerfull layout and will probably fail with large + graphs, especially if they have few edges. It can however be used to + demonstrate the effect of spring forces. This layout was implemented by using + the Jedi framework. + ]], + + example = + [[ + \tikz + \graph[simple spring layout, node distance = 3cm, speed = 2, nodes={as=,circle, draw, inner sep=3pt,outer sep=0pt}, coarsen = true, maximum step = 1]{ + a -- {b, c, d, e}, + b -- {c, d, e}, + c -- {d, e}, + d --e + }; + ]] +} + + + + +-- Implementation starts here: + +function SimpleSpringClass:run() + --Generate new force class + simple_spring = ForceController.new(self.ugraph) + + --add all required forces + simple_spring:addForce{ + force_type = ForceGraphDistance, + fun_u = function (data) return data.k*(data.k-data.d) end, + n = 1, + epoch = {"after expand", "during expand"} + } + + -- run algorithm + simple_spring:run() +end + +return SimpleSpringClass
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/algorithms/SocialGravityCloseness.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/algorithms/SocialGravityCloseness.lua new file mode 100644 index 00000000000..12e6c02e4bf --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/algorithms/SocialGravityCloseness.lua @@ -0,0 +1,128 @@ +-- Copyright 2014 by Ida Bruhns +-- +-- This file may be distributed and/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 + +local SocialClass = {} + +--Imports +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare +local ForceController = require 'pgf.gd.force.jedi.base.ForceController' +local ForceCanvasDistance = require "pgf.gd.force.jedi.forcetypes.ForceCanvasDistance" +local ForceCanvasPosition = require "pgf.gd.force.jedi.forcetypes.ForceCanvasPosition" +local ForceGraphDistance = require "pgf.gd.force.jedi.forcetypes.ForceGraphDistance" +local PathLengthsFW = require "pgf.gd.force.jedi.base.PathLengthsFW" +local Storage = require "pgf.gd.lib.Storage" + +--- +declare { + key = "social closeness layout", + algorithm = SocialClass, + postconditions = {fixed = true}, + + summary = [[This layout uses the social gravity algorithm proposed by Bannister + with closeness mass to draw graphs.]], + + documentation = + [[Bannister et all described a social gravity algorithm that can be + implemented with different kinds of gravity. + It is described in: + \begin{itemize} + \item + Michael J.~ Bannister and David Eppstein and Michael T~. Goodrich and + Lowell Trott, + \newblock Force-Directed Graph Drawing Using Social Gravity and Scaling, + \newblock \emph{CoRR,} + abs/1209.0748, 2012. + \end{itemize} + This implementation uses the closeness mass to determine the gravity of each + vertex. There are three forces in this algorithm: A spring force as + attractive force between vertices connected by an edge, an electric force as + repulsive force between all vertex pairs, and a gravitational force pulling + all vertices closer to their midpoint. The gravitational force depends on + the social mass of a vertex, which can be determined in different ways. This + algorithm uses the closeness mass. The closeness of a vertex $u$ is the + reciprocal of the sum of the shortest path from $u$ to every other vertex + $v$. The gravitational force leads to more "important" vertices ending up + closer to the middle of the drawing, since the social mass of a vertex is + proportinal to its importance. The social layouts work especially well on + unconnected graphs like forests. This layout was implemented by using the + Jedi framework. + ]], + + example = + [[ + \tikz + \graph[social closeness layout, speed = 0.9, gravity = 0.2, node distance = 0.65cm, nodes={as=,circle, draw, inner sep=3pt,outer sep=0pt}, find equilibrium = true, maximum step = 5]{ + a -- a1 -- a2 -- a, + b -- b1 -- b2 -- b, + c -- c1 -- c2 -- c, + d -- d1 -- d2 -- d, + e -- e1 -- e2 -- e, + f -- f1 -- f2 -- f, + g -- g1 -- g2 -- g, + h -- h1 -- h2 -- h, + i -- i1 -- i2 -- i, + j -- j1 -- j2 -- j, + a -- b -- c -- d -- e -- f -- g -- h -- i -- j -- a + }; + ]], + + example = + [[ + \tikz + \graph[social closeness layout, speed = 0.35, node distance = 0.7cm, maximum step = 5, nodes={as=,circle, draw, inner sep=3pt,outer sep=0pt}, radius = 1cm, gravity = 2]{ + a -- {a1 -- a2, a3}, + b -- {b1, b2 -- b3 -- b4 --{b5, b6}}, + c -- {c1--c2}, + d -- {d1, d2, d3 -- {d4, d5}, d6 --{d7, d8}} + }; + ]] +} + +local fw_attributes = Storage.newTableStorage() + +function SocialClass:run() + local dist = PathLengthsFW:breadthFirstSearch(self.ugraph) + local tmp + for vertex, n in pairs(dist) do + tmp = fw_attributes[vertex] + local sum = 0 + for i, w in pairs(n) do + sum = sum + w + end + sum = sum / # self.ugraph.vertices + tmp.mass = 1/sum + end + + fw_attributes.options = self.ugraph.options + + --Generate new force class + social_gravity = ForceController.new(self.ugraph, fw_attributes) + + --add all required forces + social_gravity:addForce{ + force_type = ForceCanvasDistance, + fun_u = function (data) return data.k/(data.d*data.d) end, + epoch = {"after expand", "during expand"} + } + social_gravity:addForce{ + force_type = ForceCanvasPosition, + fun_u = function (data) return data.attributes[data.u].mass*data.attributes.options.gravity end, + epoch = {"after expand", "during expand"} + } + social_gravity:addForce{ + force_type = ForceGraphDistance, + fun_u = function (data) return -data.d/(data.k*data.k) end, + n = 1, + epoch = {"after expand", "during expand"} + } + + social_gravity:run() +end + +return SocialClass
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/algorithms/SocialGravityDegree.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/algorithms/SocialGravityDegree.lua new file mode 100644 index 00000000000..8e94a561871 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/algorithms/SocialGravityDegree.lua @@ -0,0 +1,180 @@ +-- Copyright 2014 by Ida Bruhns +-- +-- This file may be distributed and/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 + + +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare +local ForceController = require 'pgf.gd.force.jedi.base.ForceController' +local ForceCanvasDistance = require "pgf.gd.force.jedi.forcetypes.ForceCanvasDistance" +local ForceCanvasPosition = require "pgf.gd.force.jedi.forcetypes.ForceCanvasPosition" +local ForceGraphDistance = require "pgf.gd.force.jedi.forcetypes.ForceGraphDistance" +local Storage = require "pgf.gd.lib.Storage" + +local SocialClass = {} + +--- +declare { + key = "social degree layout", + algorithm = SocialClass, + postconditions = {fixed = true}, + + summary = [[This layout uses the social gravity algorithm proposed by Bannister + with closeness mass to draw graphs.]], + + documentation = + [[Bannister et all described a social gravity algorithm that can be + implemented with different kinds of gravity. + It is described in: + \begin{itemize} + \item + Michael J.~ Bannister and David Eppstein and Michael T~. Goodrich and + Lowell Trott, + \newblock Force-Directed Graph Drawing Using Social Gravity and Scaling, + \newblock \emph{CoRR,} + abs/1209.0748, 2012. + \end{itemize} + This implementation uses the degree mass to determine the gravity of each + vertex. There are three forces in this algorithm: A spring force as + attractive force between vertices connected by an edge, an electric force as + repulsive force between all vertex pairs, and a gravitational force pulling + all vertices closer to their midpoint. The gravitational force depends on + the social mass of a vertex, which can be determined in different ways. This + algorithm uses the degree of each vertex as its mass. The gravitational + force leads to more "important" vertices ending up closer to the middle of + the drawing, since the social mass of a vertex is proportinal to its + importance. The social layouts work especially well on unconnected graphs + like forests. This layout was implemented by using the Jedi framework. + ]], + + example = + [[ + \graph[social degree layout, speed = 0.9, gravity = 0.2, node distance = 0.65cm, nodes={as=,circle, draw, inner sep=3pt,outer sep=0pt}, find equilibrium = true, maximum step = 5]{ + a -- a1 -- a2 -- a, + b -- b1 -- b2 -- b, + c -- c1 -- c2 -- c, + d -- d1 -- d2 -- d, + e -- e1 -- e2 -- e, + f -- f1 -- f2 -- f, + g -- g1 -- g2 -- g, + h -- h1 -- h2 -- h, + i -- i1 -- i2 -- i, + j -- j1 -- j2 -- j, + a -- b -- c -- d -- e -- f -- g -- h -- i -- j -- a + }; + ]], + + example = + [[ + \tikz + \graph[social degree layout, speed = 0.35, node distance = 0.7cm, maximum step = 15, nodes={as=,circle, draw, inner sep=3pt,outer sep=0pt}, radius = 1cm, gravity = 0.2]{ + a -- {a1 -- a2, a3}, + b -- {b1, b2 -- b3 -- b4 --{b5, b6}}, + c -- {c1--c2}, + d -- {d1, d2, d3 -- {d4, d5}, d6 --{d7, d8}} + }; + ]] +} + +--- +declare { + key = "gravity", + type = "number", + initial = 0.2, + + summary = "The gravity key describes the magnitude of the gravitational force.", + + documentation = + [[ + This parameter currently only affects the \lstinline{social degree layout} + and the \lstinline{social closeness layout}. The gravity key determines the + strength used to pull the vertices to the center of the canvas. + ]], + + example = + [[ + \graph[social degree layout, iterations = 100, maximum time = 100, maximum step = 10]{ + a1[weight = 2] -- {a2, a3, a4, a5}, + b1 -- {b2 -- {b3, b4}, b5} + }; + ]], + + example = + [[ + \graph[social degree layout, iterations = 100, maximum time = 100, gravity = 0.5, maximum step = 10]{ + a1 -- {a2 [mass = 2], a3, a4, a5}, + b1 -- {b2 -- {b3, b4}, b5} + }; + ]] +} + + + + +-- Implementation starts here: + +-- define time functions +local time_fun_1, time_fun_2, time_fun_3 + +function time_fun_1 (t_total, t_now) + if t_now > 3*t_total/4 then + return t_now/t_total + end + return 0 +end + +function time_fun_3 (t_total, t_now) + if t_now >= t_total/2 then + return 2 + else + return 1 + end +end + +-- define table to store variables if needed +local fw_attributes = Storage.newTableStorage() + +function SocialClass:run() + --initialize masses + local tmp + for _, vertex in ipairs(self.ugraph.vertices) do + tmp = fw_attributes[vertex] + tmp.social_mass = #self.ugraph:incoming(vertex) + end + + -- add options to storage table + fw_attributes.options = self.ugraph.options + + -- generate new force class + local social_gravity = ForceController.new(self.ugraph, fw_attributes) + + -- add all required forces + social_gravity:addForce{ + force_type = ForceCanvasDistance, + fun_u = function (data) return 4*data.k/(data.d*data.d) end, + time_fun = time_fun_2, + epoch = {"after expand", "during expand"} + } + social_gravity:addForce{ + force_type = ForceCanvasPosition, + fun_u = function (data) return data.attributes[data.u].social_mass*data.attributes.options.gravity end, + time_fun = time_fun_1, + epoch = {"after expand", "during expand"} + } + social_gravity:addForce{ + force_type = ForceGraphDistance, + fun_u = function (data) return -data.d/(data.k*data.k) end, + n = 1, + time_fun = time_fun_3, + epoch = {"after expand", "during expand"} + } + + -- run algorithm + social_gravity:run() +end + +return SocialClass
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/base/CoarseGraphFW.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/base/CoarseGraphFW.lua new file mode 100644 index 00000000000..f33d44e6ae1 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/base/CoarseGraphFW.lua @@ -0,0 +1,264 @@ +-- Copyright 2014 by Ida Bruhns +-- +-- This file may be distributed and/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 + + +--- A class for creating and handling "coarse" versions of a graph. Such versions contain +-- less nodes and edges than the original graph while retaining the overall +-- structure. This class offers functions to create coarse graphs and to expand them +-- to regain their original size. + +-- Imports +local Digraph = require "pgf.gd.model.Digraph" +local Vertex = require "pgf.gd.model.Vertex" +local Arc = require "pgf.gd.model.Arc" + +local lib = require "pgf.gd.lib" + +local CoarseGraph = Digraph.new() +CoarseGraph.__index = CoarseGraph + +--- Creates a new coarse graph derived from an existing graph. +-- +-- Generates a coarse graph for the input |Digraph|. +-- +-- Coarsening describes the process of reducing the amount of vertices in a graph +-- by merging vertices into pseudo-vertices. There are different strategies, +-- to decide which vertices should be merged, like merging vertices that belong to edges in a +-- maximal independent edge set or by creating pseudo-verties based on a maximal +-- independent node set. Those strategies are called +-- schemes. +-- +-- Coarsening is not performed automatically. The function |CoarseGraph:coarsen| +-- can be used to further coarsen the graph, or the function |CoarseGraph:uncoarsen| +-- can be used to restore the previous state. +-- +-- Note, however, that the input \meta{graph} is always modified in-place, so +-- if the original version of \meta{graph} is needed in parallel to its +-- coarse representations, a deep copy of \meta{grpah} needs to be passed over +-- to |CoarseGraph.new|. +-- +-- @param graph An existing graph that needs to be coarsened. +-- @param fw_attributes The user defined attributes, possibly attached to vertices. + +function CoarseGraph.new(ugraph, fw_attributes) + local coarse_graph = { + ugraph = ugraph, + level = 0, + scheme = CoarseGraph.coarsen_independent_edges, + ratio = 0, + fw_attributes = fw_attributes, + collapsed_vertices = {} + } + setmetatable(coarse_graph, CoarseGraph) + return coarse_graph +end + +-- locals for performance +local find_maximal_matching, arc_function + +-- This function performs one coarsening step: It finds all independent vertex +-- set according to |scheme|, coarsens them and adds the newly created +-- vertices to the collapsed_vertices table, associating them with the current +-- level. +function CoarseGraph:coarsen() + -- update the level + self.level = self.level + 1 + + local vertices = self.ugraph.vertices + local old_graph_size = #vertices + local c = {} + local fw_attributes = self.fw_attributes + local ugraph = self.ugraph + + if self.scheme == CoarseGraph.coarsen_independent_edges then + local matching = find_matching(ugraph) + local collapse_vertex + + for _,arc in ipairs(matching) do + -- get the two nodes of the edge that we are about to collapse + local a_h = arc.head + local a_t = arc.tail + local collapse_vertices = {a_h, a_t} + collapse_vertex = Vertex.new {weight = 0, mass = 0} + + ugraph:collapse(collapse_vertices, + collapse_vertex, + function (a,b) + a.weight = a.weight + b.weight + a.mass = a.mass + b.mass + if fw_attributes then + for key,value in pairs(fw_attributes[b]) do + if fw_attributes.functions[key] then + fw_attributes.functions[key](a,b) + elseif type(value) == "number" then + local tmp = fw_attributes[a] + if not tmp[key] then + tmp[key] = 0 + end + tmp[key] = tmp[key] + value + end + end + end + end, + function (a,b) + if a.weight == nil then + a.weight = b.weight + else + a.weight = a.weight + b.weight + end + end) + + local c_v_p = collapse_vertex.pos + local a_h_p = a_h.pos + local a_t_p = a_t.pos + c_v_p.x = (a_h_p.x + a_t_p.x)/2 + c_v_p.y = (a_h_p.y + a_t_p.y)/2 + + c[#c+1] = collapse_vertex + ugraph:remove{a_h, a_t} + end + + -- Enter all collapsed vertices into a table to uncoarsen one level at a time + self.collapsed_vertices[self.level] = c + else + assert(false, 'schemes other than CoarseGraph.coarsen_independent_edges are not implemented yet') + end + -- calculate the number of nodes ratio compared to the previous graph + self.ratio = #vertices / old_graph_size +end + +-- This function expands all vertices assoicated with the current level, then +-- updates the level. +function CoarseGraph:uncoarsen() + local a = self.collapsed_vertices[self.level] + local ugraph = self.ugraph + local random = math.random + local randomseed = math.randomseed + + for j=#a,1,-1 do + randomseed(42) + local to_expand = a[j] + + ugraph:expand(to_expand, function(a,b) + b.pos.x = a.pos.x + random()*10 + b.pos.y = a.pos.y + random()*10 + end) + ugraph:remove{to_expand} + ugraph:sync() + end + + self.level = self.level - 1 +end + +-- Getters +function CoarseGraph:getSize() + return #self.ugraph.vertices +end + + +function CoarseGraph:getRatio() + return self.ratio +end + + +function CoarseGraph:getLevel() + return self.level +end + + +function CoarseGraph:getGraph() + return self.ugraph +end + +-- Private helper function to determine whether the second vertex in the +-- current arc has been matched already +-- +-- @param arc The arc in question +-- @param vertex One of the arc's endpoints, either head or tail +-- @param matched_vertices The table holding all matched vertices +-- +-- @return The arc if the other endpoint has not been matched yet +function arc_function (arc, vertex, matched_vertices) + local x + if arc.head ~= vertex then + x = arc.head + else + x = arc.tail + end + if not matched_vertices[x] then + return arc + end +end + +-- The function finding a maximum matching of independent arcs. +-- +-- @param ugraph The current graph +-- +-- @return A table of arcs wich are in the matching +function find_matching(ugraph) + local matching = {} + local matched_vertices = {} + local unmatched_vertices = {} + local vertices = ugraph.vertices + + -- iterate over nodes in random order + for _,j in ipairs(lib.random_permutation(#vertices)) do + local vertex = vertices[j] + -- ignore nodes that have already been matched + if not matched_vertices[vertex] then + local arcs = {} + local all_arcs = {} + for _,v in pairs(ugraph:incoming(vertex)) do all_arcs[#all_arcs+1] = v end + for _,v in pairs(ugraph:outgoing(vertex)) do all_arcs[#all_arcs+1] = v end + -- mark the node as matched + matched_vertices[vertex] = true + + for _, a in ipairs(all_arcs) do + arcs[#arcs +1] = arc_function(a, vertex, matched_vertices) + end + + if #arcs > 0 then + -- sort edges by the weights of the adjacent vertices + table.sort(arcs, function (a, b) + local x, y + if a.head == vertex then + x = a.tail + else + x = a.head + end + if b.head == vertex then + y = b.tail + else + y = b.head + end + return x.weight < y.weight + end) + + -- match the node against the neighbour with minimum weight + matched_vertices[arcs[1].head] = true + matched_vertices[arcs[1].tail] = true + table.insert(matching, arcs[1]) + end + end + end + + -- generate a list of nodes that were not matched at all + for _,j in ipairs(lib.random_permutation(#vertices)) do + local vertex = vertices[j] + if not matched_vertices[vertex] then + table.insert(unmatched_vertices, vertex) + end + end + return matching +end + + +-- done + +return CoarseGraph
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/base/ForceController.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/base/ForceController.lua new file mode 100644 index 00000000000..1762fcf07eb --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/base/ForceController.lua @@ -0,0 +1,488 @@ +-- Copyright 2014 by Ida Bruhns +-- +-- This file may be distributed and/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 + + +--- This class is the most basic class for the Jedi framework. It manages the +-- forces, epochs, options and streamlines the graph drawing process. +-- In detail, the force template will do the following: +-- \begin{itemize} +-- \item Hold the table with all epochs currently defined, and provide +-- a function to add new ones +-- \item Hold the table associating forces with the epochs, and provide a +-- function to add new ones +-- \item Define all the non-algorithm-specific options provided by Jedi +-- \item Assert user options to catch exceptions +-- \item Save user options and library functions to local variables to enchance +-- runtime. +-- \item Add any forces that are indicated by set options +-- \item Find and call the initial positioning algorithm requested +-- \item Determine if coarsening is enabled, and manage coarsening process if so +-- \item Call the proprocesing function of each force to obtain a vertex list the +-- force will be applied to +-- \item Calculate the forces affecting each vertex. +-- \item Move the vertices, check for equilibria/used up iterations, update +-- virtual time +-- \end{itemize} + +local ForceController = {} +ForceController.__index = ForceController + +-- Imports +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare +local Coordinate = require "pgf.gd.model.Coordinate" +local CoarseGraph = require 'pgf.gd.force.jedi.base.CoarseGraphFW' +local PriorityQueue = require "pgf.gd.lib.PriorityQueue" +local ForcePullToPoint = require "pgf.gd.force.jedi.forcetypes.ForcePullToPoint" +local ForcePullToGrid = require "pgf.gd.force.jedi.forcetypes.ForcePullToGrid" + +local epochs = { + [1] = "preprocessing", + [2] = "initial layout", + [3] = "start coarsening process", + [4] = "before coarsen", + [5] = "start coarsen", + [6] = "during coarsen", + [7] = "end coarsen", + [8] = "before expand", + [9] = "start expand", + [10] = "during expand", + [11] = "end expand", + [12] = "end coarsening process", + [13] = "after expand", + [14] = "postprocessing" +} + +-- Automatic parameter generation for epoch-variables +for _,e in ipairs(epochs) do + --- + declare { + key = "iterations " .. e, + type = "number" + } + + --- + declare { + key = "maximum displacement per step " .. e, + type = "number" + } + + --- + declare { + key = "global speed factor " .. e, + type = "length" + } + + --- + declare { + key = "maximum time " .. e, + type = "number" + } + + --- + declare { + key = "find equilibrium ".. e, + type = "boolean" + } + + --- + declare { + key = "equilibrium threshold ".. e, + type = "number" + } +end + +-- Implementation starts here + +--- Function allowing user to add an at the specified position +-- +-- @params epoch A string that names the epoch +-- @params position The position in the epoch array at which the epoch should be inserted + +function ForceController:addEpoch(epoch, position) + table.insert(epochs, position, epoch) +end + +--- Function allowing the user to find an epoch's position in the epoch table +-- +-- @params epoch The epoch who's position we are trying to find +-- +-- @return An integer value matching the epch's index, or $-1$ if epoch was not found + +function ForceController:findEpoch(epoch) + for j, e in ipairs(epochs) do + if e == epoch then + return j + end + end + return -1 +end + + +-- locals for performance +local net_forces = {} +local sqrt = math.sqrt +local abs = math.abs +local sum_up, options, move_vertices, get_net_force, preprocessing, epoch_forces + +--- Creating a new force algorithm +-- @params ugraph The ugraph object the graph drawing algorithm will run on +-- @params fw_attributes The storage object holding the additional attributes defined by +-- the engineer +-- +-- @returns A new instance of force template +function ForceController.new(ugraph, fw_attributes) + return setmetatable( + {epoch_forces = {}, + ugraph = ugraph, + fw_attributes = fw_attributes, + pull_to_point = false, + }, ForceController) +end + +--- Running the force algorithm + +function ForceController:run() + -- locals for performance + local ugraph = self.ugraph + local coarse_graph = CoarseGraph.new(ugraph, self.fw_attributes) + local vertices_initalized = false + options = ugraph.options + epoch_forces = self.epoch_forces + local minimum_graph_size = options["minimum coarsening size"] + local vertices = ugraph.vertices + local arcs = ugraph.arcs + local downsize_ratio = options["downsize ratio"] + local natural_spring_length = options["node distance"] + local snap_to_grid = options["snap to grid"] + local coarsen = options["coarsen"] + + -- Assert user input + assert(minimum_graph_size >= 2, 'the minimum coarsening size of coarse graphs (value: ' .. minimum_graph_size .. ') needs to be greater than or equal to 2') + assert(downsize_ratio >= 0 and downsize_ratio <=1, 'the downsize ratio of the coarse graphs (value: ' .. downsize_ratio .. ') needs to be greater than or equal to 0 and smaller than or equal to 1') + assert(natural_spring_length >= 0, 'the node distance (value: ' .. natural_spring_length .. ') needs to be greater than or equal to 0') + + -- initialize vertex and arc weights + for _,vertex in ipairs(vertices) do + vertex.weight = vertex.options["coarsening weight"] + vertex.mass = vertex.options.mass + end + + for _,arc in ipairs(arcs) do + arc.weight = 1 + end + + -- Initialize epoch_forces table entries as empty tables + for _, e in ipairs(epochs) do + if not self.epoch_forces[e] then + self.epoch_forces[e] = {} + end + end + + -- Find inital positioning algorithm + local initial_positioning_class = options.algorithm_phases['initial positioning force framework'] -- initial_types[self.initial_layout] + + -- If snap to grid option is set and no force was added yet, add an extra + -- force to post-processing + if snap_to_grid then + self:addForce{ + force_type = ForcePullToGrid, + cap = 1, + time_fun = function() return 40 end, + epoch = {"postprocessing"} + } + options["iterations postprocessing"] = options["iterations postprocessing"] or 200 + options["maximum time postprocessing"] = options["maximum time postprocessing"] or 200 + options["find equilibrium postprocessing"] = options["find equilibrium postprocessing"] or true + options["equilibrium threshold postprocessing"] = options["equilibrium threshold postprocessing"] or 1 + options["maximum displacement per step postprocessing"] = options["maximum displacement per step postprocessing"] or 1 + options["global speed factor postprocessing"] = options["global speed factor postprocessing"] or 1 + end + + -- Find marker epochs + local start_coarsening = self:findEpoch("start coarsening process") + local end_coarsening = self:findEpoch("end coarsening process") + local start_coarsen = self:findEpoch("start coarsen") + local end_coarsen = self:findEpoch("end coarsen") + local start_expand = self:findEpoch("start expand") + local end_expand = self:findEpoch("end expand") + + + -- iterate over epoch table + local i = 1 + while i <= #epochs do + local e = epochs[i] + + local iterations = options["iterations "..e] or options["iterations"] + -- assert input + assert(iterations >= 0, 'iterations (value: ' .. iterations .. ') needs to be greater than 0') + + -- Check for desired vertices and collect them in a table if any are found + local desired = false + local desired_vertices = {} + -- initialize node weights + for _,vertex in ipairs(vertices) do + if vertex.options then + if vertex.options["desired at"] then + desired = true + desired_vertices[vertex] = vertex.options["desired at"] + end + end + end + + -- Add pull to point force if desired vertices were found and engineer did not add + -- this force + if desired and not self.pull_to_point then + self:addForce{ + force_type = ForcePullToPoint, + time_fun = function(t_now, t_max) return 5 end + } + end + + -- initialize the coarse graph data structure. + if coarsen then + -- vertices = coarse_graph.ugraph.vertices + -- arcs = coarse_graph.ugraph.arcs + if i >= start_coarsening and i < end_coarsening then + -- coarsen the graph repeatedly until only minimum_graph_size nodes + -- are left or until the size of the coarse graph was not reduced by + -- at least the downsize ratio configured by the user + if i >= start_coarsen and i < start_expand then + if coarse_graph:getSize() > minimum_graph_size and coarse_graph:getRatio() <= (1 - downsize_ratio) then + if i == start_coarsen then + coarse_graph:coarsen() + elseif i < end_coarsen then + preprocessing(coarse_graph.ugraph.vertices, coarse_graph.ugraph.arcs, e, coarse_graph.ugraph) + move_vertices(coarse_graph.ugraph.vertices, e) + else + i = start_coarsen - 1 + end + end + end + + -- between coarsening and expanding + if (i > end_coarsen) and (i < start_expand) then + -- use the natural spring length as the initial natural spring length + local spring_length = natural_spring_length + + if not vertices_initalized then + initial_positioning_class.new { vertices = coarse_graph.ugraph.vertices, + options = options, + desired_vertices = desired_vertices + }:run() + vertices_initalized = true + end + + preprocessing(coarse_graph.ugraph.vertices, coarse_graph.ugraph.arcs, e, coarse_graph.ugraph) + + -- set the spring length to the average arc length of the initial layout + local spring_length = 0 + for _,arc in ipairs(arcs) do + local x = abs(arc.head.pos.x - arc.tail.pos.x) + local y = abs(arc.head.pos.y - arc.tail.pos.y) + spring_length = spring_length + sqrt(x * x + y * y) + end + spring_length = spring_length / #arcs + + -- additionally improve the layout with the force-based algorithm + -- if there are more than two nodes in the coarsest graph + if coarse_graph:getSize() > 2 and end_coarsen and not start_expand then + move_vertices(coarse_graph.ugraph.vertices, e) + end + end + + -- undo coarsening step by step, applying the force-based sub-algorithm + -- to every intermediate coarse graph as well as the original graph + if i >= start_expand then + if coarse_graph:getLevel() > 0 then + if i == start_expand then + coarse_graph:uncoarsen() + elseif i < end_expand then + preprocessing(coarse_graph.ugraph.vertices, coarse_graph.ugraph.arcs, e, coarse_graph.ugraph) + move_vertices(coarse_graph.ugraph.vertices, e) + else + i = start_expand - 1 + end + else + preprocessing(coarse_graph.ugraph.vertices, coarse_graph.ugraph.arcs, e, coarse_graph.ugraph) + move_vertices(coarse_graph.ugraph.vertices, e) + end + end + -- Before and after the coarsening process + elseif i < start_coarsening or i > end_coarsening then + if not vertices_initalized then + initial_positioning_class.new { + vertices = coarse_graph.ugraph.vertices, + options = options, + desired_vertices = desired_vertices }:run() + vertices_initalized = true + end + preprocessing(coarse_graph.ugraph.vertices, coarse_graph.ugraph.arcs, e, coarse_graph.ugraph) + move_vertices(coarse_graph.ugraph.vertices, e) + end + else + -- Same without coarsen + if i < start_coarsening or i > end_coarsening then + if not vertices_initalized then + initial_positioning_class.new { + vertices = vertices, + options = options, + desired_vertices = desired_vertices }:run() + vertices_initalized = true + end + preprocessing(vertices, arcs, e, ugraph) + move_vertices(vertices, e, self.ugraph) + end + end + i = i + 1 + end +end + + +--- Preprocessing for all force types in force configuration +-- +-- @params v The vertices of the current graph +-- @params a The arcs of the current graph +-- @params epoch The preprocessing algorithm will only be applied to the forces +-- associated with this epoch. +-- @params ugraph The current graph object + +function preprocessing(v, a, epoch, ugraph) + for _, fc in ipairs(epoch_forces[epoch]) do + fc:preprocess(v, a, ugraph) + end +end + + +--- Adding forces to the algorithm. +-- +-- @params force_data A table containing force type, time function, force function, +-- capping thresholds and the epochs in which this force will be active + +function ForceController:addForce(force_data) + local t = force_data.force_type + if t == ForcePullToPoint then + self.pull_to_point = true + end + + local f = t.new {force = force_data, options = self.ugraph.options, fw_attributes = self.fw_attributes or {}} + if force_data.epoch == nil then + force_data.epoch = {} + end + for _,e in ipairs(force_data.epoch) do + local tab = self.epoch_forces[e] + if not tab then + tab = {} + end + tab[#tab +1] = f + self.epoch_forces[e] = tab + end +end + + +--- Moving vertices according to force functions until the maximum number of +-- iterations is reached +-- +-- @params vertices The vertices in the current graph +-- @params epoch The current epoch, to find the forces that are active + +function move_vertices(vertices, epoch, g) + if #epoch_forces[epoch] == 0 then + return + end + local iterations = options["iterations ".. epoch] or options["iterations"] + local find_equilibrium = options["find equilibrium ".. epoch] or options["find equilibrium"] + local epsilon = options["equilibrium threshold ".. epoch] or options["equilibrium threshold"] + local speed = options["global speed factor ".. epoch] or options["global speed factor"] + local max_step = options["maximum displacement per step ".. epoch] or options["maximum displacement per step"] + + assert(epsilon >= 0, 'the threshold for finding an equilibirum (equilibrium threshold) (value: ' .. epsilon .. ') needs to be greater than or equal to 0') + assert(speed > 0, 'the speed at which the vertices move (value: ' .. speed .. ') needs to be greater than 0') + assert(max_step > 0, 'the maximum displacement per step each vertex can move per iteration (value: ' .. max_step .. ') needs to be greater than 0') + + local max_time = options["maximum time ".. epoch] or options["maximum time"] + local d_t = max_time/iterations + local t_now = 0 + local random = math.random + local randomseed = math.randomseed + + for j = 1 , iterations do + t_now = t_now + d_t + net_forces = get_net_force(vertices, j, t_now, epoch) + + -- normalize the force vector if necessary + for v, c in pairs(net_forces) do + local n = sqrt(c.x*c.x+c.y*c.y) + if n > max_step then + local factor = max_step/n + c.x = c.x*factor + c.y = c.y*factor + end + end + + -- if not in equilibrium yet, apply forces + if not find_equilibrium or sum_up(net_forces)*d_t > epsilon then + local cool_down_dt = d_t + if cool_down_dt > 1 then + cool_down_dt = 1 + 1/d_t + end + for _, v in ipairs(vertices) do + local factor = 1/(v.mass or 1) + local c1 = net_forces[v] + local x = speed * cool_down_dt * c1.x * factor + local y = speed * cool_down_dt * c1.y * factor + local p = v.pos + p.x = p.x + x + p.y = p.y + y + end + else + break + end + end +end + + +-- calculate the net force for each vertex in one iteration +-- +-- @params vertices the vertices of the current graph +-- @params j The current iteration +-- @params t_now The current virtual time +-- @params epoch The current epoch +-- +-- @return A table of coordinate-objects associated with vertices. The +-- coordinate object hold the calculated net displacement for +-- the $x$ and $y$ coordinate. +function get_net_force(vertices, j, t_now, epoch) + local net_forces = {} + local natural_spring_length = options["node distance"] + + for _,v in ipairs(vertices) do + net_forces[v] = Coordinate.new(0,0) + end + + for _,force_class in ipairs(epoch_forces[epoch]) do + force_class:applyTo{net_forces = net_forces, options = options, j = j, t_now = t_now, k = natural_spring_length} + end + + return net_forces +end + +-- Helper function to sum up all calculated forces +-- +-- @params tab A table holding coordinate objects as values +-- +-- @returns The sum of the absolute $x$ and $y$ values in this table +function sum_up(tab) + local sum = 0 + for v, c in pairs(tab) do + sum = sum + abs(c.x) + abs(c.y) + end + return sum +end + +return ForceController
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/base/ForceTemplate.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/base/ForceTemplate.lua new file mode 100644 index 00000000000..06b61870ff4 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/base/ForceTemplate.lua @@ -0,0 +1,44 @@ +-- Copyright 2014 by Ida Bruhns +-- +-- This file may be distributed and/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 + + +--- This is the parent class for forces. It provides a constructor and methods +-- stubs to be overwritten in the subclasses. + +-- Imports +local lib = require "pgf.gd.lib" + +local ForceTemplate = lib.class {} + +-- constructor +function ForceTemplate:constructor() + self.force = self.force + self.fw_attributes = self.fw_attributes + if not self.force.time_fun then + self.force.time_fun = function() return 1 end + end +end + +-- Method stub for preprocessing +-- +-- @param v The vertices the list will be build on + +function ForceTemplate:preprocess(v) +end + +-- Method stub for applying the forces +-- +-- @param data A table holding data like the table the forces are collected +-- in, the current iteration, the current time stamp, some options +-- or the natural spring length + +function ForceTemplate:applyTo(data) +end + +return ForceTemplate
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/base/InitialTemplate.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/base/InitialTemplate.lua new file mode 100644 index 00000000000..ad315e7a660 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/base/InitialTemplate.lua @@ -0,0 +1,64 @@ +-- Copyright 2014 by Ida Bruhns +-- +-- This file may be distributed and/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 + + +--- This is the parent class for initial layout algorithms. It provides a +-- constructor and methods stubs to be overwritten in the subclasses as well +-- as placing vertices which are |desired at| a certain point. + +-- Imports +local lib = require "pgf.gd.lib" + +local InitialTemplate = lib.class {} + +-- constructor +function InitialTemplate:constructor() + self.vertices = self.vertices + self.options = self.options + self.desired_vertices = self.desired_vertices +end + +-- Method placing |desired at| vertices at the point they are desired +-- +-- @params desired_vertices A table containing all the vertices where the +-- |desired at| option is set. +-- +-- @return |placed| A boolean array stating if vertices have been placed yet +-- @return |centroid_x| The x-coordinate of the midpoint of all placed vertices +-- @return |centroid_y| The y-coordinate of the midpoint of all placed vertices + +function InitialTemplate:desired(desired_vertices) + local placed = {} + + local centroid_x, centroid_y = 0, 0 + + local size = 0 + for v, da in pairs(desired_vertices) do + local p = v.pos + local x, y = da.x, da.y + p.x = x or 0 + p.y = y or 0 + centroid_x = centroid_x + x + centroid_y = centroid_y + y + placed[v] = true + size = size +1 + end + if size>0 then + centroid_x = centroid_x / size + centroid_y = centroid_y / size + end + + return placed, centroid_x, centroid_y +end + +-- Method stub for running the layout algorithm +function InitialTemplate:run() +end + +return InitialTemplate
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/base/PathLengthsFW.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/base/PathLengthsFW.lua new file mode 100644 index 00000000000..cf7cbddc20f --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/base/PathLengthsFW.lua @@ -0,0 +1,174 @@ +-- Copyright 2014 by Ida Bruhns +-- +-- This file may be distributed and/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 + +--- This is a helper class providing different functions that deal with graph +-- distances. This class can be used by engineers and implementers if they +-- need to calculate anything regarding graph distances. + +local PathLengths = {} + +-- Imports +local PriorityQueue = require "pgf.gd.lib.PriorityQueue" +local Preprocessing = require "pgf.gd.force.jedi.base.Preprocessing" + +-- This algorithm conducts a breadth first search on the graph it is given. +-- +-- @param ugraph The graph on which the search should be conducted +-- +-- @return A table holding every vertex $v$ as key and a table as value. The +-- value table holds all other vertices $u$ as keys and their shortest +-- distance to $v$ as value + +function PathLengths:breadthFirstSearch(ugraph) + local distances = {} + local vertices = ugraph.vertices + local arcs = ugraph.arcs + + for _,v in ipairs(vertices) do + distances[v] = {} + local dist = distances[v] + for _,w in ipairs(vertices) do + dist[w] = #vertices +1 + end + dist[v] = 0 + end + local n = 1 + local p = Preprocessing.overExactlyNPairs(vertices, arcs, n) + while (#p > 0) do + for _, v in ipairs(p) do + local tab = distances[v.tail] + tab[v.head] = n + end + n = n + 1 + p = Preprocessing.overExactlyNPairs(vertices, arcs, n) + end + return(distances) +end + + +-- This function performs Dijkstra's algorithm on the graph. +-- +-- @param ugraph The graph where the paths should be found +-- @param source The source vertex +-- +-- @return |distance| A table holding every vertex $v$ as key and a table as +-- value. The value table holds all other vertices $u$ as +-- keys and their shortest distance to $v$ as value +-- @return |levels| A table holding the levels of the graph as keys and a +-- table holding the vertices found on that level as values +-- @return |parent| A tbale holding each vertex as key and it's parent vertex +-- as value + +function PathLengths:dijkstra(ugraph, source) + local distance = {} + local levels = {} + local parent = {} + + local queue = PriorityQueue.new() + + -- reset the distance of all nodes and insert them into the priority queue + for _,v in ipairs(ugraph.vertices) do + if v == source then + distance[v] = 0 + parent[v] = nil + queue:enqueue(v, distance[v]) + else + distance[v] = #ugraph.vertices + 1 -- this is about infinity ;) + queue:enqueue(v, distance[v]) + end + end + + while not queue:isEmpty() do + local u = queue:dequeue() + + assert(distance[u] < #ugraph.vertices + 1, 'the graph is not connected, Dijkstra will not work') + + if distance[u] > 0 then + levels[distance[u]] = levels[distance[u]] or {} + table.insert(levels[distance[u]], u) + end + + + + for _,edge in ipairs(ugraph:outgoing(u)) do + local v = edge.head + local alternative = distance[u] + 1 + if alternative < distance[v] then + distance[v] = alternative + + parent[v] = u + + -- update the priority of v + queue:updatePriority(v, distance[v]) + end + end + end + + return distance, levels, parent +end + +-- This function finds the pseudo diameter of the graph, which is the longest +-- shortest path in the graph +-- +-- @param ugraph The graph who's pseudo diameter is wanted +-- +-- @ return |diameter| The pseudo diameter of the graph +-- @ return |start_node| The start node of the longest shortest path in the +-- graph +-- @ return |end_node| The end node of the longest shortest path in the graph + +function PathLengths:pseudoDiameter(ugraph) + + -- find a node with minimum degree + local start_node = ugraph.vertices[1] + for _,v in ipairs(ugraph.vertices) do + if #ugraph:incoming(v) + #ugraph:outgoing(v) < #ugraph:incoming(start_node) + #ugraph:outgoing(start_node) then + start_node = v + end + end + + assert(start_node) + + local old_diameter = 0 + local diameter = 0 + local end_node = nil + + while true do + local distance, levels = self:dijkstra(ugraph, start_node) + + -- the number of levels is the same as the distance of the nodes + -- in the last level to the start node + old_diameter = diameter + diameter = #levels + + -- abort if the diameter could not be improved + if diameter == old_diameter then + end_node = levels[#levels][1] + break + end + + -- select the node with the smallest degree from the last level as + -- the start node for the next iteration + start_node = levels[#levels][1] + for _,node in ipairs(levels[#levels]) do + if #ugraph:incoming(node)+#ugraph:outgoing(node) < #ugraph:incoming(start_node) + #ugraph:outgoing(start_node) then + start_node = node + end + end + + assert(start_node) + end + + assert(start_node) + assert(end_node) + + return diameter, start_node, end_node +end + +return PathLengths
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/base/Preprocessing.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/base/Preprocessing.lua new file mode 100644 index 00000000000..182a1e4fa79 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/base/Preprocessing.lua @@ -0,0 +1,122 @@ +-- Copyright 2014 by Ida Bruhns +-- +-- 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 + + +--- This file holds functions to create lists of vertex pairs. All +-- functions return a Graph object containing the vertices of the +-- original graph and an edge between the vertices forming a pair +-- under the specified conditions. The lists can be precomouted to +-- enhance performance. + +local PreprocessClass = {} + +-- Imports +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare +local Digraph = require "pgf.gd.model.Digraph" + + +-- Creates a graph object with an arc between all pairwise disjoint vertex +-- pairs and returns the arc table +-- +-- @param vertices The vertices of the original graph +-- +-- @return An arc table + +function PreprocessClass.allPairs(vertices) + local aP = Digraph.new{} + for _, vertex in ipairs(vertices) do + for _, vertex2 in ipairs(vertices) do + if vertex ~= vertex2 then + if not aP:contains(vertex) then + aP:add {vertex} + end + if not aP:contains(vertex2) then + aP:add {vertex2} + end + if not aP:arc(vertex, vertex2) and not aP:arc(vertex2, vertex) then + aP:connect(vertex, vertex2) + end + end + end + end + return aP.arcs +end + + +-- Creates a graph object with an arc between all pairwise disjoint vertex +-- pairs that are connected by a shortest path of length n in the original +-- graph and returns the arc table +-- +-- @param vertices The vertices of the original graph +-- @param arcs The arcs of the original graph +-- @param n The length of the shortest path we are looking for +-- +-- @return An arc table + +function PreprocessClass.overExactlyNPairs(vertices, arcs, n) + local waste, p_full = PreprocessClass.overMaxNPairs(vertices, arcs, n) + local waste, p_small = PreprocessClass.overMaxNPairs(vertices, arcs, n-1) + for _, paar in ipairs(p_full.arcs) do + if p_small:arc(paar.head, paar.tail) ~= nil or p_small:arc(paar.tail, paar.head) ~= nil then + p_full:disconnect(paar.head, paar.tail) + p_full:disconnect(paar.tail, paar.head) + end + end + return p_full.arcs +end + + +-- Creates a graph object with an arc between all pairwise disjoint vertex +-- pairs that are connected by a shortest path of length n or shorter in the +-- original graph and returns the arc table +-- +-- @param vertices The vertices of the original graph +-- @param arcs The arcs of the original graph +-- @param n The length of the shortest path we are looking for +-- +-- @return An arc table + +function PreprocessClass.overMaxNPairs(vertices, arcs, n) + assert(n >= 0, 'n (value: ' .. n.. ') needs to be greater or equal 0') + local p = Digraph.new{} + local oneHop = Digraph.new{} + if n> 0 then + for _, arc in ipairs(arcs) do + local vertex = arc.head + local vertex2 = arc.tail + if not p:contains(vertex) then + p:add {vertex} + oneHop:add {vertex} + end + if not p:contains(vertex2) then + p:add {vertex2} + oneHop:add {vertex2} + end + if p:arc(vertex, vertex2) == nil and p:arc(vertex2, vertex) == nil then + p:connect(vertex, vertex2) + oneHop:connect(vertex, vertex2) + end + end + end + + n = n-1 + while n > 0 do + for _, paar in ipairs(p.arcs) do + for _, vertex in ipairs(vertices) do + if paar.head ~= vertex and p:arc(paar.head, vertex) == nil and p:arc(vertex, paar.head) == nil and (oneHop:arc(paar.tail, vertex) ~= nil or oneHop:arc(vertex, paar.tail) ~= nil) then + p:connect(paar.head, vertex) + end + end + end + n = n-1 + end + return p.arcs, p +end + +return PreprocessClass
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/doc.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/doc.lua new file mode 100644 index 00000000000..620c035d523 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/doc.lua @@ -0,0 +1,363 @@ +-- Copyright 2014 by Ida Bruhns and Till Tantau +-- +-- This file may be distributed and/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 + +-- Imports +local key = require 'pgf.gd.doc'.key +local documentation = require 'pgf.gd.doc'.documentation +local summary = require 'pgf.gd.doc'.summary +local example = require 'pgf.gd.doc'.example + + +-------------------------------------------------------------------- +key "maximum step" + +summary +[[ +This option determines the maximum ditance every vertex is allowed to travel +in one iteration. +]] + +documentation +[[ +No matter how large the forces influencing a vertex, the effect +on the drawing should be limited to avoid vertices "jumping" from one side of +the canvas to each other due to a strong force pulling them further than their +ideal destination. The amount of space a vertex is allowed to travel in one +iteration is limited by the \lstinline{maximum step} parameter. It is $5000$ +by default. That means by default, this parameter should not get in your way. +]] + + +example +[[ +\tikz + \graph[social degree layout, iterations = 2, maximum time = 2, maximum step = 6pt, coarsen = false]{ + a1 -- {a2, a3, a4, a5}, + b1 -- {b2 -- {b3, b4}, b5} + }; +]] + +example +[[ +\tikz + \graph[social degree layout, iterations = 2, maximum time = 2, maximum step = 12pt, coarsen = false]{ + a1 -- {a2, a3, a4, a5}, + b1 -- {b2 -- {b3, b4}, b5} + }; +]] +-------------------------------------------------------------------- + + + + + +-------------------------------------------------------------------- +key "speed" + +summary +[[ This is a factor every calculated step is multiplied by.]] + +documentation +[[ +The speed is the distance a vertex travels if it is influenced by a force of +$1$N$\cdot\gamma$. The speed is only a factor that will influence the total +amount every vertex can move: Half the speed makes half the movement, twice +the speed doubles the distance traveled. +]] + +example +[[ +\tikz + \graph[social degree layout, iterations = 1, maximum time = 1, maximum step = 100, speed = 0.2, coarsen = false]{ + a1 -- {a2, a3, a4, a5}, + b1 -- {b2 -- {b3, b4}, b5} + }; +]] + +example +[[ +\tikz + \graph[social degree layout, iterations = 1, maximum time= 1, maximum step = 100, speed = 0.4, coarsen = false]{ + a1 -- {a2, a3, a4, a5}, + b1 -- {b2 -- {b3, b4}, b5} + }; +]] +-------------------------------------------------------------------- + + + +-------------------------------------------------------------------- +key "maximum time" + +summary +[[ The highest amount of virtual time the algorithm is allowed to take.]] + +documentation +[[ +This option is part of the virtual time construct of Jedi. The virtual time +concept allows graph drawing algorithm engineers to switch forces on and of +after a relative or absolute amount of time has elapsed. If the iterations +stay the same, doubling the maximum time has the same effect as doubling the +speed: Vertices move faster, but it is possible they miss their intended +destination. Also increasing the iterations changes the "resolution" of the +graph drawing algorithm: More steps are simulated in the same time. +]] + +example +[[ +\tikz + \graph[social degree layout, iterations = 20, maximum time = 100, coarsen = false, maximum step = 0.5, gravity = 2]{ + a1 -- {a2, a3, a4, a5}, + b1 -- {b2 -- {b3, b4}, b5} + }; +]] + +example +[[ +\tikz + \graph[social degree layout, iterations = 20, maximum time = 200, coarsen = false, maximum step = 0.5, gravity = 2]{ + a1 -- {a2, a3, a4, a5}, + b1 -- {b2 -- {b3, b4}, b5} + }; +]] +-------------------------------------------------------------------- + + + +-------------------------------------------------------------------- +key "find equilibrium" + +summary +[[ If this option is |true|, the framework checks the vertex movment to detect +low movement near the equilibrium and stop the algorithm]] + +documentation +[[ +Since we often do not know how many iterations are enough, the framework will +detect when the vertices (almost) stop moving and stop the algorithm. After +each iteration, the framework adds up the net force influencing all the +vertices. If it falls below the threshold |epsilon|, the algorithm +will ignore the left over iterations and terminate. You can disable this +behavior by setting this parameter to |false|. Allowing the framework to find +the equilibrium usually saves you time, while allowing more iterations (or a + lower threshold) generates higher quality drawings. +]] + +example +[[ +\tikz + \graph[social degree layout, iterations = 300, maximum time = 300, coarsen = false, maximum step = 10, epsilon = 10]{ + a1 -- {a2, a3, a4, a5}, + b1 -- {b2 -- {b3, b4}, b5} + }; +]] + +example +[[ +\tikz + \graph[social degree layout, iterations = 300, maximum time = 300, maximum step = 10, find equilibrium = false]{ + a1 -- {a2, a3, a4, a5}, + b1 -- {b2 -- {b3, b4}, b5} + }; +]] +-------------------------------------------------------------------- + + + +-------------------------------------------------------------------- +key "epsilon" + +summary +[[ The threshold for the |find equilibrium| option.]] + +documentation +[[ +This key specifies the threshold for the |find equilibrium| option. The lower +epsilon, the longer the graph drawing algorithm will take, but the closer the +resulting drawing will be to the true energy minimum. +]] + +example +[[ +\tikz + \graph[social degree layout, iterations = 200, maximum time = 200, maximum step = 10, coarsen = false, epsilon = 2]{ + a1 -- {a2, a3, a4, a5}, + b1 -- {b2 -- {b3, b4}, b5} + }; +]] + +example +[[ +\tikz + \graph[social degree layout, iterations = 200, maximum time = 200, maximum step = 10, epsilon = 12, coarsen = false]{ + a1 -- {a2, a3, a4, a5}, + b1 -- {b2 -- {b3, b4}, b5} + }; +]] +-------------------------------------------------------------------- + + + +-------------------------------------------------------------------- +key "snap to grid" + +summary +[[ This option enables the post-processing step |snap to grid|.]] + +documentation +[[ +This key is the on/off-switch for the grid forces. The |snap to grid| option +triggers a form of post-processing were all vertices are pulled to the closest +point on a virtual grid. Please note that there is no repulsive force between +the vertices, so it is possible that two vertices are pulled to the same grid +point. The grid size is determined by the parameters |grid x length| and +|grid y length|. +]] + +example +[[ +\tikz + \graph[social degree layout, iterations = 100, maximum time = 100, maximum step = 10]{ + a1 -- {a2, a3, a4, a5}, + b1 -- {b2 -- {b3, b4}, b5} + }; +]] + +example +[[ +\tikz{ + \graph[social degree layout, iterations = 100, maximum time = 100, snap to grid =true, grid x length = 5mm, grid y length = 5mm, maximum step = 10]{ + a1 -- {a2, a3, a4, a5}, + b1 -- {b2 -- {b3, b4}, b5} + }; +]] +-------------------------------------------------------------------- + + + +-------------------------------------------------------------------- +key "grid x length" + +summary +[[ This option determines the cell size in $x$ direction for the |snap to grid| +option.]] + +documentation +[[ +The size of the cells of the virtual grid can be configured by the user. This +key allows a configuration of the horizontal cell width. +]] + +example +[[ +\tikz + \graph[social degree layout, iterations = 100, maximum time = 100, snap to grid =true, grid x length = 5mm, grid y length = 5mm, maximum step = 10]{ + a1 -- {a2, a3, a4, a5}, + b1 -- {b2 -- {b3, b4}, b5} + }; +]] + +example +[[ +\tikz + \graph[social degree layout, iterations = 100, maximum time = 100, snap to grid =true, grid x length = 9mm, grid y length = 5mm, maximum step = 10]{ + a1 -- {a2, a3, a4, a5}, + b1 -- {b2 -- {b3, b4}, b5} + }; +]] +-------------------------------------------------------------------- + + + +-------------------------------------------------------------------- +key "grid y length" + +summary +[[ This option determines the cell size in $x$ direction for the |snap to grid| +option.]] + +documentation +[[ +Same as |grid x length|, but in vertical direction (height of the cells). +]] + +example +[[ +\tikz + \graph[social degree layout, iterations = 100, maximum time = 100, snap to grid =true, grid x length = 5mm, grid y length = 5mm, maximum step = 10]{ + a1 -- {a2, a3, a4, a5}, + b1 -- {b2 -- {b3, b4}, b5} + }; +]] + +example +[[ +\tikz + \graph[social degree layout, iterations = 100, maximum time = 100, snap to grid =true, grid x length = 5mm, grid y length = 9mm, maximum step = 10]{ + a1 -- {a2, a3, a4, a5}, + b1 -- {b2 -- {b3, b4}, b5} + }; +]] +-------------------------------------------------------------------- + + +-------------------------------------------------------------------- +key "mass" + +summary +[[ + The mass of a vertex determines how fast it can move. Vertices + with higher mass move slower. +]] + +documentation +[[ + The mass of a vertex determines how fast this vertex + moves. Mass is directly inverse proportional to the distance the vertex + moves. In contrast to the global speed factor, mass usually only affects a + single vertex. A vertex with a higher mass will move slower if affected by + the same mass than a vertex with a lower mass. By default, each vertex has a + mass of $1$. +]] + +example +[[ + \tikz + \graph[social degree layout, iterations = 100, maximum time = 100, maximum displacement per step = 10]{ + a1 -- {a2, a3, a4, a5}, + b1 -- {b2 -- {b3, b4}, b5} + }; +]] + +example +[[ + \tikz + \graph[social degree layout, iterations = 100, maximum time = 100, maximum displacement per step = 10]{ + a1 -- {a2, a3, a4, a5}, + b1[mass = 4] -- {b2 -- {b3, b4}, b5} + }; +]] +-------------------------------------------------------------------- + + +-------------------------------------------------------------------- +key "coarsening weight" + +summary +[[ + The coarsening weight of a vertex determines when it will be + coarsened. +]] + +documentation +[[ + Vertices with higher coarsening weight are considered more important and + will be coarsened later, or not at all. +]] +-------------------------------------------------------------------- diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceAbsoluteValue.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceAbsoluteValue.lua new file mode 100644 index 00000000000..774743e52e6 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceAbsoluteValue.lua @@ -0,0 +1,94 @@ +-- Copyright 2014 by Ida Bruhns +-- +-- This file may be distributed and/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 + +--- This is a subclass of ForceTemplate, which is used to implement forces +-- that work on individual vertices. Forces of this kind simply add an +-- absolute value set in the force data to each vertex' $x$ and $y$ coodinate + +-- Imports +local ForceTemplate = require "pgf.gd.force.jedi.base.ForceTemplate" +local lib = require "pgf.gd.lib" +local Preprocessing = require "pgf.gd.force.jedi.base.Preprocessing" + +-- Localize math functions +local max = math.max +local sqrt = math.sqrt +local min = math.min + +-- Implementation starts here: + +local ForceAbsoluteValue = lib.class { base_class = ForceTemplate } + +function ForceAbsoluteValue:constructor () + ForceTemplate.constructor(self) + self.p = {} +end + + +-- This force class works on a vertex array that is part of the force data +-- defined when adding the force. This array is copied into p. All vertices of +-- the graph are saved in the local variable |ver|. +-- +-- @param v The vertices of the graph we are trying to find a layout for. + +function ForceAbsoluteValue:preprocess(v) + self.ver = v + self.p = self.force.vertices +end + + +-- Applying the force to the vertices and adding the effect to the passed net +-- force array +-- +-- @param data The parameters needed to aplly the force: The options table, +-- the current time stamp, an array containing the summed up net +-- forces + +function ForceAbsoluteValue:applyTo(data) + -- locals for speed + local cap = self.force.cap + local value = self.force.value + local net_forces = data.net_forces + local t_max = self.options["maximum time"] + local t_now = data.t_now + local p = self.p + local time_fun = self.force.time_fun + + -- Evaluate time function + local time_factor = time_fun(t_max, t_now) + if time_factor == 0 then + return + end + + for _,v in ipairs(self.ver) do + for _, i in ipairs (self.p) do + -- Is the vertex in the list? + if v.name == i then + + local f = value * time_factor + + -- cap effect if necessary + if cap then + if f <= 0 then + x = max(-cap, f) + else + x = min(cap, f) + end + end + + -- add calculated effect to net forces + local c1 = net_forces[v] + c1.x = c1.x + f + c1.y = c1.y + f + end + end + end +end + +return ForceAbsoluteValue
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceCanvasDistance.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceCanvasDistance.lua new file mode 100644 index 00000000000..6d9e69745ed --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceCanvasDistance.lua @@ -0,0 +1,201 @@ +-- Copyright 2014 by Ida Bruhns +-- +-- This file may be distributed and/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 + + +--- This is a subclass of ForceTemplate, which is used to implement forces between +-- vertex pairs. The forces depend on the canvas distance of the vertices in +-- the pair. This class is e.~g.~ used for electric forces. + +-- Imports +local ForceTemplate = require "pgf.gd.force.jedi.base.ForceTemplate" +local lib = require "pgf.gd.lib" +local Preprocessing = require "pgf.gd.force.jedi.base.Preprocessing" + +-- Localize math functions +local max = math.max +local sqrt = math.sqrt +local min = math.min + +-- Implementation starts here: +local ForceCanvasDistance = lib.class { base_class = ForceTemplate } + +function ForceCanvasDistance:constructor () + ForceTemplate.constructor(self) + self.p = {} +end + + +-- This force class works on all pairwise disjoint vertex pairs. This +-- function generates a new graph object containing all vertices from the +-- original graph and arcs between all pairwise disjoint vertex pairs. The +-- arcs-table of this new object will be saved in the variable |p|. +-- +-- @param v The vertices of the graph we are trying to find a layout for. + +function ForceCanvasDistance:preprocess(v) + self.p = Preprocessing.allPairs(v) +end + + +-- Applying the force to the vertices and adding the effect to the passed net +-- force array +-- +-- @param data The parameters needed to aplly the force: The options table, +-- the current time stamp, an array containing the summed up net +-- forces + +function ForceCanvasDistance:applyTo(data) + -- locals for speed + local cap = self.force.cap + local fun_u = self.force.fun_u + local fun_v = self.force.fun_v + local net_forces = data.net_forces + local t_max = self.options["maximum time"] + local t_now = data.t_now + local k = data.k + local p = self.p + local time_fun = self.force.time_fun + local fw_attributes = self.fw_attributes + + -- Evaluate time function + local time_factor = time_fun(t_max, t_now) + if time_factor == 0 then + return + end + + if not fun_v then + local data = { k = k, attributes = fw_attributes } + for _, i in ipairs(p) do + -- dereference + local p2 = i.head + local p1 = i.tail + local p2_pos = p2.pos + local p1_pos = p1.pos + + -- calculate distance between two points + local x = p2_pos.x - p1_pos.x + local y = p2_pos.y - p1_pos.y + local d = max(sqrt(x*x+y*y),0.1) + + -- apply force function + data.u = p2 + data.v = p1 + data.d = d + local e = fun_u(data) + + -- Include time function + local f = e * time_factor / d + + -- calculate effect on x/y + local g = x * f + local h = y * f + + -- cap effect if necessary + if cap then + if g <= 0 then + x = max(-cap, g) + else + x = min(cap, g) + end + + if h <= 0 then + y = max(-cap, h) + else + y = min(cap, h) + end + else + x = g + y = h + end + + -- add calculated effect to net forces + local c1 = net_forces[p1] + c1.x = c1.x - x + c1.y = c1.y - y + local c2 = net_forces[p2] + c2.x = c2.x + x + c2.y = c2.y + y + end + else + -- There are different functions for head and tail vertex + local data = { k = k, attributes = fw_attributes } + for _, i in ipairs(p) do + -- dereference + local p2 = i.head + local p1 = i.tail + local p2_pos = p2.pos + local p1_pos = p1.pos + + -- calculate distance between two points + local x = p2_pos.x - p1_pos.x + local y = p2_pos.y - p1_pos.y + local d = max(sqrt(x*x+y*y),0.1) + + -- apply force function to distance and k (natural spring length + data.u = p2 + data.v = p1 + data.d = d + local e_head = fun_u(data) + local e_tail = fun_v(data) + + -- Include time function + local f_head = time_factor * e_head / d + local f_tail = time_factor * e_tail / d + + -- calculate effect on x/y + local g_head = x * f_head + local g_tail = x * f_tail + local h_head = y * f_head + local h_tail = y * f_tail + + -- cap effect if necessary + local x_head, x_tail, y_head, y_tail + if cap then + if g_head <= 0 then + x_head = max(-cap, g_head) + else + x_head = min(cap, g_head) + end + + if g_tail <= 0 then + x_tail = max(-cap, g_tail) + else + x_tail = min(cap, g_tail) + end + + if h_head <= 0 then + y_head = max(-cap, h_head) + else + y_head = min(cap, h_head) + end + + if h_tail <= 0 then + y_tail = max(-cap, h_tail) + else + y_tail = min(cap, h_tail) + end + else + x_head = g_head + x_tail = g_tail + y_head = h_head + y_tail = h_tail + end + + -- add calculated effect to net forces + local c1 = net_forces[p1] + c1.x = c1.x - x_tail + c1.y = c1.y - y_tail + local c2 = net_forces[p2] + c2.x = c2.x + x_head + c2.y = c2.y + y_head + end + end +end + +return ForceCanvasDistance
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceCanvasPosition.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceCanvasPosition.lua new file mode 100644 index 00000000000..377ab22c348 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceCanvasPosition.lua @@ -0,0 +1,117 @@ +-- Copyright 2014 by Ida Bruhns +-- +-- This file may be distributed and/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 + + +--- This is a subclass of ForceTemplate, which is used to implement forces +-- that work on individual vertices. The forces depend on the canvas position +-- of the vertices. This class is e.~g.~ used for gravitational forces. + +local ForceTemplate = require "pgf.gd.force.jedi.base.ForceTemplate" +local lib = require "pgf.gd.lib" + +local ForceCanvasPosition = lib.class { base_class = ForceTemplate } + +-- Localize math functions +local max = math.max +local sqrt = math.sqrt +local min = math.min + +-- Implementation starts here: + +function ForceCanvasPosition:constructor () + ForceTemplate.constructor(self) + self.p = {} +end + + +-- This force class works on individual vertices and only depends on their +-- current position. Thus the vertex table of the current graph is simply +-- copied to the variable |p|. +-- +-- @param v The vertices of the graph we are trying to find a layout for. + +function ForceCanvasPosition:preprocess(v) + self.p = v +end + + +-- Applying the force to the vertices and adding the effect to the passed net +-- force array +-- +-- @param data The parameters needed to aplly the force: The options table, +-- the current time stamp, an array containing the summed up net +-- forces + +function ForceCanvasPosition:applyTo(data) + --localize + local cap = self.force.cap + local fun_u = self.force.fun_u + local net_forces = data.net_forces + local t_max = self.options["maximum time"] + local t_now = data.t_now + local p = self.p + local time_fun = self.force.time_fun + local initial_gravity = self.options["gravity"] + local fw_attributes = self.fw_attributes + + -- evaluate time function + local time_factor = time_fun(t_max, t_now) + if time_factor == 0 then + return + end + + -- Find midpoint of all vertices since they will be attracted to this point + local centroid_x, centroid_y = 0,0 + for _, v in ipairs(p) do + local pos = v.pos + centroid_x = centroid_x + pos.x + centroid_y = centroid_y + pos.y + end + centroid_x = centroid_x/#p + centroid_y = centroid_y/#p + + -- Iterate over the precomputed vertex list + for _, v in ipairs(p) do + -- localize + local p1 = v.pos + + -- apply force function + local factor = fun_u{attributes = fw_attributes, u = v} + + -- calculate distance between vertex and centroid + local x = centroid_x - p1.x + local y = centroid_y - p1.y + + -- calculate effect on x/y + local h = factor * time_factor + x = x * h + y = y * h + + -- cap effect if necessary + if cap then + if x <= 0 then + x = max(-cap, x) + else + x = min(cap, x) + end + if y <= 0 then + y = max(-cap, y) + else + y = min(cap, y) + end + end + + -- add calculated effect to net forces + local c = net_forces[v] + c.x = c.x + x + c.y = c.y + y + end +end + +return ForceCanvasPosition
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceGraphDistance.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceGraphDistance.lua new file mode 100644 index 00000000000..628f1192ac0 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceGraphDistance.lua @@ -0,0 +1,205 @@ +-- Copyright 2014 by Ida Bruhns +-- +-- This file may be distributed and/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 + + +--- This is a subclass of ForceTemplate, which is used to implement forces between +-- vertex pairs. The forces depend on the graph distance of the vertices in +-- the pair. This class is e.~g.~ used for spring forces. + + +local ForceTemplate = require "pgf.gd.force.jedi.base.ForceTemplate" +local lib = require "pgf.gd.lib" +local Preprocessing = require "pgf.gd.force.jedi.base.Preprocessing" + +-- Localize math functions +local max = math.max +local sqrt = math.sqrt +local min = math.min + +-- Implementation starts here: + +local ForceGraphDistance = lib.class { base_class = ForceTemplate } + +function ForceGraphDistance:constructor () + ForceTemplate.constructor(self) + self.p = {} +end + + +-- This force class works on all pairwise disjoint vertex pairs connected by +-- a path of length maximum $n$. The parameter $n$ is given by the engineer in +-- the force declaration. This function generates a new graph object +-- containing all vertices from the original graph and arcs between all +-- pairwise disjoint vertex pairs. The arcs-table of this new object will be +-- saved in the variable |p|. +-- +-- @param v The vertices of the graph we are trying to find a layout for. + +function ForceGraphDistance:preprocess(v, a) + self.p = Preprocessing.overExactlyNPairs(v, a, self.force.n) +end + + +-- Applying the force to the vertices andadding the effect to the passed net +-- force array +-- +-- @param data The parameters needed to aplly the force: The options table, +-- the current time stamp, an array containing the summed up net +-- forces + +function ForceGraphDistance:applyTo(data) + -- locals for speed + local cap = self.force.cap + local fun_u = self.force.fun_u + local fun_v = self.force.fun_v + local net_forces = data.net_forces + local t_max = self.options["maximum time"] + local t_now = data.t_now + local k = data.k + local p = self.p + local time_fun = self.force.time_fun + local fw_attributes = self.fw_attributes + + -- Evaluate time function + local time_factor = time_fun(t_max, t_now) + if time_factor == 0 then + return + end + + if not fun_v then + local data = { k = k, attributes = fw_attributes } + for _, i in ipairs(p) do + -- dereference + local p2 = i.head + local p1 = i.tail + local p2_pos = p2.pos + local p1_pos = p1.pos + + -- calculate distance between two points + local x = p2_pos.x - p1_pos.x + local y = p2_pos.y - p1_pos.y + local d = max(sqrt(x*x+y*y),0.1) + + -- apply force function to distance and k (natural spring length) + data.u = p2 + data.v = p1 + data.d = d + local e = fun_u(data) + + -- Include time function + local f = e * time_factor / d + + -- calculate effect on x/y + local g = x * f + local h = y * f + + -- cap effect if necessary + if cap then + if g <= 0 then + x = max(-cap, g) + else + x = min(cap, g) + end + + if g <= 0 then + y = max(-cap, h) + else + y = min(cap, h) + end + else + x = g + y = h + end + + -- add calculated effect to net forces + local c1 = net_forces[p1] + c1.x = c1.x - x + c1.y = c1.y - y + local c2 = net_forces[p2] + c2.x = c2.x + x + c2.y = c2.y + y + end + else + -- There are different functions for head and tail vertex + local data = { k = k, attributes = fw_attributes } + for _, i in ipairs(p) do + -- dereference + local p2 = i.head + local p1 = i.tail + local p2_pos = p2.pos + local p1_pos = p1.pos + + -- calculate distance between two points + local x = p2_pos.x - p1_pos.x + local y = p2_pos.y - p1_pos.y + + local d = max(sqrt(x*x+y*y),0.1) + + -- apply force function to distance and k (natural spring length + data.u = p2 + data.v = p1 + data.d = d + local e_head = fun_u(data) + local e_tail = fun_v(data) + + -- Include time function + local f_head = time_factor * e_head / d + local f_tail = time_factor * e_tail / d + + -- calculate effect on x/y + local g_head = x * f_head + local g_tail = x * f_tail + local h_head = y * f_head + local h_tail = y * f_tail + + -- cap effect if necessary + local x_head, x_tail, y_head, y_tail + if cap then + if g_head <= 0 then + x_head = max(-cap, g_head) + else + x_head = min(cap, g_head) + end + + if g_tail <= 0 then + x_tail = max(-cap, g_tail) + else + x_tail = min(cap, g_tail) + end + + if h_head <= 0 then + y_head = max(-cap, h_head) + else + y_head = min(cap, h_head) + end + + if h_tail <= 0 then + y_tail = max(-cap, h_tail) + else + y_tail = min(cap, h_tail) + end + else + x_head = g_head + x_tail = g_tail + y_head = h_head + y_tail = h_tail + end + + -- add calculated effect to net forces + local c1 = net_forces[p1] + c1.x = c1.x - x_tail + c1.y = c1.y - y_tail + local c2 = net_forces[p2] + c2.x = c2.x + x_head + c2.y = c2.y + y_head + end + end +end + +return ForceGraphDistance
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForcePullToGrid.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForcePullToGrid.lua new file mode 100644 index 00000000000..b196a8a6a95 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForcePullToGrid.lua @@ -0,0 +1,123 @@ +-- Copyright 2014 by Ida Bruhns +-- +-- This file may be distributed and/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 + + +--- This is a subclass of ForceTemplate, which is used to implement forces +-- that work on individual vertices and pulls them to a virtual grid with +-- cells of the size determined by the user options |grid x length| and +-- |grid y length|. The forces depend on the canvas position +-- of the vertices relative to th next grid point. This class is e.~g.~ used +-- for the post-processing technique |snap to grid|. + + +-- Imports +local ForceTemplate = require "pgf.gd.force.jedi.base.ForceTemplate" +local lib = require "pgf.gd.lib" +local Preprocessing = require "pgf.gd.force.jedi.base.Preprocessing" + +-- Localize math functions +local max = math.max +local sqrt = math.sqrt +local min = math.min +local floor = math.floor +local round +function round(number) + return floor((number * 10 + 0.5) / 10) +end + +-- Implementation starts here: + +local ForcePullToGrid = lib.class { base_class = ForceTemplate } + +function ForcePullToGrid:constructor () + ForceTemplate.constructor(self) + self.p = {} +end + +-- This force class works on individual vertices and only depends on their +-- current position. Thus the vertex table of the current graph is simply +-- copied to the variable |p|. +-- +-- @param v The vertices of the graph we are trying to find a layout for. + +function ForcePullToGrid:preprocess(v) + self.p = v +end + + +-- Applying the force to the vertices andadding the effect to the passed net +-- force array +-- +-- @param data The parameters needed to aplly the force: The options table, +-- the current time stamp, an array containing the summed up net +-- forces + +function ForcePullToGrid:applyTo(data) + -- locals for speed + local cap = self.force.cap + local net_forces = data.net_forces + local t_max = self.options["maximum time"] + local grid_x_distance = self.options["grid x length"] + local grid_y_distance = self.options["grid y length"] + local t_now = data.t_now + local p = self.p + local time_fun = self.force.time_fun + local length = 5--self.options["node distance"] + + -- Evaluate time function + local time_factor = time_fun(t_max, t_now) + if time_factor == 0 then + return + end + + for _, v in ipairs(p) do + -- dereference + local p1 = v.pos + local p2_x = round(p1.x/grid_x_distance)*grid_x_distance + local p2_y = round(p1.y/grid_y_distance)*grid_y_distance + + -- calculate distance between vertex and grid point + local x = p1.x - p2_x + local y = p1.y - p2_y + local d = max(sqrt(x*x+y*y),0.1) + local l = -d/(length*length) + + -- Include time function + local h = l * time_factor + + -- scale effect according to direction + local f = x * h + local g = y * h + + -- cap effect if necessary + if cap then + if f <= 0 then + x = max(-cap, f) + else + x = min(cap, f) + end + + if g <= 0 then + y = max(-cap, g) + else + y = min(cap, g) + end + else + x = f + y = g + end + + -- add calculated effect to net forces + local c1 = net_forces[v] + c1.x = c1.x - x + c1.y = c1.y - y + end +end + +return ForcePullToGrid
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForcePullToPoint.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForcePullToPoint.lua new file mode 100644 index 00000000000..908fdcf6b2e --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForcePullToPoint.lua @@ -0,0 +1,119 @@ +-- Copyright 2014 by Ida Bruhns +-- +-- This file may be distributed and/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 + +--- This is a subclass of ForceTemplate, which is used to implement forces +-- that work on individual vertices and pulls them to a specific point on the +-- canvas. This point is given by the |desired at| option. The forces depend +-- on the canvas position of the vertices relative to the canvas point it is +-- pulled to. + + +-- Imports +local ForceTemplate = require "pgf.gd.force.jedi.base.ForceTemplate" +local lib = require "pgf.gd.lib" +local Preprocessing = require "pgf.gd.force.jedi.base.Preprocessing" + +-- Localize math functions +local max = math.max +local sqrt = math.sqrt +local min = math.min + +-- Implementation starts here: + +local ForcePullToPoint = lib.class { base_class = ForceTemplate } + +function ForcePullToPoint:constructor () + ForceTemplate.constructor(self) + self.p = {} +end + +-- This force class works on individual vertices and depends on their +-- current position as well as the point it is desired at. Thus all vertices +-- where the |desired at| option is set are added to the table |p| together +-- with the point where they are wanted. +-- +-- @param v The vertices of the graph we are trying to find a layout for. + +function ForcePullToPoint:preprocess(v) + for _,vertex in ipairs(v) do + if vertex.options then + local da = vertex.options["desired at"] + if da then + self.p[vertex]= {da} + end + end + end +end + + +-- Applying the force to the vertices andadding the effect to the passed net +-- force array +-- +-- @param data The parameters needed to aplly the force: The options table, +-- the current time stamp, an array containing the summed up net +-- forces + +function ForcePullToPoint:applyTo(data) + -- locals for speed + local cap = self.force.cap + local net_forces = data.net_forces + local t_max = self.options["maximum time"] + local t_now = data.t_now + local p = self.p + local time_fun = self.force.time_fun + + -- Evaluate time function + local time_factor = time_fun(t_max, t_now) + if time_factor == 0 then + return + end + + for v, point in pairs(p) do + -- dereference + local p1 = v.pos + local p2 = point[1] + + -- calculate distance between vertex and centroid + local x = p1.x - p2.x + local y = p1.y - p2.y + local d = max(sqrt(x*x+y*y),0.1) + + -- Include time function + local h = d * time_factor + + -- scale effect according to direction + local f = x * h + local g = y * h + + -- cap effect if necessary + if cap then + if f <= 0 then + x = max(-cap, f) + else + x = min(cap, f) + end + + if g <= 0 then + y = max(-cap, g) + else + y = min(cap, g) + end + else + x = f + y = g + end + + -- add calculated effect to net forces + local c1 = net_forces[v] + c1.x = c1.x - x + c1.y = c1.y - y + end +end + +return ForcePullToPoint
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/initialpositioning/CircularInitialPositioning.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/initialpositioning/CircularInitialPositioning.lua new file mode 100644 index 00000000000..1ed35653c93 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/initialpositioning/CircularInitialPositioning.lua @@ -0,0 +1,60 @@ +-- Copyright 2014 by Ida Bruhns +-- +-- This file may be distributed and/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 + +--- This class implements an inital position algorithm for graph drawing, placing the vertices on +-- a circle with th radius given by the |radius| key +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare +local InitialTemplate = require "pgf.gd.force.jedi.base.InitialTemplate" +local lib = require "pgf.gd.lib" + +local CircularInitialPositioning = lib.class { base_class = InitialTemplate } + + +--- +declare { + key = "circular initial position", + algorithm = CircularInitialPositioning, + phase = "initial positioning force framework", + phase_default = true +} + +-- Implementation starts here: + +function CircularInitialPositioning:constructor () + InitialTemplate.constructor(self) +end + +function CircularInitialPositioning:run() + -- locals for speed + local vertices = self.vertices + local tmp = (self.options["node pre sep"] + self.options["node post sep"]) + + (self.options["sibling pre sep"] + self.options["sibling post sep"]) + local min_radius = tmp * #self.vertices/2/math.pi + local radius = math.max(self.options.radius, min_radius) + local desired_vertices = self.desired_vertices + -- place vertices where the |desired at | option has been set first + local placed, centroid_x, centroid_y = InitialTemplate:desired(desired_vertices) + local angle = 2*math.pi / #vertices + local a = angle + local sin = math.sin + local cos = math.cos + + for _, vertex in ipairs(vertices) do + -- place all other vertices with respect to the one already placed + if placed[vertex] == nil then + local p = vertex.pos + p.x = sin(a) * radius + centroid_x + p.y = cos(a) * radius + centroid_y + a = a + angle + end + end +end + + +return CircularInitialPositioning
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/initialpositioning/GridInitialPositioning.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/initialpositioning/GridInitialPositioning.lua new file mode 100644 index 00000000000..cb710b35d60 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/initialpositioning/GridInitialPositioning.lua @@ -0,0 +1,60 @@ +-- Copyright 2014 by Ida Bruhns +-- +-- This file may be distributed and/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 + +--- This class implements an inital position algorithm for graph drawing, placing the vertices on +-- a grid with square cells with width |node distance| +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare +local InitialTemplate = require "pgf.gd.force.jedi.base.InitialTemplate" +local lib = require "pgf.gd.lib" + +local GridInitialPositioning = lib.class { base_class = InitialTemplate } + + +--- +declare { + key = "grid initial position", + algorithm = GridInitialPositioning, + phase = "initial positioning force framework", +} + +-- Implementation starts here: + +function GridInitialPositioning:constructor () + InitialTemplate.constructor(self) +end + +function GridInitialPositioning:run() + -- locals for speed + local vertices = self.vertices + local dist = self.options["node distance"] + local desired_vertices = self.desired_vertices + -- place vertices where the |desired at | option has been set first + local placed, centroid_x, centroid_y = InitialTemplate:desired(desired_vertices) + local n = math.ceil(math.sqrt(#vertices)) + local x = -dist + local y = 0 + + for i, vertex in ipairs(vertices) do + -- place all other vertices with respect to the one already placed + if placed[vertex] == nil then + if i <= (y/dist+1)*n then + x = x + dist + else + x = 0 + y = y + dist + end + local p = vertex.pos + p.x = x + centroid_x + p.y = y + centroid_y + end + end +end + + +return GridInitialPositioning
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/initialpositioning/RandomInitialPositioning.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/initialpositioning/RandomInitialPositioning.lua new file mode 100644 index 00000000000..39a58b9e2bd --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/initialpositioning/RandomInitialPositioning.lua @@ -0,0 +1,49 @@ +-- Copyright 2014 by Ida Bruhns +-- +-- This file may be distributed and/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 + +--- This class implements an inital position algorithm for graph drawing, placing the vertices at +-- random positions. +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare +local InitialTemplate = require "pgf.gd.force.jedi.base.InitialTemplate" +local lib = require "pgf.gd.lib" + +local RandomInitialPositioning = lib.class { base_class = InitialTemplate } + +--- +declare { + key = "random initial position", + algorithm = RandomInitialPositioning, + phase = "initial positioning force framework" +} + +-- Implementation starts here: + +function RandomInitialPositioning:constructor () + InitialTemplate.constructor(self) +end + +function RandomInitialPositioning:run() + -- locals for speed + local random = math.random + local vertices = self.vertices + local desired_vertices = self.desired_vertices + -- place vertices where the |desired at | option has been set first + local placed, centroid_x, centroid_y = InitialTemplate:desired(desired_vertices) + + for _, vertex in ipairs(vertices) do + -- place all other vertices with respect to the one already placed + if placed[vertex] == nil then + p = vertex.pos + p.x = 100 * random() + centroid_x + p.y = 100 * random() + centroid_y + end + end +end + +return RandomInitialPositioning
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/library.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/library.lua new file mode 100644 index 00000000000..4cc7128b16a --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/force/jedi/library.lua @@ -0,0 +1,115 @@ +-- Copyright 2014 by Ida Bruhns +-- +-- This file may be distributed and/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 + + +--- The library providing the graph drawing framework Jedi +-- This labrary requires all graph drawing algorithms and inital +-- positioning algorithms provided with the first release of Jedi. +-- It also defines the mass key attached to all vertices. + +-- Library name +local jedi + +-- require inital positioning algorithms +require "pgf.gd.force.jedi.initialpositioning.CircularInitialPositioning" +require "pgf.gd.force.jedi.initialpositioning.RandomInitialPositioning" +require "pgf.gd.force.jedi.initialpositioning.GridInitialPositioning" + +-- require graph drawing algorithms +require "pgf.gd.force.jedi.algorithms.FruchtermanReingold" +require "pgf.gd.force.jedi.algorithms.HuSpringElectricalFW" +require "pgf.gd.force.jedi.algorithms.SimpleSpring" +require "pgf.gd.force.jedi.algorithms.SocialGravityCloseness" +require "pgf.gd.force.jedi.algorithms.SocialGravityDegree" + + +-- define parameter +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare + +--- +declare { + key = "maximum displacement per step", + type = "length", + initial = "100", + documentation_in = "pgf.gd.force.jedi.doc" +} + +--- +declare { + key = "global speed factor", + type = "length", + initial = "1", + documentation_in = "pgf.gd.force.jedi.doc" +} + +--- +declare { + key = "maximum time", + type = "number", + initial = "50", + documentation_in = "pgf.gd.force.jedi.doc" +} + +--- +declare { + key = "find equilibrium", + type = "boolean", + initial = true, + documentation_in = "pgf.gd.force.jedi.doc" +} + +--- +declare { + key = "equilibrium threshold", + type = "number", + initial = "3", + documentation_in = "pgf.gd.force.jedi.doc" +} + +--- +declare { + key = "grid x length", + type = "length", + initial = "10pt", + documentation_in = "pgf.gd.force.jedi.doc" +} + +--- +declare { + key = "grid y length", + type = "length", + initial = "10pt", + documentation_in = "pgf.gd.force.jedi.doc" +} + +--- +declare { + key = "snap to grid", + type = "boolean", + initial = false, + documentation_in = "pgf.gd.force.jedi.doc" +} + +--- +declare { + key = "mass", + type = "number", + initial = "1", + + documentation_in = "pgf.gd.force.jedi.doc" +} + +--- +declare { + key = "coarsening weight", + type = "number", + initial = "1", + + documentation_in = "pgf.gd.force.jedi.doc" +} diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceCore.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceCore.lua index 3e5d947c736..8de6d96d48d 100644 --- a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceCore.lua +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceCore.lua @@ -7,7 +7,7 @@ -- -- See the file doc/generic/pgf/licenses/LICENSE for more information --- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceCore.lua,v 1.5 2013/03/07 18:17:14 tantau Exp $ +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceCore.lua,v 1.6 2014/02/24 10:40:32 tantau Exp $ @@ -57,14 +57,21 @@ local InterfaceCore = { -- The declared keys keys = {}, + -- The phase kinds + phase_kinds = {}, + -- Internals for handling the options stack option_stack = {}, option_cache_height = nil, option_initial = { - algorithm_phases = {} + algorithm_phases = { + ["preprocessing stack"] = {}, + ["edge routing stack"] = {}, + ["postprocessing stack"] = {}, + } }, option_aliases = { - [{}] = true -- dummy entry to make sure table is not empty + [{}] = true -- Remove, once Lua Link Bug is fixed }, -- Constant strings for special collection kinds. diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceToAlgorithms.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceToAlgorithms.lua index 586efa12067..0ea7bacf96a 100644 --- a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceToAlgorithms.lua +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceToAlgorithms.lua @@ -7,7 +7,7 @@ -- -- See the file doc/generic/pgf/licenses/LICENSE for more information --- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceToAlgorithms.lua,v 1.17 2013/12/20 14:44:46 tantau Exp $ +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceToAlgorithms.lua,v 1.18 2014/02/24 10:40:32 tantau Exp $ @@ -433,7 +433,14 @@ end --- -- This function is called by |declare| for ``algorithm --- keys.'' They are detected by the presence of the field |algorithm| +-- keys.'' These keys are normally used without a value as in just +-- |\graph[tree layout]|, but you can optionally pass a value to +-- them. In this case, this value must be the name of a \emph{phase} +-- and the algorithm of this phase will be set (and not the +-- default phase of the key), see the description of phases below for +-- details. +-- +-- Algorithm keys are detected by the presence of the field |algorithm| -- in the table |t| passed to |declare|. Here is an example of how it -- is used: --\begin{codeexample}[code only, tikz syntax=false] @@ -488,23 +495,28 @@ end -- drawing engine which ``phase'' of the graph drawing process your -- option applies to. Each time you select an algorithm later on -- through use of the algorithm's key, the algorithm for this phase --- will be set; algorithms of other phases will not be changed. So, --- for instance, when an algorithm is part of the spanning tree +-- will be set; algorithms of other phases will not be changed. +-- For instance, when an algorithm is part of the spanning tree -- computation, its phase will be |"spanning tree computation"| and -- using its key does not change the main algorithm, but only the -- algorithm used during the computation of a spanning tree for the -- current graph (in case this is needed by the main algorithm). In -- case the |phase| field is missing, the phase |main| is used. Thus, -- when no phase field is given, the key will change the main --- algorithm used to draw the graph. +-- algorithm used to draw the graph. -- -- Later on, the algorithm set for the current phase can be accessed -- through the special |algorithm_phases| field of |options| --- tables. The |algorithm_phases| table will contain a field for each --- phase for which some algorithm has been set. +-- tables. The |algorithm_phases| table will contain two fields for each +-- phase for which some algorithm has been set: One field is the name +-- of the phase and its value will be the most recently set algorithm +-- (class) set for this phase. The other field is the name of the +-- phase followed by |" stack"|. It will contain an array of all +-- algorithm classes that have been set for this key with the most +-- recently at the end. -- -- The following example shows the declaration of an algorithm that is --- the default for the phase |spanning tree computation|: +-- the default for the phase |"spanning tree computation"|: -- --\begin{codeexample}[code only, tikz syntax=false] -- --- @@ -525,7 +537,6 @@ end -- The algorithm is called as follows during a run of the main -- algorithms: -- --- --\begin{codeexample}[code only, tikz syntax=false] -- local graph = ... -- the graph object -- local spanning_algorithm_class = graph.options.algorithm_phases["spanning tree computation"] @@ -541,7 +552,10 @@ end -- be installed as the default algorithm for the phase. This can be -- done only once per phase. Furthermore, for such a default algorithm -- the |algorithm| key must be table, it may not be a string (in other --- words, all default algorithms are loaded immediately). +-- words, all default algorithms are loaded immediately). Accessing +-- the |algorithm_phases| table for a phase for which no algorithm has +-- been set will result in the default algorithm and the phase stack +-- will also contain this algorithm; otherwise the phase stack will be empty. -- -- (You cannot call this function directly, it is included for -- documentation purposes only.) @@ -587,6 +601,9 @@ local function declare_algorithm (t) -- Save in the algorithm_classes table: InterfaceCore.algorithm_classes[t.key] = store_me + assert(t.type == nil, "type may not be set for an algorithm key") + t.type = "string" + -- Install! InterfaceCore.binding:declareCallback(t) @@ -596,6 +613,11 @@ local function declare_algorithm (t) assert (type(store_me) == "table", "default algorithms must be loaded immediately") InterfaceCore.option_initial.algorithm_phases[t.phase] = store_me + InterfaceCore.option_initial.algorithm_phases[t.phase .. " stack"] = { store_me } + else + InterfaceCore.option_initial.algorithm_phases[t.phase .. " stack"] = { + dummy = true -- Remove once Lua Link Bug is fixed + } end return true diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceToC.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceToC.lua index 5b220f318bc..37db0b3d91e 100644 --- a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceToC.lua +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceToC.lua @@ -7,7 +7,7 @@ -- -- See the file doc/generic/pgf/licenses/LICENSE for more information --- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceToC.lua,v 1.6 2013/05/23 20:01:27 tantau Exp $ +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceToC.lua,v 1.7 2014/02/24 10:40:32 tantau Exp $ --- @@ -63,9 +63,9 @@ function InterfaceToC.declare_algorithm_written_in_c (t) for i=1,#edges do edges[edges[i]] = i end - collectgarbage("stop") -- BUG! Remove this when everything is linked against a SINGLE Lua lib at runtime + collectgarbage("stop") -- Remove once Lua Link Bug is fixed t.algorithm_written_in_c (self.digraph, back_table, edges, self) - collectgarbage("restart") -- Remove also! + collectgarbage("restart") -- Remove once Lua Link Bug is fixed end } end diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceToDisplay.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceToDisplay.lua index cc72dc5910a..a636e79b8ea 100644 --- a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceToDisplay.lua +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceToDisplay.lua @@ -7,7 +7,7 @@ -- -- See the file doc/generic/pgf/licenses/LICENSE for more information --- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceToDisplay.lua,v 1.12 2013/12/20 14:44:46 tantau Exp $ +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/interface/InterfaceToDisplay.lua,v 1.14 2014/03/19 09:20:59 tantau Exp $ @@ -183,7 +183,7 @@ function InterfaceToDisplay.runGraphDrawingAlgorithm() -- End timing: local stop = os.clock() - + return stop - start end @@ -577,7 +577,7 @@ function InterfaceToDisplay.pushOption(key, value, height) assert (algorithm, "algorithm class not found") push_on_option_stack(phase_unique, - { phase = key_record.phase, algorithm = algorithm }, + { phase = value or key_record.phase, algorithm = algorithm }, height) if key_record.phase == "main" then @@ -943,6 +943,15 @@ function get_current_options_table (height, table) local function handle (k, v) if k == phase_unique then algorithm_phases[v.phase] = v.algorithm + local phase_stack = v.phase .. " stack" + local t = rawget(algorithm_phases, phase_stack) + if not t then + t = algorithm_phases[phase_stack] + assert(type(t) == "table", "unknown phase") + t = lib.copy(t) + algorithm_phases[phase_stack] = t + end + t[#t + 1] = v.algorithm elseif k == collections_unique then LookupTable.addOne(collections, v) else diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/lib.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/lib.lua index dcfb468d063..aaa7f648a51 100644 --- a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/lib.lua +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/lib.lua @@ -7,7 +7,7 @@ -- -- See the file doc/generic/pgf/licenses/LICENSE for more information --- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/lib.lua,v 1.5 2013/04/04 20:43:45 tantau Exp $ +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/lib.lua,v 1.6 2014/02/24 10:40:32 tantau Exp $ @@ -351,6 +351,49 @@ function lib.class(t) end + +--- +-- Returns a method that is loaded only on demand for a class. +-- +-- The idea behind this function is that you may have a class (or just +-- a table) for which some methods are needed only seldomly. In this +-- case, you can put these methods in a separate file and then use +-- |ondemand| to indicate that the methods are found in a +-- another file. +-- +--\begin{codeexample}[code only] +-- -- File Foo.lua +-- local Foo = {} +-- function Foo.bar () ... end +-- function Foo.bar2 () ... end +-- Foo.bar3 = lib.ondemand("Foo_extra", Foo, "bar3") +-- Foo.bar4 = lib.ondemand("Foo_extra", Foo, "bar4") +-- +-- return Foo +-- +-- -- Foo_extra.lua +-- local Foo = require "Foo" +-- function Foo.bar3 () ... end +-- function Foo.bar4 () ... end +--\end{codeexample} +-- +-- @param filename The name of the file when extra methods are +-- located. +-- @param table The table for which the missing functions should be +-- loaded when they are accessed. +-- @param method The name of the method. +-- +-- @return A function that, when called, loads the filename using +-- |require| and, then, forwards the call to the method. + +function lib.ondemand(filename, table, name) + return function(...) + require (filename) + return table[name] (...) + end +end + + -- Done return lib
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/lib/Transform.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/lib/Transform.lua index 88ffd6446a4..d477c682f60 100644 --- a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/lib/Transform.lua +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/lib/Transform.lua @@ -7,7 +7,7 @@ -- -- See the file doc/generic/pgf/licenses/LICENSE for more information --- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/lib/Transform.lua,v 1.1 2012/11/27 17:24:26 tantau Exp $ +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/lib/Transform.lua,v 1.2 2014/02/24 10:40:32 tantau Exp $ --- @@ -99,6 +99,22 @@ function Transform.concat(a,b) end + +--- +-- Inverts a transformation matrix. +-- +-- @param t The transformation. +-- +-- @return The inverted transformation +-- +function Transform.invert(t) + local t1, t2, t3, t4 = t[1], t[2], t[3], t[4] + local idet = 1/(t1*t4 - t2*t3) + + return { t4*idet, -t2*idet, -t3*idet, t1*idet, -t[5], -t[6] } +end + + -- Done return Transform
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/model/Arc.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/model/Arc.lua index ff08573cbbd..5c2f10cd383 100644 --- a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/model/Arc.lua +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/model/Arc.lua @@ -7,7 +7,7 @@ -- -- See the file doc/generic/pgf/licenses/LICENSE for more information --- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/model/Arc.lua,v 1.6 2013/12/20 14:44:46 tantau Exp $ +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/model/Arc.lua,v 1.7 2015/04/21 14:10:00 tantau Exp $ --- @@ -30,14 +30,21 @@ -- |optionsXxxx| functions, that will search for options in all of the -- synactic edges that ``belong'' to an edge. -- --- Similarly, even though an arc has a |path| field, setting this --- field does not immediately set the paths of the syntactic --- edges. Indeed, you will normally want to setup and modify the --- |path| field of an arc during your algorithm and only at the very --- end, ``write it back'' to the multiple syntactic edges underlying --- the graph. For this purpose, the method |sync| is used, which is --- called automatically for the |ugraph| and |digraph| of a scope as --- well as for spanning trees. +-- In order to \emph{set} options of the edges, you can set the +-- |generate_options| field of an arc (which is |nil| by default), see +-- the |declare_parameter_sequence| function for the syntax. Similar +-- to the |path| field below, the options set in this table are +-- written back to the syntactic edges during a sync. +-- +-- In detail, the following happens: Even though an arc has a |path| +-- field and a |generated_options| field, setting these fields does +-- not immediately set the paths of the syntactic edges nor does it +-- generate options. Indeed, you will normally want to setup and +-- modify the |path| field of an arc during your algorithm and only at +-- the very end, ``write it back'' to the multiple syntactic edges +-- underlying the graph. For this purpose, the method |sync| is used, +-- which is called automatically for the |ugraph| and |digraph| of a +-- scope as well as for spanning trees. -- -- The bottom line concerning the |path| field is the following: If -- you just want a straight line along an arc, just leave the field as @@ -56,9 +63,12 @@ -- in case of a loop. -- @field path If non-nil, the path of the arc. See the description -- above. --- @field syntactic_edges This field is an array containing syntactic --- edges (``real'' edges in the syntactic digraph) that underly this --- arc. +-- @field generated_options If non-nil, some options to be passed back +-- to the original syntactic edges, see the description above. +-- @field syntactic_edges In case this arc is an arc in the syntatic +-- digraph (and only then), this field contains an array containing +-- syntactic edges (``real'' edges in the syntactic digraph) that +-- underly this arc. Otherwise, the field will be empty or |nil|. -- local Arc = {} Arc.__index = Arc @@ -434,13 +444,14 @@ end --- --- Sync an |Arc| with its syntactic edges. This causes the following: +-- Sync an |Arc| with its syntactic edges with respect to the path and +-- generated options. It causes the following to happen: -- If the |path| field of the arc is |nil|, nothing --- happens. Otherwise, a copy of the |path| is created. However, for --- every path element that is a function, this function is invoked --- with the syntactic edge as its parameter. The result of this call --- should now be a |Coordinate|, which will replace the function in --- the |Path|. +-- happens with respect to the path. Otherwise, a copy of the |path| +-- is created. However, for every path element that is a function, +-- this function is invoked with the syntactic edge as its +-- parameter. The result of this call should now be a |Coordinate|, +-- which will replace the function in the |Path|. -- -- You use this method like this: --\begin{codeexample}[code only, tikz syntax=false] @@ -451,6 +462,10 @@ end --arc:sync() --\end{codeexample} -- +-- Next, similar to the path, the field |generated_options| is +-- considered. If it is not |nil|, then all options listed in this +-- field are appended to all syntactic edges underlying the arc. +-- -- Note that this function will automatically be called for all arcs -- of the |ugraph|, the |digraph|, and the |spanning_tree| of an -- algorithm by the rendering pipeline. @@ -493,6 +508,26 @@ function Arc:sync() end end end + if self.generated_options then + local head = self.head + local tail = self.tail + local a = self.syntactic_digraph:arc(tail,head) + if a and #a.syntactic_edges>0 then + for _,e in ipairs(a.syntactic_edges) do + for _,o in ipairs(self.generated_options) do + e.generated_options[#e.generated_options+1] = o + end + end + end + local a = head ~= tail and self.syntactic_digraph:arc(head,tail) + if a and #a.syntactic_edges>0 then + for _,e in ipairs(a.syntactic_edges) do + for _,o in ipairs(self.generated_options) do + e.generated_options[#e.generated_options+1] = o + end + end + end + end end diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/model/Coordinate.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/model/Coordinate.lua index a3b0a7c20bf..8d11fe89d8f 100644 --- a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/model/Coordinate.lua +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/model/Coordinate.lua @@ -7,7 +7,7 @@ -- -- See the file doc/generic/pgf/licenses/LICENSE for more information --- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/model/Coordinate.lua,v 1.3 2013/05/23 20:01:27 tantau Exp $ +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/model/Coordinate.lua,v 1.4 2014/02/24 10:40:32 tantau Exp $ --- @@ -18,7 +18,9 @@ -- right and the $y$-axis goes up. -- -- @field x --- @field y +-- @field y +-- +-- There is also a static field called |origin| that is always equal to the origin. local Coordinate = {} Coordinate.__index = Coordinate @@ -43,6 +45,9 @@ function Coordinate.new(x,y) end +Coordinate.origin = Coordinate.new(0,0) + + --- Creates a new coordinate that is a copy of an existing one. -- -- @return A new coordinate at the same location as |self| @@ -56,7 +61,7 @@ end --- Apply a transformation matrix to a coordinate, -- see |pgf.gd.lib.Transform| for details. -- --- @param t A tansformation +-- @param t A tansformation. function Coordinate:apply(t) local x = self.x diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/model/Path.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/model/Path.lua index 03a7e7c695c..07cd78d34e1 100644 --- a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/model/Path.lua +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/model/Path.lua @@ -7,7 +7,7 @@ -- -- See the file doc/generic/pgf/licenses/LICENSE for more information --- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/model/Path.lua,v 1.2 2013/09/23 20:04:26 tantau Exp $ +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/model/Path.lua,v 1.3 2014/02/24 10:40:32 tantau Exp $ --- @@ -58,10 +58,12 @@ require("pgf.gd.model").Path = Path local Coordinate = require "pgf.gd.model.Coordinate" local Bezier = require "pgf.gd.lib.Bezier" +local lib = require "pgf.gd.lib" +-- Private function -local function rigid (x) +function Path.rigid (x) if type(x) == "function" then return x() else @@ -69,6 +71,7 @@ local function rigid (x) end end +local rigid = Path.rigid --- @@ -346,7 +349,7 @@ end -- Appends a |curveto| to the path. There can be either three -- coordinates (or functions) as parameters (the two support points -- and the target) or six numbers, where two consecutive numbers form a --- Coordinate. Which case is meant is detected by the presence of a +-- |Coordinate|. Which case is meant is detected by the presence of a -- sixth non-nil parameter. function Path:appendCurveto(a,b,c,d,e,f) @@ -363,6 +366,10 @@ function Path:appendCurveto(a,b,c,d,e,f) end + + + + --- -- Makes a path ``rigid,'' meaning that all coordinates that are only -- given as functions are replaced by the values these functions @@ -1183,7 +1190,53 @@ function Path:pad(padding) end - + +--- +-- Appends an arc (as in the sense of ``a part of the circumference of +-- a circle'') to the path. You may optionally provide a +-- transformation matrix, which will be applied to the arc. In detail, +-- the following happens: We first invert the transformation +-- and apply it to the start point. Then we compute the arc +-- ``normally'', as if no transformation matrix were present. Then we +-- apply the transformation matrix to all computed points. +-- +-- @function Path:appendArc(start_angle,end_angle,radius,trans) +-- +-- @param start_angle The start angle of the arc. Must be specified in +-- degrees. +-- @param end_angle the end angle of the arc. +-- @param radius The radius of the circle on which this arc lies. +-- @param trans A transformation matrix. If |nil|, the identity +-- matrix will be assumed. + +Path.appendArc = lib.ondemand("Path_arced", Path, "appendArc") + + + +--- +-- Appends a clockwise arc (as in the sense of ``a part of the circumference of +-- a circle'') to the path such that it ends at a given point. If a +-- transformation matrix is given, both start and end point are first +-- transformed according to the inverted transformation, then the arc +-- is computed and then transformed back. +-- +-- @function Path:appendArcTo(target,radius_or_center,clockwise,trans) +-- +-- @param target The point where the arc should end. +-- @param radius_or_center If a number, it is the radius of the circle +-- on which this arc lies. If it is a |Coordinate|, this is the center +-- of the circle. +-- @param clockwise If true, the arc will be clockwise. Otherwise (the +-- default, if nothing or |nil| is given), the arc will be counter +-- clockise. +-- @param trans A transformation matrix. If missing, +-- the identity matrix is assumed. + +Path.appendArcTo = lib.ondemand("Path_arced", Path, "appendArcTo") + + + + -- -- @return The Path as string. -- diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/model/Path_arced.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/model/Path_arced.lua new file mode 100644 index 00000000000..ace3825e350 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/model/Path_arced.lua @@ -0,0 +1,316 @@ +-- Copyright 2014 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/model/Path_arced.lua,v 1.2 2015/05/08 10:23:50 tantau Exp $ + + +local Path = require 'pgf.gd.model.Path' + +-- Imports + +local Coordinate = require "pgf.gd.model.Coordinate" +local Transform = require "pgf.gd.lib.Transform" + + + +-- Locals + +local rigid = Path.rigid + +local tan = math.tan +local sin = math.sin +local cos = math.cos +local sqrt = math.sqrt +local atan2 = math.atan2 +local abs = math.abs + +local to_rad = math.pi/180 +local to_deg = 180/math.pi +local pi_half = math.pi/2 + +local function sin_quarter(x) + x = x % 360 + if x == 0 then + return 0 + elseif x == 90 then + return 1 + elseif x == 180 then + return 0 + else + return -1 + end +end + +local function cos_quarter(x) + x = x % 360 + if x == 0 then + return 1 + elseif x == 90 then + return 0 + elseif x == 180 then + return -1 + else + return 0 + end +end + +local function atan2deg(y,x) + + -- Works like atan2, but returns the angle in degrees and, returns + -- exactly a multiple of 90 if x or y are zero + + if x == 0 then + if y < 0 then + return -90 + else + return 90 + end + elseif y == 0 then + if x < 0 then + return 180 + else + return 0 + end + else + return atan2(y,x) * to_deg + end + +end + +local function subarc (path, startx, starty, start_angle, delta, radius, trans, center_x, center_y) + + local end_angle = start_angle + delta + local factor = tan (delta*to_rad/4) * 1.333333333333333333333 * radius + + local s1, c1, s190, c190, s2, c2, s290, c290 + + if start_angle % 90 == 0 then + s1, c1, s190, c190 = sin_quarter(start_angle), cos_quarter(start_angle), sin_quarter(start_angle+90), cos_quarter(start_angle+90) + else + local a1 = start_angle*to_rad + s1, c1, s190, c190 = sin(a1), cos(a1), sin(a1+pi_half), cos(a1+pi_half) + end + + if end_angle % 90 == 0 then + s2, c2, s290, c290 = sin_quarter(end_angle), cos_quarter(end_angle), sin_quarter(end_angle-90), cos_quarter(end_angle-90) + else + local a2 = end_angle * to_rad + s2, c2, s290, c290 = sin(a2), cos(a2), sin(a2-pi_half), cos(a2-pi_half) + end + + local lastx, lasty = center_x + c2*radius, center_y + s2*radius + + path[#path + 1] = "curveto" + path[#path + 1] = Coordinate.new (startx + c190*factor, starty + s190*factor) + path[#path + 1] = Coordinate.new (lastx + c290*factor, lasty + s290*factor) + path[#path + 1] = Coordinate.new (lastx, lasty) + + if trans then + path[#path-2]:apply(trans) + path[#path-1]:apply(trans) + path[#path ]:apply(trans) + end + + return lastx, lasty, end_angle +end + + + +local function arc (path, start, start_angle, end_angle, radius, trans, centerx, centery) + + -- @param path is the path object + -- @param start is the start coordinate + -- @param start_angle is given in degrees + -- @param end_angle is given in degrees + -- @param radius is the radius + -- @param trans is an optional transformation matrix that gets applied to all computed points + -- @param centerx optionally: x-part of the center of the circle + -- @param centery optionally: y-part of the center of the circle + + local startx, starty = start.x, start.y + + -- Compute center: + centerx = centerx or startx - cos(start_angle*to_rad)*radius + centery = centery or starty - sin(start_angle*to_rad)*radius + + if start_angle < end_angle then + -- First, ensure that the angles are in a reasonable range: + start_angle = start_angle % 360 + end_angle = end_angle % 360 + + if end_angle <= start_angle then + -- In case the modulo has inadvertedly moved the end angle + -- before the start angle: + end_angle = end_angle + 360 + end + + -- Ok, now create a series of arcs that are at most quarter-cycles: + while start_angle < end_angle do + if start_angle + 179 < end_angle then + -- Add a quarter cycle: + startx, starty, start_angle = subarc(path, startx, starty, start_angle, 90, radius, trans, centerx, centery) + elseif start_angle + 90 < end_angle then + -- Add 60 degrees to ensure that there are no small segments + -- at the end + startx, starty, start_angle = subarc(path, startx, starty, start_angle, (end_angle-start_angle)/2, radius, trans, centerx, centery) + else + subarc(path, startx, starty, start_angle, end_angle - start_angle, radius, trans, centerx, centery) + break + end + end + + elseif start_angle > end_angle then + -- First, ensure that the angles are in a reasonable range: + start_angle = start_angle % 360 + end_angle = end_angle % 360 + + if end_angle >= start_angle then + -- In case the modulo has inadvertedly moved the end angle + -- before the start angle: + end_angle = end_angle - 360 + end + + -- Ok, now create a series of arcs that are at most quarter-cycles: + while start_angle > end_angle do + if start_angle - 179 > end_angle then + -- Add a quarter cycle: + startx, starty, start_angle = subarc(path, startx, starty, start_angle, -90, radius, trans, centerx, centery) + elseif start_angle - 90 > end_angle then + -- Add 60 degrees to ensure that there are no small segments + -- at the end + startx, starty, start_angle = subarc(path, startx, starty, start_angle, (end_angle-start_angle)/2, radius, trans, centerx, centery) + else + subarc(path, startx, starty, start_angle, end_angle - start_angle, radius, trans, centerx, centery) + break + end + end + + -- else, do nothing + end +end + + +-- Doc see Path.lua + +function Path:appendArc(start_angle,end_angle,radius, trans) + + local start = rigid(self[#self]) + assert(type(start) == "table", "trying to append an arc to a path that does not end with a coordinate") + + if trans then + start = start:clone() + start:apply(Transform.invert(trans)) + end + + arc (self, start, start_angle, end_angle, radius, trans) +end + + + + +-- Doc see Path.lua + +function Path:appendArcTo (target, radius_or_center, clockwise, trans) + + local start = rigid(self[#self]) + assert(type(start) == "table", "trying to append an arc to a path that does not end with a coordinate") + + local trans_target = target + local centerx, centery, radius + + if type(radius_or_center) == "number" then + radius = radius_or_center + else + centerx, centery = radius_or_center.x, radius_or_center.y + end + + if trans then + start = start:clone() + trans_target = target:clone() + local itrans = Transform.invert(trans) + start:apply(itrans) + trans_target:apply(itrans) + if centerx then + local t = radius_or_center:clone() + t:apply(itrans) + centerx, centery = t.x, t.y + end + end + + if not centerx then + -- Compute center + local dx, dy = target.x - start.x, target.y - start.y + + if abs(dx) == abs(dy) and abs(dx) == radius then + if (dx < 0 and dy < 0) or (dx > 0 and dy > 0) then + centerx = start.x + centery = trans_target.y + else + centerx = trans_target.x + centery = start.y + end + else + local l_sq = dx*dx + dy*dy + if l_sq >= radius*radius*4*0.999999 then + centerx = (start.x+trans_target.x) / 2 + centery = (start.y+trans_target.y) / 2 + assert(l_sq <= radius*radius*4/0.999999, "radius too small for arc") + else + -- Normalize + local l = sqrt(l_sq) + local nx = dx / l + local ny = dy / l + + local e = sqrt(radius*radius - 0.25*l_sq) + + centerx = start.x + 0.5*dx - ny*e + centery = start.y + 0.5*dy + nx*e + end + end + end + + local start_dx, start_dy, target_dx, target_dy = + start.x - centerx, start.y - centery, + trans_target.x - centerx, trans_target.y - centery + + if not radius then + -- Center is given, compute radius: + radius_sq = start_dx^2 + start_dy^2 + + -- Ensure that the circle is, indeed, centered: + assert (abs(target_dx^2 + target_dy^2 - radius_sq)/radius_sq < 1e-5, "attempting to add an arc with incorrect center") + + radius = sqrt(radius_sq) + end + + -- Compute start and end angle: + local start_angle = atan2deg(start_dy, start_dx) + local end_angle = atan2deg(target_dy, target_dx) + + if clockwise then + if end_angle > start_angle then + end_angle = end_angle - 360 + end + else + if end_angle < start_angle then + end_angle = end_angle + 360 + end + end + + arc (self, start, start_angle, end_angle, radius, trans, centerx, centery) + + -- Patch last point to avoid rounding problems: + self[#self] = target +end + + + +-- Done + +return true
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/pedigrees.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/pedigrees.lua new file mode 100644 index 00000000000..72bb3e904ee --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/pedigrees.lua @@ -0,0 +1,21 @@ +-- Copyright 2015 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/pedigrees.lua,v 1.1 2015/03/12 15:58:20 tantau Exp $ + + +local pedigrees = {} + +-- Declare namespace +require("pgf.gd").pedigrees = tree + + +-- Done + +return pedigrees
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/pedigrees/Koerner2015.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/pedigrees/Koerner2015.lua new file mode 100644 index 00000000000..460aaaed905 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/pedigrees/Koerner2015.lua @@ -0,0 +1,162 @@ +-- Copyright 2015 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/pedigrees/Koerner2015.lua,v 1.1 2015/03/12 15:58:23 tantau Exp $ + + +local Koerner2015 = {} + + +-- Namespace +require("pgf.gd.pedigrees").Koerner2015 = Koerner2015 + +-- Imports +local InterfaceToAlgorithms = require "pgf.gd.interface.InterfaceToAlgorithms" +local Storage = require "pgf.gd.lib.Storage" +local Direct = require "pgf.gd.lib.Direct" + +-- Shorthand: +local declare = InterfaceToAlgorithms.declare + + +--- +declare { + key = "mate", + type = "boolean", + + summary = [[" + Edges of type |mate| join mates. + "]], +} + + +--- +declare { + key = "child", + type = "boolean", + + summary = [[" + Edges of type |child| join a parent to a child. The parent is the tail of the edge, the child is the head. + "]], +} + +--- +declare { + key = "sibling", + type = "boolean", + + summary = [[" + Edges of type |sibling| join a siblings (persons with identical parents). + "]], +} + + +--- +declare { + key = "simple pedigree layout", + algorithm = Koerner2015, + + postconditions = { + upward_oriented = true + }, + + summary = [[" + A simple algorithm for drawing a pedigree. + "]], + documentation = [[" + ... + "]], + examples = [[" + \tikz \graph [simple pedigree layout, default edge operator=complete bipartite] + { + Eve -- [mate] Felix; + { Eve, Felix } -> [child] { George, Hank }; + + Alice -- [mate] Bob; + { Alice, Bob } -> [child] { Charly, Dave, Eve }; + }; + "]] +} + + +function Koerner2015:run() + + local g = self.digraph + + -- Compute ranks: + + local visited = {} + local ranks = {} + + local queue = { { g.vertices[1], 1 } } + + local queue_start = 1 + local queue_end = 1 + + local function put(v, r) + queue_end = queue_end + 1 + queue [queue_end] = { v, r } + end + + local function get() + local v = queue[queue_start][1] + local r = queue[queue_start][2] + queue_start = queue_start + 1 + return v,r + end + + while queue_start <= queue_end do + + -- Pop + local v, rank = get() + ranks[v] = rank + + visited [v] = true + + -- Follow mates: + for _,a in ipairs(g:outgoing(v)) do + if a:options("sibling") then + if not visited[a.head] then + put(a.head, rank) + end + end + end + for _,a in ipairs(g:incoming(v)) do + if a:options("child") then + if not visited[a.tail] then + put(a.tail, rank-1) + end + end + end + for _,a in ipairs(g:outgoing(v)) do + if a:options("child") then + if not visited[a.head] then + put(a.head, rank+1) + end + end + end + for _,a in ipairs(g:outgoing(v)) do + if a:options("mate") then + if not visited[a.head] then + put(a.head, rank) + end + end + end + end + + for i,v in ipairs(g.vertices) do + v.pos.x = i*50 + v.pos.y = ranks[v] * 50 + end + +end + +return Koerner2015 + + diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/pedigrees/library.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/pedigrees/library.lua new file mode 100644 index 00000000000..4e6000d038f --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/pedigrees/library.lua @@ -0,0 +1,21 @@ +-- Copyright 2015 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/pedigrees/library.lua,v 1.1 2015/03/12 15:58:23 tantau Exp $ + + + +--- +-- A pedigree depicts parent--mate--sibling relationships between individuals. +-- +-- @library + +local pedigrees -- Library name + +require "pgf.gd.pedigrees.Koerner2015" diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/routing.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/routing.lua new file mode 100644 index 00000000000..0dd9659852e --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/routing.lua @@ -0,0 +1,22 @@ +-- 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/routing.lua,v 1.1 2014/02/24 10:40:32 tantau Exp $ + + + +local routing = {} + +-- Declare namespace +require("pgf.gd").routing = routing + + +-- Done + +return routing
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/routing/Hints.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/routing/Hints.lua new file mode 100644 index 00000000000..46c337090f4 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/routing/Hints.lua @@ -0,0 +1,100 @@ +-- Copyright 2014 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/routing/Hints.lua,v 1.2 2015/05/18 17:01:16 tantau Exp $ + + +--- +-- The |Hints| class provides a way for graph drawing algorithms to +-- communicate certain possibilities concerning the routing of edges +-- to edge routing algorithms. This partly decouples the choice of the +-- vertex positioning algorithms from the choice of edge routing +-- algorithm. For instance, for a simple necklace routing, it is +-- unclear whether the edges on the necklace should be routing ``along +-- the necklace'' or not. Thus, necklace routing algoirthms will +-- ``hint'' that a necklace is present and only when the +-- |necklace routing| algorithm is selected will these hints lead to +-- actual bending of edges. +-- +-- For each kind of hint, there are methods in this class for creating +-- the hints and other methods for reading them. Hints are always +-- local to the ugraph. + +local Hints = {} + +-- Namespace +require("pgf.gd.routing").Hints = Hints + +-- Imports +local Storage = require("pgf.gd.lib.Storage") +local Coordinate = require("pgf.gd.model.Coordinate") + + + + +-- The necklace storage + +local necklaces = Storage.new() + + +--- +-- Adds a necklace hint. In this case, the hint indicates that the +-- given sequence of vertices lie on a circle. +-- +-- The idea is that an algorithm may specify that in a +-- given graph certain sequences of nodes form a ``necklace,'' which +-- is typically a circle. There may be more than one necklace inside a +-- given graph. For each necklace, +-- whenever an arc connects subsequent nodes on the necklace, they get +-- bend in such a way that they lie follow the path of the +-- necklace. If an arc lies on more than one necklace, the ``last one +-- wins.'' +-- +-- @param ugraph The ugraph to which this hint is added +-- @param necklace The sequence of vertices that form the necklace. If +-- the necklace is closed, the last vertex must equal the first one. +-- @param center If provided, must be |Coordinate| that specifies the +-- center of the circle on which the vertices lie. If not provided, +-- the origin is assumed. +-- @param clockwise If |true|, the vertices are in clockwise order, +-- otherwise in counter-clockwise order. + +function Hints.addNecklaceCircleHint(ugraph, necklace, center, clockwise) + local a = necklaces[ugraph] or {} + necklaces[ugraph] = a + + a[#a+1] = { + necklace = necklace, + center = center or Coordinate.origin, + clockwise = clockwise + } +end + + +--- +-- Gets the necklace hints. +-- +-- This function will return an array whose entries are necklace +-- hints. Each entry in the array has a |necklace| field, which is the +-- field passed to the |addNecklaceXxxx| methods. For a circle +-- necklace, the |center| and |clockwise| fields will be set. (Other +-- necklaces are not yet implemented.) +-- +-- @param ugraph The ugraph for which the necklace hints are +-- requested. +-- @return The array of necklaces as described above. + +function Hints.getNecklaceHints(ugraph) + return necklaces[ugraph] or {} +end + +-- done + +return Hints + diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/routing/NecklaceRouting.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/routing/NecklaceRouting.lua new file mode 100644 index 00000000000..23a9d265923 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/routing/NecklaceRouting.lua @@ -0,0 +1,90 @@ +-- Copyright 2014 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/routing/NecklaceRouting.lua,v 1.1 2014/04/28 12:28:46 tantau Exp $ + + +-- The class; it processes necklace hints. + +local NecklaceRouting = {} + + +-- Namespace +require("pgf.gd.routing").NecklaceRouting = NecklaceRouting + +-- Imports +local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare + +local Hints = require "pgf.gd.routing.Hints" +local Path = require "pgf.gd.model.Path" + + +--- +declare { + key = "necklace routing", + algorithm = NecklaceRouting, + + phase = "edge routing", + + summary = "Bends all edges of a graph that lie on ``necklaces'' along these necklaces.", + + documentation = [[" + Some graph drawing algorithms lay out some or all nodes along a + path, which is then called a \emph{necklace.} For instance, the + |simple necklace layout| places all nodes on a circle and that + circle is the ``necklace.'' When the |necklace rounting| edge + routing algorithm is selected, all edges that connect subsequent + nodes on such a necklace are bend in such a way that the + ``follow the necklace path.'' In the example case, this will + cause all edges that connect adjacent nodes to become arcs on + of the circle on which the nodes lie. + + Note that local edge routing options for an edge may overrule + the edge routing computed by the algorithm as in the edge from 6 + to 7 in the example. + "]], + + examples = [[" + \tikz \graph [simple necklace layout, node distance=1.5cm, + necklace routing, + nodes={draw,circle}, edges={>={Stealth[round,sep,bend]}}] + { 1 -> 2 [minimum size=30pt] <- 3 <-> 4 -- + 5 -- 6 -- [bend left] 7 -- 1 -- 4 }; + "]] +} + + + +-- The implementation + +function NecklaceRouting:run() + local ugraph = self.ugraph + + for _,entry in ipairs(Hints.getNecklaceHints(ugraph)) do + assert (entry.center) -- no other necklace types, yet + local prev + for _,vertex in ipairs(entry.necklace) do + if prev then + local a = ugraph:arc(prev, vertex) + if a then + local p = Path.new() + p:appendMoveto(a.tail.pos:clone()) + p:appendArcTo(a.head.pos:clone(), entry.center, entry.clockwise) + a.path = p + end + end + prev = vertex + end + end +end + + +-- done + +return NecklaceRouting
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/routing/library.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/routing/library.lua new file mode 100644 index 00000000000..80364d11a39 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/routing/library.lua @@ -0,0 +1,29 @@ +-- 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/routing/library.lua,v 1.1 2014/02/24 10:40:33 tantau Exp $ + + +-- Imports +local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare + +--- +-- This library contains algorithms for routing edges through a graph. +-- +-- @library + +local routing -- Library name + +-- Load declarations from: + +-- Load algorithms from: +require "pgf.gd.routing.NecklaceRouting" + + +-- General declarations diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/trees/ReingoldTilford1981.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/trees/ReingoldTilford1981.lua index 2bcade8b967..c808607385b 100644 --- a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/trees/ReingoldTilford1981.lua +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/trees/ReingoldTilford1981.lua @@ -7,7 +7,7 @@ -- -- See the file doc/generic/pgf/licenses/LICENSE for more information --- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/trees/ReingoldTilford1981.lua,v 1.10 2013/05/23 20:01:29 tantau Exp $ +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/trees/ReingoldTilford1981.lua,v 1.11 2014/03/19 09:20:59 tantau Exp $ @@ -37,7 +37,7 @@ declare { upward_oriented = true }, - documentation_in = "pgf.gd.doc.trees.ReingoldTilford1981" + documentation_in = "pgf.gd.trees.doc" } @@ -45,7 +45,7 @@ declare { declare { key = "missing nodes get space", type = "boolean", - documentation_in = "pgf.gd.doc.trees.ReingoldTilford1981" + documentation_in = "pgf.gd.trees.doc" } @@ -55,7 +55,7 @@ declare { key = "significant sep", type = "length", initial = "0", - documentation_in = "pgf.gd.doc.trees.ReingoldTilford1981" + documentation_in = "pgf.gd.trees.doc" } @@ -67,7 +67,7 @@ declare { { key = "minimum number of children" , value=2 }, { key = "significant sep", value = 10 }, }, - documentation_in = "pgf.gd.doc.trees.ReingoldTilford1981" + documentation_in = "pgf.gd.trees.doc" } --- @@ -79,7 +79,7 @@ declare { { key = "missing nodes get space" }, { key = "significant sep", value = 0 }, }, - documentation_in = "pgf.gd.doc.trees.ReingoldTilford1981" + documentation_in = "pgf.gd.trees.doc" } diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/doc/trees/ReingoldTilford1981.lua b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/trees/doc.lua index 794214c5f74..09c35611723 100644 --- a/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/doc/trees/ReingoldTilford1981.lua +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/trees/doc.lua @@ -7,7 +7,7 @@ -- -- See the file doc/generic/pgf/licenses/LICENSE for more information --- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/doc/trees/ReingoldTilford1981.lua,v 1.1 2013/03/05 23:44:30 tantau Exp $ +-- @release $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/lua/pgf/gd/trees/doc.lua,v 1.1 2014/03/19 09:20:59 tantau Exp $ local key = require 'pgf.gd.doc'.key diff --git a/Master/texmf-dist/tex/generic/pgf/graphdrawing/tex/pgflibrarygraphdrawing.code.tex b/Master/texmf-dist/tex/generic/pgf/graphdrawing/tex/pgflibrarygraphdrawing.code.tex index 02d4d8ffa30..ad648c29402 100644 --- a/Master/texmf-dist/tex/generic/pgf/graphdrawing/tex/pgflibrarygraphdrawing.code.tex +++ b/Master/texmf-dist/tex/generic/pgf/graphdrawing/tex/pgflibrarygraphdrawing.code.tex @@ -8,7 +8,7 @@ % % See the file doc/generic/pgf/licenses/LICENSE for more details. -\ProvidesFileRCS[v\pgfversion] $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/tex/pgflibrarygraphdrawing.code.tex,v 1.38 2013/08/28 11:47:35 tantau Exp $ +\ProvidesFileRCS[v\pgfversion] $Header: /cvsroot/pgf/pgf/generic/pgf/graphdrawing/tex/pgflibrarygraphdrawing.code.tex,v 1.39 2015/06/08 21:58:24 tantau Exp $ @@ -978,6 +978,14 @@ } + +% The following is used to ensure that if we need to resume the graph +% drawing (through co-routines), we pass control back to TeX +% first. Otherwise, new text input levels are created and there may be +% only 15 of these... +\newif\ifpgfgdresumecoroutine + + % Ends a graph drawing scope % % Description: @@ -988,14 +996,22 @@ % See \pgfgdbeginscope \def\pgfgdendscope{% + \pgfgdresumecoroutinefalse% \directlua{ pgf.gd.interface.InterfaceToDisplay.runGraphDrawingAlgorithm() - } - \endgroup + }% + \pgfutil@loop% + \ifpgfgdresumecoroutine% + \pgfgdresumecoroutinefalse% + \directlua{ + pgf.gd.interface.InterfaceToDisplay.resumeGraphDrawingCoroutine() + }% + \pgfutil@repeat% + \endgroup% % Late positioning has ended - \directlua{pgf.gd.interface.InterfaceToDisplay.renderGraph()} - \directlua{pgf.gd.interface.InterfaceToDisplay.endGraphDrawingScope()} - \endgroup + \directlua{pgf.gd.interface.InterfaceToDisplay.renderGraph()}% + \directlua{pgf.gd.interface.InterfaceToDisplay.endGraphDrawingScope()}% + \endgroup% } |